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: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: Greg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
This commit is contained in:
Alexey Kardashevskiy 2014-03-07 15:37:40 +11:00 committed by Andreas Färber
parent df99d30d4e
commit a46622fd07
2 changed files with 40 additions and 10 deletions

View file

@ -4,6 +4,36 @@
#include "hw/ppc/spapr.h" #include "hw/ppc/spapr.h"
#include "mmu-hash64.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, static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
target_ulong pte_index) 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 value2 = args[3];
target_ulong ret = H_P2; target_ulong ret = H_P2;
if (resource == H_SET_MODE_ENDIAN) { if (resource == H_SET_MODE_RESOURCE_LE) {
if (value1) { if (value1) {
ret = H_P3; ret = H_P3;
goto out; goto out;
@ -698,22 +728,17 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ret = H_P4; ret = H_P4;
goto out; goto out;
} }
switch (mflags) { switch (mflags) {
case H_SET_MODE_ENDIAN_BIG: case H_SET_MODE_ENDIAN_BIG:
CPU_FOREACH(cs) { CPU_FOREACH(cs) {
PowerPCCPU *cp = POWERPC_CPU(cs); set_spr(cs, SPR_LPCR, 0, LPCR_ILE);
CPUPPCState *env = &cp->env;
env->spr[SPR_LPCR] &= ~LPCR_ILE;
} }
ret = H_SUCCESS; ret = H_SUCCESS;
break; break;
case H_SET_MODE_ENDIAN_LITTLE: case H_SET_MODE_ENDIAN_LITTLE:
CPU_FOREACH(cs) { CPU_FOREACH(cs) {
PowerPCCPU *cp = POWERPC_CPU(cs); set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE);
CPUPPCState *env = &cp->env;
env->spr[SPR_LPCR] |= LPCR_ILE;
} }
ret = H_SUCCESS; ret = H_SUCCESS;
break; break;

View file

@ -153,8 +153,13 @@ typedef struct sPAPREnvironment {
#define H_PP1 (1ULL<<(63-62)) #define H_PP1 (1ULL<<(63-62))
#define H_PP2 (1ULL<<(63-63)) #define H_PP2 (1ULL<<(63-63))
/* H_SET_MODE flags */ /* Values for 2nd argument to H_SET_MODE */
#define H_SET_MODE_ENDIAN 4 #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_BIG 0
#define H_SET_MODE_ENDIAN_LITTLE 1 #define H_SET_MODE_ENDIAN_LITTLE 1