Commit a46622fd authored by Alexey Kardashevskiy's avatar Alexey Kardashevskiy Committed by Andreas Färber
Browse files

spapr_hcall: Fix little-endian resource handling in H_SET_MODE



This changes resource code definitions to ones used in the host kernel.

This fixes H_SET_MODE_RESOURCE_LE (switch between big endian and
little endian) to sync registers from KVM before changing LPCR value.

This adds a set_spr() helper to update an SPR in a CPU's context to avoid
possible races and makes use of it to change LPCR.

Signed-off-by: default avatarAlexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: default avatarGreg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: default avatarAndreas Färber <afaerber@suse.de>
parent df99d30d
Loading
Loading
Loading
Loading
+33 −8
Original line number Diff line number Diff line
@@ -4,6 +4,36 @@
#include "hw/ppc/spapr.h"
#include "mmu-hash64.h"

struct SPRSyncState {
    CPUState *cs;
    int spr;
    target_ulong value;
    target_ulong mask;
};

static void do_spr_sync(void *arg)
{
    struct SPRSyncState *s = arg;
    PowerPCCPU *cpu = POWERPC_CPU(s->cs);
    CPUPPCState *env = &cpu->env;

    cpu_synchronize_state(s->cs);
    env->spr[s->spr] &= ~s->mask;
    env->spr[s->spr] |= s->value;
}

static void set_spr(CPUState *cs, int spr, target_ulong value,
                    target_ulong mask)
{
    struct SPRSyncState s = {
        .cs = cs,
        .spr = spr,
        .value = value,
        .mask = mask
    };
    run_on_cpu(cs, do_spr_sync, &s);
}

static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
                                     target_ulong pte_index)
{
@@ -689,7 +719,7 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr,
    target_ulong value2 = args[3];
    target_ulong ret = H_P2;

    if (resource == H_SET_MODE_ENDIAN) {
    if (resource == H_SET_MODE_RESOURCE_LE) {
        if (value1) {
            ret = H_P3;
            goto out;
@@ -698,22 +728,17 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr,
            ret = H_P4;
            goto out;
        }

        switch (mflags) {
        case H_SET_MODE_ENDIAN_BIG:
            CPU_FOREACH(cs) {
                PowerPCCPU *cp = POWERPC_CPU(cs);
                CPUPPCState *env = &cp->env;
                env->spr[SPR_LPCR] &= ~LPCR_ILE;
                set_spr(cs, SPR_LPCR, 0, LPCR_ILE);
            }
            ret = H_SUCCESS;
            break;

        case H_SET_MODE_ENDIAN_LITTLE:
            CPU_FOREACH(cs) {
                PowerPCCPU *cp = POWERPC_CPU(cs);
                CPUPPCState *env = &cp->env;
                env->spr[SPR_LPCR] |= LPCR_ILE;
                set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE);
            }
            ret = H_SUCCESS;
            break;
+7 −2
Original line number Diff line number Diff line
@@ -153,8 +153,13 @@ typedef struct sPAPREnvironment {
#define H_PP1             (1ULL<<(63-62))
#define H_PP2             (1ULL<<(63-63))

/* H_SET_MODE flags */
#define H_SET_MODE_ENDIAN        4
/* Values for 2nd argument to H_SET_MODE */
#define H_SET_MODE_RESOURCE_SET_CIABR           1
#define H_SET_MODE_RESOURCE_SET_DAWR            2
#define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE     3
#define H_SET_MODE_RESOURCE_LE                  4

/* Flags for H_SET_MODE_RESOURCE_LE */
#define H_SET_MODE_ENDIAN_BIG    0
#define H_SET_MODE_ENDIAN_LITTLE 1