@@ -4,6 +4,33 @@
#include "hw/ppc/spapr.h"
#include "mmu-hash64.h"
+struct spr_sync_struct {
+ CPUState *cs;
+ int spr;
+ target_ulong value;
+ target_ulong mask;
+};
+
+static void do_spr_sync(void *arg)
+{
+ struct spr_sync_struct *s = arg;
+ PowerPCCPU *cp = POWERPC_CPU(s->cs);
+ CPUPPCState *env = &cp->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 spr_sync_struct 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)
{
@@ -667,7 +694,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;
@@ -676,22 +703,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;
@@ -699,6 +721,32 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr,
default:
ret = H_UNSUPPORTED_FLAG;
}
+ } else if (resource == H_SET_MODE_RESOURCE_ADDR_TRANS_MODE) {
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+
+ if (!(pcc->insns_flags2 & PPC2_ISA207S)) {
+ return H_P2;
+ }
+ if (value1) {
+ ret = H_P3;
+ goto out;
+ }
+ if (value2) {
+ ret = H_P4;
+ goto out;
+ }
+ switch (mflags) {
+ case 0:
+ case 2:
+ case 3:
+ CPU_FOREACH(cs) {
+ set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SH, LPCR_AIL);
+ }
+ return H_SUCCESS;
+
+ default:
+ return H_UNSUPPORTED_FLAG;
+ }
}
out:
@@ -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
@@ -458,6 +458,8 @@ struct ppc_slb_t {
#define MSR_LE 0 /* Little-endian mode 1 hflags */
#define LPCR_ILE (1 << (63-38))
+#define LPCR_AIL 0x01800000 /* Alternate interrupt location */
+#define LPCR_AIL_SH (63-40)
#define msr_sf ((env->msr >> MSR_SF) & 1)
#define msr_isf ((env->msr >> MSR_ISF) & 1)
This changes resource code definitions to ones used in the host kernel. This fixes H_SET_MODE_RESOURCE_LE (change between big endian and little endian) to update registers from KVM before changing LPCR value. This adds handling of H_SET_MODE_RESOURCE_ADDR_TRANS_MODE, for POWER8 (PowerISA 2.07) only. This adds a set_spr() helper to update an SPR in a CPU's context to avoid possible races. This makes use of the set_spr() helper for LPCR update in H_SET_MODE handler. Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> --- hw/ppc/spapr_hcall.c | 64 +++++++++++++++++++++++++++++++++++++++++++------- include/hw/ppc/spapr.h | 9 +++++-- target-ppc/cpu.h | 2 ++ 3 files changed, 65 insertions(+), 10 deletions(-)