Skip to contentSkip to conversation

ppc: Add "native" POWER8 platform

PATCH In review Benjamin Herrenschmidt 11 participants 198 messages Nov 11, 2015 – Mar 17, 2017 Page 1 of 2

lore
Share & tools
lore
#0 lore raw

cover ppc: Add "native" POWER8 platform

This requires an OPAL firmware file which isn't included yet, will emulate enough to boot existing distros and run KVM inside TCG

#1 lore raw

PATCH 1/77 ppc: Remove MMU_MODEn_SUFFIX definitions

We don't use the resulting accessors and this gets in the way of the split I/D TLB work.

---
 target-ppc/cpu.h | 3 ---
 1 file changed, 3 deletions(-)
target-ppc/cpu.h +0 -3
index b34aed6..9ef0859 100644
@@ -1246,9 +1246,6 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
 #define cpu_list ppc_cpu_list
 
 /* MMU modes definitions */
-#define MMU_MODE0_SUFFIX _user
-#define MMU_MODE1_SUFFIX _kernel
-#define MMU_MODE2_SUFFIX _hypv
 #define MMU_USER_IDX 0
 static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 {
Signature · 1 line
2.5.0
#2 lore raw

PATCH 2/77 ppc: Use split I/D mmu modes to avoid flushes on interrupts

In reply to Benjamin Herrenschmidt

We rework the way the MMU indices are calculated, providing separate indices for I and D side based on MSR:IR and MSR:DR respectively, and thus no longer need to flush the TLB on context changes. This also adds correct support for HV as a separate address space.

---
 target-ppc/cpu.h         | 11 +++++++---
 target-ppc/excp_helper.c | 11 ----------
 target-ppc/helper_regs.h | 54 +++++++++++++++++++++++++++++++++++++++++-------
 target-ppc/machine.c     |  4 +++-
 target-ppc/translate.c   |  7 ++++---
 5 files changed, 62 insertions(+), 25 deletions(-)
target-ppc/cpu.h +8 -3
index 9ef0859..aaa7117 100644
@@ -462,6 +462,8 @@ struct ppc_slb_t {
 #define MSR_EP   6  /* Exception prefix on 601                               */
 #define MSR_IR   5  /* Instruction relocate                                  */
 #define MSR_DR   4  /* Data relocate                                         */
+#define MSR_IS   5  /* Instruction address space (BookE)                     */
+#define MSR_DS   4  /* Data address space (BookE)                            */
 #define MSR_PE   3  /* Protection enable on 403                              */
 #define MSR_PX   2  /* Protection exclusive on 403                  x        */
 #define MSR_PMM  2  /* Performance monitor mark on POWER            x        */
@@ -505,6 +507,8 @@ struct ppc_slb_t {
 #define msr_ep   ((env->msr >> MSR_EP)   & 1)
 #define msr_ir   ((env->msr >> MSR_IR)   & 1)
 #define msr_dr   ((env->msr >> MSR_DR)   & 1)
+#define msr_is   ((env->msr >> MSR_IS)   & 1)
+#define msr_ds   ((env->msr >> MSR_DS)   & 1)
 #define msr_pe   ((env->msr >> MSR_PE)   & 1)
 #define msr_px   ((env->msr >> MSR_PX)   & 1)
 #define msr_pmm  ((env->msr >> MSR_PMM)  & 1)
@@ -944,7 +948,7 @@ struct ppc_segment_page_sizes {
 
 /*****************************************************************************/
 /* The whole PowerPC CPU context */
-#define NB_MMU_MODES 3
+#define NB_MMU_MODES    8
 
 #define PPC_CPU_OPCODES_LEN          0x40
 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20
@@ -1108,7 +1112,8 @@ struct CPUPPCState {
     /* Those resources are used only in QEMU core */
     target_ulong hflags;      /* hflags is a MSR & HFLAGS_MASK         */
     target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */
-    int mmu_idx;         /* precomputed MMU index to speed up mem accesses */
+    int immu_idx;         /* precomputed MMU index to speed up insn access */
+    int dmmu_idx;         /* precomputed MMU index to speed up data accesses */
 
     /* Power management */
     int (*check_pow)(CPUPPCState *env);
@@ -1249,7 +1254,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
 #define MMU_USER_IDX 0
 static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 {
-    return env->mmu_idx;
+    return ifetch ? env->immu_idx : env->dmmu_idx;
 }
 
 #include "exec/cpu-all.h"
target-ppc/excp_helper.c +0 -11
index 4250106..3e39098 100644
@@ -623,9 +623,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
 
     if (env->spr[SPR_LPCR] & LPCR_AIL) {
         new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-    } else if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) {
-        /* If we disactivated any translation, flush TLBs */
-        tlb_flush(cs, 1);
     }
 
 #ifdef TARGET_PPC64
@@ -674,14 +671,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     /* Reset exception state */
     cs->exception_index = POWERPC_EXCP_NONE;
     env->error_code = 0;
-
-    if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
-        (env->mmu_model == POWERPC_MMU_BOOKE206)) {
-        /* XXX: The BookE changes address space when switching modes,
-                we should probably implement that as different MMU indexes,
-                but for the moment we do it the slow way and flush all.  */
-        tlb_flush(cs, 1);
-    }
 }
 
 void ppc_cpu_do_interrupt(CPUState *cs)
target-ppc/helper_regs.h +47 -7

67 lines not on this page · raw message

target-ppc/machine.c +3 -1
index f4ac761..b969492 100644
@@ -90,9 +90,11 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
     qemu_get_betls(f, &env->nip);
     qemu_get_betls(f, &env->hflags);
     qemu_get_betls(f, &env->hflags_nmsr);
-    qemu_get_sbe32s(f, &env->mmu_idx);
     qemu_get_sbe32(f); /* Discard unused power_mode */
 
+    /* Ignore saved mmu_idx, recompute */
+    hreg_compute_mem_idx(env);
+
     return 0;
 }
 
target-ppc/translate.c +4 -3
index 308ad68..6d9f252 100644
@@ -11220,8 +11220,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
                 env->nip, env->lr, env->ctr, cpu_read_xer(env),
                 cs->cpu_index);
     cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx "  HF "
-                TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0],
-                env->hflags, env->mmu_idx);
+                TARGET_FMT_lx " iidx %d didx %d\n",
+                env->msr, env->spr[SPR_HID0],
+                env->hflags, env->immu_idx, env->dmmu_idx);
 #if !defined(NO_TIMER_DUMP)
     cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64
 #if !defined(CONFIG_USER_ONLY)
@@ -11426,7 +11427,7 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.spr_cb = env->spr_cb;
     ctx.pr = msr_pr;
     ctx.hv = !msr_pr && msr_hv;
-    ctx.mem_idx = env->mmu_idx;
+    ctx.mem_idx = env->dmmu_idx;
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
Signature · 1 line
2.5.0
#3 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 02/77] ppc: Use split I/D mmu modes to avoid flushes on interrupts

Quoted 105 lines · Benjamin Herrenschmidt

We rework the way the MMU indices are calculated, providing separate indices for I and D side based on MSR:IR and MSR:DR respectively, and thus no longer need to flush the TLB on context changes. This also adds correct support for HV as a separate address space.

---
 target-ppc/cpu.h         | 11 +++++++---
 target-ppc/excp_helper.c | 11 ----------
 target-ppc/helper_regs.h | 54 +++++++++++++++++++++++++++++++++++++++++-------
 target-ppc/machine.c     |  4 +++-
 target-ppc/translate.c   |  7 ++++---
 5 files changed, 62 insertions(+), 25 deletions(-)
target-ppc/cpu.h +8 -3
index 9ef0859..aaa7117 100644
@@ -462,6 +462,8 @@ struct ppc_slb_t {
 #define MSR_EP   6  /* Exception prefix on 601                               */
 #define MSR_IR   5  /* Instruction relocate                                  */
 #define MSR_DR   4  /* Data relocate                                         */
+#define MSR_IS   5  /* Instruction address space (BookE)                     */
+#define MSR_DS   4  /* Data address space (BookE)                            */
 #define MSR_PE   3  /* Protection enable on 403                              */
 #define MSR_PX   2  /* Protection exclusive on 403                  x        */
 #define MSR_PMM  2  /* Performance monitor mark on POWER            x        */
@@ -505,6 +507,8 @@ struct ppc_slb_t {
 #define msr_ep   ((env->msr >> MSR_EP)   & 1)
 #define msr_ir   ((env->msr >> MSR_IR)   & 1)
 #define msr_dr   ((env->msr >> MSR_DR)   & 1)
+#define msr_is   ((env->msr >> MSR_IS)   & 1)
+#define msr_ds   ((env->msr >> MSR_DS)   & 1)
 #define msr_pe   ((env->msr >> MSR_PE)   & 1)
 #define msr_px   ((env->msr >> MSR_PX)   & 1)
 #define msr_pmm  ((env->msr >> MSR_PMM)  & 1)
@@ -944,7 +948,7 @@ struct ppc_segment_page_sizes {
 
 /*****************************************************************************/
 /* The whole PowerPC CPU context */
-#define NB_MMU_MODES 3
+#define NB_MMU_MODES    8
 
 #define PPC_CPU_OPCODES_LEN          0x40
 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20
@@ -1108,7 +1112,8 @@ struct CPUPPCState {
     /* Those resources are used only in QEMU core */
     target_ulong hflags;      /* hflags is a MSR & HFLAGS_MASK         */
     target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */
-    int mmu_idx;         /* precomputed MMU index to speed up mem accesses */
+    int immu_idx;         /* precomputed MMU index to speed up insn access */
+    int dmmu_idx;         /* precomputed MMU index to speed up data accesses */
 
     /* Power management */
     int (*check_pow)(CPUPPCState *env);
@@ -1249,7 +1254,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
 #define MMU_USER_IDX 0
 static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 {
-    return env->mmu_idx;
+    return ifetch ? env->immu_idx : env->dmmu_idx;
 }
 
 #include "exec/cpu-all.h"
target-ppc/excp_helper.c +0 -11
index 4250106..3e39098 100644
@@ -623,9 +623,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
 
     if (env->spr[SPR_LPCR] & LPCR_AIL) {
         new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-    } else if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) {
-        /* If we disactivated any translation, flush TLBs */
-        tlb_flush(cs, 1);
     }
 
 #ifdef TARGET_PPC64
@@ -674,14 +671,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     /* Reset exception state */
     cs->exception_index = POWERPC_EXCP_NONE;
     env->error_code = 0;
-
-    if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
-        (env->mmu_model == POWERPC_MMU_BOOKE206)) {
-        /* XXX: The BookE changes address space when switching modes,
-                we should probably implement that as different MMU indexes,
-                but for the moment we do it the slow way and flush all.  */
-        tlb_flush(cs, 1);
-    }
 }
 
 void ppc_cpu_do_interrupt(CPUState *cs)
target-ppc/helper_regs.h +1 -3
index 271fddf..f7edd5b 100644
@@ -41,11 +41,50 @@ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env)
 
 static inline void hreg_compute_mem_idx(CPUPPCState *env)
 {
-    /* Precompute MMU index */
-    if (msr_pr == 0 && msr_hv != 0) {
-        env->mmu_idx = 2;
+    /* This is our encoding for server processors

target-ppc/helper_regs.h static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) · line 54

Quoted 10 lines
target-ppc/helper_regs.h +10 -0
+     *
+     *   0 = Guest User space virtual mode
+     *   1 = Guest Kernel space virtual mode
+     *   2 = Guest Kernel space real mode
+     *   3 = HV User space virtual mode
+     *   4 = HV Kernel space virtual mode
+     *   5 = HV Kernel space real mode
+     *
+     * The combination PR=1 IR&DR=0 is invalid, we will treat
+     * it as IR=DR=1

Hmm.. so being in problem state with translation off would certainly be a bad idea, but would it actually behave this way on CPU hardware?

target-ppc/helper_regs.h static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) · line 66

Quoted 12 lines
target-ppc/helper_regs.h +12 -0
+     *
+     * For BookE, we need 8 MMU modes as follow:
+     *
+     *  0 = AS 0 HV User space
+     *  1 = AS 0 HV Kernel space
+     *  2 = AS 1 HV User space
+     *  3 = AS 1 HV Kernel space
+     *  4 = AS 0 Guest User space
+     *  5 = AS 0 Guest Kernel space
+     *  6 = AS 1 Guest User space
+     *  7 = AS 1 Guest Kernel space
+     */

I'm wondering if it might be simpler to unify these and allow all 8 theoretical possibilities (hv/guest * user/kernel * translationmode) for both server and BookE.

Quoted 44 lines
target-ppc/helper_regs.h +24 -4
+    if (env->mmu_model & POWERPC_MMU_BOOKE) {
+        env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1;
+        env->immu_idx += msr_is ? 2 : 0;
+        env->dmmu_idx += msr_ds ? 2 : 0;
+        env->immu_idx += msr_gs ? 4 : 0;
+        env->dmmu_idx += msr_gs ? 4 : 0;
     } else {
-        env->mmu_idx = 1 - msr_pr;
+        /* First calucalte a base value independent of HV */
+        if (msr_pr != 0) {
+            /* User space, ignore IR and DR */
+            env->immu_idx = env->dmmu_idx = 0;
+        } else {
+            /* Kernel, setup a base I/D value */
+            env->immu_idx = msr_ir ? 1 : 2;
+            env->dmmu_idx = msr_dr ? 1 : 2;
+        }
+        /* Then offset it for HV */
+        if (msr_hv) {
+            env->immu_idx += 3;
+            env->dmmu_idx += 3;
+        }
     }
 }
 
@@ -82,9 +121,10 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
     }
     if (((value >> MSR_IR) & 1) != msr_ir ||
         ((value >> MSR_DR) & 1) != msr_dr) {
-        /* Flush all tlb when changing translation mode */
-        tlb_flush(cs, 1);
-        excp = POWERPC_EXCP_NONE;
+        cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+    }
+    if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
+        ((value >> MSR_GS) & 1) != msr_gs) {
         cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
     }
     if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
target-ppc/machine.c +0 -0
index f4ac761..b969492 100644

target-ppc/machine.c static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) · line 93

Quoted 4 lines
target-ppc/machine.c +0 -1
@@ -90,9 +90,11 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
     qemu_get_betls(f, &env->nip);
     qemu_get_betls(f, &env->hflags);
     qemu_get_betls(f, &env->hflags_nmsr);
-    qemu_get_sbe32s(f, &env->mmu_idx);

Have I missed something, or do you still need a read here to read the mmux_idx, even though you'll ignore it, otherwise you'll get out of sync and break migration from an old stream.

Quoted 33 lines
target-ppc/machine.c +3 -0
     qemu_get_sbe32(f); /* Discard unused power_mode */
 
+    /* Ignore saved mmu_idx, recompute */
+    hreg_compute_mem_idx(env);
+
     return 0;
 }
 
target-ppc/translate.c +4 -3
index 308ad68..6d9f252 100644
@@ -11220,8 +11220,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
                 env->nip, env->lr, env->ctr, cpu_read_xer(env),
                 cs->cpu_index);
     cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx "  HF "
-                TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0],
-                env->hflags, env->mmu_idx);
+                TARGET_FMT_lx " iidx %d didx %d\n",
+                env->msr, env->spr[SPR_HID0],
+                env->hflags, env->immu_idx, env->dmmu_idx);
 #if !defined(NO_TIMER_DUMP)
     cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64
 #if !defined(CONFIG_USER_ONLY)
@@ -11426,7 +11427,7 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.spr_cb = env->spr_cb;
     ctx.pr = msr_pr;
     ctx.hv = !msr_pr && msr_hv;
-    ctx.mem_idx = env->mmu_idx;
+    ctx.mem_idx = env->dmmu_idx;
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#4 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 02/77] ppc: Use split I/D mmu modes to avoid flushes on interrupts

Thanks for reviewing !

Quoted 17 lines · David Gibson
Quoted 8 lines

 static inline void hreg_compute_mem_idx(CPUPPCState *env) {
-    /* Precompute MMU index */
-    if (msr_pr == 0 && msr_hv != 0) {
-        env->mmu_idx = 2;
+    /* This is our encoding for server processors +     *
+     *   0 = Guest User space virtual mode

Quoted 8 lines

+     *   1 = Guest Kernel space virtual mode
+     *   2 = Guest Kernel space real mode
+     *   3 = HV User space virtual mode
+     *   4 = HV Kernel space virtual mode
+     *   5 = HV Kernel space real mode
+     *
+     * The combination PR=1 IR&DR=0 is invalid, we will treat +     * it as IR=DR=1

Quoted 2 lines

Hmm.. so being in problem state with translation off would certainly be a bad idea, but would it actually behave this way on CPU hardware?

No, it's not allowed in HW. I think (maybe in another patch) I enforce it. Architecturally, setting PR=1 will fore IR, DR and EE to 1

Quoted 13 lines
Quoted 4 lines

+     *
+     * For BookE, we need 8 MMU modes as follow:
+     *
+     *  0 = AS 0 HV User space

Quoted 8 lines

+     *  1 = AS 0 HV Kernel space
+     *  2 = AS 1 HV User space
+     *  3 = AS 1 HV Kernel space
+     *  4 = AS 0 Guest User space
+     *  5 = AS 0 Guest Kernel space
+     *  6 = AS 1 Guest User space
+     *  7 = AS 1 Guest Kernel space
+     */

Quoted 3 lines

I'm wondering if it might be simpler to unify these and allow all 8 theoretical possibilities (hv/guest * user/kernel * translationmode) for both server and BookE.

I don't see the point. Server doesn't have "AS" and the distinction only appears in that single function...

Quoted 46 lines
Quoted 46 lines

+    if (env->mmu_model & POWERPC_MMU_BOOKE) {
+        env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1; +        env->immu_idx += msr_is ? 2 : 0;
+        env->dmmu_idx += msr_ds ? 2 : 0;
+        env->immu_idx += msr_gs ? 4 : 0;
+        env->dmmu_idx += msr_gs ? 4 : 0;
     } else {
-        env->mmu_idx = 1 - msr_pr;
+        /* First calucalte a base value independent of HV */ +        if (msr_pr != 0) {
+            /* User space, ignore IR and DR */
+            env->immu_idx = env->dmmu_idx = 0;
+        } else {
+            /* Kernel, setup a base I/D value */
+            env->immu_idx = msr_ir ? 1 : 2;
+            env->dmmu_idx = msr_dr ? 1 : 2;
+        }
+        /* Then offset it for HV */
+        if (msr_hv) {
+            env->immu_idx += 3;
+            env->dmmu_idx += 3;
+        }
     }
 }

(quoted diff) +0 -0
@@ -82,9 +121,10 @@ static inline int hreg_store_msr(CPUPPCState
 *env, target_ulong value,
      }

     if (((value >> MSR_IR) & 1) != msr_ir || ((value >> MSR_DR) & 1) != msr_dr) {
-        /* Flush all tlb when changing translation mode */ -        tlb_flush(cs, 1);
-        excp = POWERPC_EXCP_NONE;
+        cs->interrupt_request |= CPU_INTERRUPT_EXITTB; +    }
+    if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
+        ((value >> MSR_GS) & 1) != msr_gs) { cs->interrupt_request |= CPU_INTERRUPT_EXITTB; }
     if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&

target-ppc/machine.c +0 -0
index f4ac761..b969492 100644
@@ -90,9 +90,11 @@ static int cpu_load_old(QEMUFile *f, void
 *opaque, int version_id)
Quoted 8 lines

target-ppc/machine.c static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) · line 93

Quoted 4 lines
target-ppc/machine.c +0 -1
      qemu_get_betls(f, &env->nip);
      qemu_get_betls(f, &env->hflags);
      qemu_get_betls(f, &env->hflags_nmsr);
-    qemu_get_sbe32s(f, &env->mmu_idx);

Have I missed something, or do you still need a read here to read the mmux_idx, even though you'll ignore it, otherwise you'll get out of sync and break migration from an old stream.

I am not completely cognizant of the migration stuff, that's very possible yes. Do I need to read into a dummy local ? Or is there a way to just say "drop 4 bytes from stream" ?

Note that I have generally completely overlooked the migration impact of my patches, this is something that I need to do but I wouldn't mind your help identifying the parts.

Quoted 38 lines
Quoted 37 lines

     qemu_get_sbe32(f); /* Discard unused power_mode */
+    /* Ignore saved mmu_idx, recompute */
+    hreg_compute_mem_idx(env);
+
     return 0;
 }

target-ppc/translate.c +0 -0
index 308ad68..6d9f252 100644
@@ -11220,8 +11220,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE
 *f, fprintf_function cpu_fprintf,
                  env->nip, env->lr, env->ctr, cpu_read_xer(env),
                  cs->cpu_index);

     cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx "  HF "
-                TARGET_FMT_lx " idx %d\n", env->msr, env-

Quoted 1 line

spr[SPR_HID0],

-                env->hflags, env->mmu_idx);
+                TARGET_FMT_lx " iidx %d didx %d\n", +                env->msr, env->spr[SPR_HID0],
+                env->hflags, env->immu_idx, env->dmmu_idx); #if !defined(NO_TIMER_DUMP)
     cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 #if !defined(CONFIG_USER_ONLY)

target-ppc/translate.c +1 -1
@@ -11426,7 +11427,7 @@ void gen_intermediate_code(CPUPPCState
 *env, struct TranslationBlock *tb)
      ctx.spr_cb = env->spr_cb;
      ctx.pr = msr_pr;
      ctx.hv = !msr_pr && msr_hv;
-    ctx.mem_idx = env->mmu_idx;
+    ctx.mem_idx = env->dmmu_idx;
      ctx.insns_flags = env->insns_flags;
      ctx.insns_flags2 = env->insns_flags2;

     ctx.access_type = -1;

#5 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 02/77] ppc: Use split I/D mmu modes to avoid flushes on interrupts

Quoted 2 lines · Benjamin Herrenschmidt

Thanks for reviewing !
On Mon, 2015-11-16 at 15:49 +1100, David Gibson wrote:

[snip]

Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
target-ppc/machine.c +0 -0
index f4ac761..b969492 100644
@@ -90,9 +90,11 @@ static int cpu_load_old(QEMUFile *f, void
 *opaque, int version_id)
Quoted 8 lines

target-ppc/machine.c static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) · line 93

Quoted 4 lines
target-ppc/machine.c +0 -1
      qemu_get_betls(f, &env->nip);
      qemu_get_betls(f, &env->hflags);
      qemu_get_betls(f, &env->hflags_nmsr);
-    qemu_get_sbe32s(f, &env->mmu_idx);

Have I missed something, or do you still need a read here to read the mmux_idx, even though you'll ignore it, otherwise you'll get out of sync and break migration from an old stream.

Quoted 3 lines

I am not completely cognizant of the migration stuff, that's very possible yes. Do I need to read into a dummy local ? Or is there a way to just say "drop 4 bytes from stream" ?

There's actually an example of this just below :)

Quoted 3 lines

Note that I have generally completely overlooked the migration impact of my patches, this is something that I need to do but I wouldn't mind your help identifying the parts.

Ok, I'll do what I can.

Quoted 1 line
Quoted 1 line
Quoted 1 line

     qemu_get_sbe32(f); /* Discard unused power_mode */

^^^
example of skipping a redundant value in the migration stream

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#6 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 02/77] ppc: Use split I/D mmu modes to avoid flushes on interrupts

In reply to Benjamin Herrenschmidt
Quoted 6 lines · Benjamin Herrenschmidt

We rework the way the MMU indices are calculated, providing separate indices for I and D side based on MSR:IR and MSR:DR respectively, and thus no longer need to flush the TLB on context changes. This also adds correct support for HV as a separate address space.

Paolo had a patch set poking at the same places a while back to speed up the ppc target by almost 10%.

Paolo, what happened to those patches? Would you prefer to rebase them on top of the HV bits or have Ben look into them while he's at it anyway? ;)

Alex

Quoted 203 lines
---
 target-ppc/cpu.h         | 11 +++++++---
 target-ppc/excp_helper.c | 11 ----------
 target-ppc/helper_regs.h | 54 +++++++++++++++++++++++++++++++++++++++++-------
 target-ppc/machine.c     |  4 +++-
 target-ppc/translate.c   |  7 ++++---
 5 files changed, 62 insertions(+), 25 deletions(-)
target-ppc/cpu.h +8 -3
index 9ef0859..aaa7117 100644
@@ -462,6 +462,8 @@ struct ppc_slb_t {
 #define MSR_EP   6  /* Exception prefix on 601                               */
 #define MSR_IR   5  /* Instruction relocate                                  */
 #define MSR_DR   4  /* Data relocate                                         */
+#define MSR_IS   5  /* Instruction address space (BookE)                     */
+#define MSR_DS   4  /* Data address space (BookE)                            */
 #define MSR_PE   3  /* Protection enable on 403                              */
 #define MSR_PX   2  /* Protection exclusive on 403                  x        */
 #define MSR_PMM  2  /* Performance monitor mark on POWER            x        */
@@ -505,6 +507,8 @@ struct ppc_slb_t {
 #define msr_ep   ((env->msr >> MSR_EP)   & 1)
 #define msr_ir   ((env->msr >> MSR_IR)   & 1)
 #define msr_dr   ((env->msr >> MSR_DR)   & 1)
+#define msr_is   ((env->msr >> MSR_IS)   & 1)
+#define msr_ds   ((env->msr >> MSR_DS)   & 1)
 #define msr_pe   ((env->msr >> MSR_PE)   & 1)
 #define msr_px   ((env->msr >> MSR_PX)   & 1)
 #define msr_pmm  ((env->msr >> MSR_PMM)  & 1)
@@ -944,7 +948,7 @@ struct ppc_segment_page_sizes {
 
 /*****************************************************************************/
 /* The whole PowerPC CPU context */
-#define NB_MMU_MODES 3
+#define NB_MMU_MODES    8
 
 #define PPC_CPU_OPCODES_LEN          0x40
 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20
@@ -1108,7 +1112,8 @@ struct CPUPPCState {
     /* Those resources are used only in QEMU core */
     target_ulong hflags;      /* hflags is a MSR & HFLAGS_MASK         */
     target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */
-    int mmu_idx;         /* precomputed MMU index to speed up mem accesses */
+    int immu_idx;         /* precomputed MMU index to speed up insn access */
+    int dmmu_idx;         /* precomputed MMU index to speed up data accesses */
 
     /* Power management */
     int (*check_pow)(CPUPPCState *env);
@@ -1249,7 +1254,7 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
 #define MMU_USER_IDX 0
 static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 {
-    return env->mmu_idx;
+    return ifetch ? env->immu_idx : env->dmmu_idx;
 }
 
 #include "exec/cpu-all.h"
target-ppc/excp_helper.c +0 -11
index 4250106..3e39098 100644
@@ -623,9 +623,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
 
     if (env->spr[SPR_LPCR] & LPCR_AIL) {
         new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-    } else if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) {
-        /* If we disactivated any translation, flush TLBs */
-        tlb_flush(cs, 1);
     }
 
 #ifdef TARGET_PPC64
@@ -674,14 +671,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     /* Reset exception state */
     cs->exception_index = POWERPC_EXCP_NONE;
     env->error_code = 0;
-
-    if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
-        (env->mmu_model == POWERPC_MMU_BOOKE206)) {
-        /* XXX: The BookE changes address space when switching modes,
-                we should probably implement that as different MMU indexes,
-                but for the moment we do it the slow way and flush all.  */
-        tlb_flush(cs, 1);
-    }
 }
 
 void ppc_cpu_do_interrupt(CPUState *cs)
target-ppc/helper_regs.h +47 -7
index 271fddf..f7edd5b 100644
@@ -41,11 +41,50 @@ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env)
 
 static inline void hreg_compute_mem_idx(CPUPPCState *env)
 {
-    /* Precompute MMU index */
-    if (msr_pr == 0 && msr_hv != 0) {
-        env->mmu_idx = 2;
+    /* This is our encoding for server processors
+     *
+     *   0 = Guest User space virtual mode
+     *   1 = Guest Kernel space virtual mode
+     *   2 = Guest Kernel space real mode
+     *   3 = HV User space virtual mode
+     *   4 = HV Kernel space virtual mode
+     *   5 = HV Kernel space real mode
+     *
+     * The combination PR=1 IR&DR=0 is invalid, we will treat
+     * it as IR=DR=1
+     *
+     * For BookE, we need 8 MMU modes as follow:
+     *
+     *  0 = AS 0 HV User space
+     *  1 = AS 0 HV Kernel space
+     *  2 = AS 1 HV User space
+     *  3 = AS 1 HV Kernel space
+     *  4 = AS 0 Guest User space
+     *  5 = AS 0 Guest Kernel space
+     *  6 = AS 1 Guest User space
+     *  7 = AS 1 Guest Kernel space
+     */
+    if (env->mmu_model & POWERPC_MMU_BOOKE) {
+        env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1;
+        env->immu_idx += msr_is ? 2 : 0;
+        env->dmmu_idx += msr_ds ? 2 : 0;
+        env->immu_idx += msr_gs ? 4 : 0;
+        env->dmmu_idx += msr_gs ? 4 : 0;
     } else {
-        env->mmu_idx = 1 - msr_pr;
+        /* First calucalte a base value independent of HV */
+        if (msr_pr != 0) {
+            /* User space, ignore IR and DR */
+            env->immu_idx = env->dmmu_idx = 0;
+        } else {
+            /* Kernel, setup a base I/D value */
+            env->immu_idx = msr_ir ? 1 : 2;
+            env->dmmu_idx = msr_dr ? 1 : 2;
+        }
+        /* Then offset it for HV */
+        if (msr_hv) {
+            env->immu_idx += 3;
+            env->dmmu_idx += 3;
+        }
     }
 }
 
@@ -82,9 +121,10 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
     }
     if (((value >> MSR_IR) & 1) != msr_ir ||
         ((value >> MSR_DR) & 1) != msr_dr) {
-        /* Flush all tlb when changing translation mode */
-        tlb_flush(cs, 1);
-        excp = POWERPC_EXCP_NONE;
+        cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+    }
+    if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
+        ((value >> MSR_GS) & 1) != msr_gs) {
         cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
     }
     if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
target-ppc/machine.c +3 -1
index f4ac761..b969492 100644
@@ -90,9 +90,11 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
     qemu_get_betls(f, &env->nip);
     qemu_get_betls(f, &env->hflags);
     qemu_get_betls(f, &env->hflags_nmsr);
-    qemu_get_sbe32s(f, &env->mmu_idx);
     qemu_get_sbe32(f); /* Discard unused power_mode */
 
+    /* Ignore saved mmu_idx, recompute */
+    hreg_compute_mem_idx(env);
+
     return 0;
 }
 
target-ppc/translate.c +4 -3
index 308ad68..6d9f252 100644
@@ -11220,8 +11220,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
                 env->nip, env->lr, env->ctr, cpu_read_xer(env),
                 cs->cpu_index);
     cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx "  HF "
-                TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0],
-                env->hflags, env->mmu_idx);
+                TARGET_FMT_lx " iidx %d didx %d\n",
+                env->msr, env->spr[SPR_HID0],
+                env->hflags, env->immu_idx, env->dmmu_idx);
 #if !defined(NO_TIMER_DUMP)
     cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64
 #if !defined(CONFIG_USER_ONLY)
@@ -11426,7 +11427,7 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.spr_cb = env->spr_cb;
     ctx.pr = msr_pr;
     ctx.hv = !msr_pr && msr_hv;
-    ctx.mem_idx = env->mmu_idx;
+    ctx.mem_idx = env->dmmu_idx;
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
#7 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 02/77] ppc: Use split I/D mmu modes to avoid flushes on interrupts

Quoted 5 lines · Alexander Graf
Quoted 5 lines

We rework the way the MMU indices are calculated, providing separate indices for I and D side based on MSR:IR and MSR:DR respectively, and thus no longer need to flush the TLB on context changes. This also adds correct support for HV as a separate address space.

Quoted 7 lines
Quoted 1 line

Paolo had a patch set poking at the same places a while back to speed up the ppc target by almost 10%.

Paolo, what happened to those patches? Would you prefer to rebase them on top of the HV bits or have Ben look into them while he's at it anyway? ;)

Ben decided to do it this way, which avoids a proliferation of MMU modes. The net effect of his patches is the same if not better.

Paolo

#8 lore raw

PATCH 3/77 ppc: Do some batching of TCG tlb flushes

In reply to Benjamin Herrenschmidt

On ppc64 especially, we flush the tlb on any slbie or tlbie instruction.

However, those instructions often come in bursts of 3 or more (context switch will favor a series of slbie's for example to an slbia if the SLB has less than a certain number of entries in it, and tlbie's can happen in a series, with PAPR, H_BULK_REMOVE can remove up to 4 entries at a time.

Doing a tlb_flush() each time is a waste of time. We end up doing a memset of the whole TLB, reloading it for the next instruction, memset'ing again, etc...

Those instructions don't have to take effect immediately. For slbie, they can wait for the next context synchronizing event. For tlbie, the next tlbsync.

This implements batching by keeping a flag that indicates that we have a TLB in need of flushing. We check it on interrupts, rfi's, isync's and tlbsync and flush the TLB if needed.

This reduces the number of tlb_flush() on a boot to a ubuntu installer first dialog screen from roughly 360K down to 36K.

---
 hw/ppc/spapr_hcall.c     | 12 +++++++++---
 target-ppc/cpu.h         |  2 ++
 target-ppc/excp_helper.c |  9 +++++++++
 target-ppc/helper.h      |  1 +
 target-ppc/helper_regs.h | 13 +++++++++++++
 target-ppc/mmu-hash64.c  | 12 +++---------
 target-ppc/mmu_helper.c  |  9 ++++++++-
 target-ppc/translate.c   | 39 ++++++++++++++++++++++++++++++++++++---
 8 files changed, 81 insertions(+), 16 deletions(-)
hw/ppc/spapr_hcall.c +9 -3
index cebceea..7e2cb4b 100644
@@ -220,6 +220,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 
     switch (ret) {
     case REMOVE_SUCCESS:
+        check_tlb_flush(env);
         return H_SUCCESS;
 
     case REMOVE_NOT_FOUND:
@@ -257,6 +258,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                                   target_ulong opcode, target_ulong *args)
 {
     CPUPPCState *env = &cpu->env;
+    target_ulong rc = H_SUCCESS;
     int i;
 
     for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
@@ -290,14 +292,18 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
             break;
 
         case REMOVE_PARM:
-            return H_PARAMETER;
+            rc = H_PARAMETER;
+            goto exit;
 
         case REMOVE_HW:
-            return H_HARDWARE;
+            rc = H_HARDWARE;
+            goto exit;
         }
     }
+ exit:
+    check_tlb_flush(env);
 
-    return H_SUCCESS;
+    return rc;
 }
 
 static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target-ppc/cpu.h +2 -0
index aaa7117..e6c43f9 100644
@@ -1013,6 +1013,8 @@ struct CPUPPCState {
     /* PowerPC 64 SLB area */
     ppc_slb_t slb[MAX_SLB_ENTRIES];
     int32_t slb_nr;
+    /* tcg TLB needs flush (deferred slb inval instruction typically) */
+    uint32_t tlb_need_flush;
 #endif
     /* segment registers */
     hwaddr htab_base;
target-ppc/excp_helper.c +9 -0
index 3e39098..c1d6605 100644
@@ -671,6 +671,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     /* Reset exception state */
     cs->exception_index = POWERPC_EXCP_NONE;
     env->error_code = 0;
+
+    /* Any interrupt is context synchronizing, check if TCG TLB
+     * needs a delayed flush on ppc64
+     */
+    check_tlb_flush(env);
 }
 
 void ppc_cpu_do_interrupt(CPUState *cs)
@@ -692,6 +697,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
                   __func__, env, env->pending_interrupts,
                   cs->interrupt_request, (int)msr_me, (int)msr_ee);
 #endif
+
     /* External reset */
     if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
         env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
@@ -896,6 +902,9 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
      * as rfi is always the last insn of a TB
      */
     cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+
+    /* Context synchronizing: check if TCG TLB needs flush */
+    check_tlb_flush(env);
 }
 
 void helper_rfi(CPUPPCState *env)
target-ppc/helper.h +1 -0
index 869be15..ff2d50b 100644
@@ -16,6 +16,7 @@ DEF_HELPER_1(rfmci, void, env)
 DEF_HELPER_1(rfid, void, env)
 DEF_HELPER_1(hrfid, void, env)
 #endif
+DEF_HELPER_1(check_tlb_flush, void, env)
 #endif
 
 DEF_HELPER_3(lmw, void, env, tl, i32)
target-ppc/helper_regs.h +13 -0
index f7edd5b..57da931 100644
@@ -151,4 +151,17 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
     return excp;
 }
 
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
+static inline void check_tlb_flush(CPUPPCState *env)
+{
+    CPUState *cs = CPU(ppc_env_get_cpu(env));
+    if (env->tlb_need_flush) {
+        env->tlb_need_flush = 0;
+        tlb_flush(cs, 1);
+    }
+}
+#else
+static inline void check_tlb_flush(CPUPPCState *env) { }
+#endif
+
 #endif /* !defined(__HELPER_REGS_H__) */
target-ppc/mmu-hash64.c +3 -9
index 7df6ede..71e1d14 100644
@@ -97,10 +97,8 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
 
 void helper_slbia(CPUPPCState *env)
 {
-    PowerPCCPU *cpu = ppc_env_get_cpu(env);
-    int n, do_invalidate;
+    int n;
 
-    do_invalidate = 0;
     /* XXX: Warning: slbia never invalidates the first segment */
     for (n = 1; n < env->slb_nr; n++) {
         ppc_slb_t *slb = &env->slb[n];
@@ -111,17 +109,13 @@ void helper_slbia(CPUPPCState *env)
              *      and we still don't have a tlb_flush_mask(env, n, mask)
              *      in QEMU, we just invalidate all TLBs
              */
-            do_invalidate = 1;
+            env->tlb_need_flush = true;
         }
     }
-    if (do_invalidate) {
-        tlb_flush(CPU(cpu), 1);
-    }
 }
 
 void helper_slbie(CPUPPCState *env, target_ulong addr)
 {
-    PowerPCCPU *cpu = ppc_env_get_cpu(env);
     ppc_slb_t *slb;
 
     slb = slb_lookup(env, addr);
@@ -136,7 +130,7 @@ void helper_slbie(CPUPPCState *env, target_ulong addr)
          *      and we still don't have a tlb_flush_mask(env, n, mask)
          *      in QEMU, we just invalidate all TLBs
          */
-        tlb_flush(CPU(cpu), 1);
+        env->tlb_need_flush = true;
     }
 }
 
target-ppc/mmu_helper.c +8 -1
index e52d0e5..54bc5d1 100644
@@ -23,6 +23,7 @@
 #include "mmu-hash64.h"
 #include "mmu-hash32.h"
 #include "exec/cpu_ldst.h"
+#include "helper_regs.h"
 
 //#define DEBUG_MMU
 //#define DEBUG_BATS
@@ -1940,6 +1941,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)
     case POWERPC_MMU_2_03:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_07:
+        env->tlb_need_flush = 0;
 #endif /* defined(TARGET_PPC64) */
         tlb_flush(CPU(cpu), 1);
         break;
@@ -2019,7 +2021,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
          *      and we still don't have a tlb_flush_mask(env, n, mask) in QEMU,
          *      we just invalidate all TLBs
          */
-        tlb_flush(CPU(cpu), 1);
+        env->tlb_need_flush = 1;
         break;
 #endif /* defined(TARGET_PPC64) */
     default:
@@ -2904,6 +2906,11 @@ void helper_booke206_tlbflush(CPUPPCState *env, target_ulong type)
 }
 
 
+void helper_check_tlb_flush(CPUPPCState *env)
+{
+    check_tlb_flush(env);
+}
+
 /*****************************************************************************/
 
 /* try to fill the TLB and return an exception if error. If retaddr is
target-ppc/translate.c +36 -3
index 6d9f252..e18d204 100644
@@ -3299,9 +3299,32 @@ static void gen_eieio(DisasContext *ctx)
 {
 }
 
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
+static inline void gen_check_tlb_flush(DisasContext *ctx)
+{
+    TCGv_i32 t = tcg_temp_new_i32();
+    TCGLabel *l = gen_new_label();
+
+    tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, tlb_need_flush));
+    tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, l);
+    gen_helper_check_tlb_flush(cpu_env);
+    gen_set_label(l);
+    tcg_temp_free_i32(t);
+}
+#else
+static inline void gen_check_tlb_flush(DisasContext *ctx) { }
+#endif
+
 /* isync */
 static void gen_isync(DisasContext *ctx)
 {
+    /*
+     * We need to check for a pending TLB flush. This can only happen in
+     * kernel mode however so check MSR_PR
+     */
+    if (!ctx->pr) {
+        gen_check_tlb_flush(ctx);
+    }
     gen_stop_exception(ctx);
 }
 
@@ -3458,6 +3481,15 @@ STCX(stqcx_, 16);
 /* sync */
 static void gen_sync(DisasContext *ctx)
 {
+    uint32_t l = (ctx->opcode >> 21) & 3;
+
+    /*
+     * For l == 2, it's a ptesync, We need to check for a pending TLB flush.
+     * This can only happen in kernel mode however so check MSR_PR as well.
+     */
+    if (l == 2 && !ctx->pr) {
+        gen_check_tlb_flush(ctx);
+    }
 }
 
 /* wait */
@@ -4851,10 +4883,11 @@ static void gen_tlbsync(DisasContext *ctx)
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
-    /* This has no effect: it should ensure that all previous
-     * tlbie have completed
+    /* tlbsync is a nop for server, ptesync handles delayed tlb flush,
+     * embedded however needs to deal with tlbsync. We don't try to be
+     * fancy and swallow the overhead of checking for both.
      */
-    gen_stop_exception(ctx);
+    gen_check_tlb_flush(ctx);
 #endif
 }
 
Signature · 1 line
2.5.0
#9 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 03/77] ppc: Do some batching of TCG tlb flushes

Quoted 215 lines · Benjamin Herrenschmidt

On ppc64 especially, we flush the tlb on any slbie or tlbie instruction.

However, those instructions often come in bursts of 3 or more (context switch will favor a series of slbie's for example to an slbia if the SLB has less than a certain number of entries in it, and tlbie's can happen in a series, with PAPR, H_BULK_REMOVE can remove up to 4 entries at a time.

Doing a tlb_flush() each time is a waste of time. We end up doing a memset of the whole TLB, reloading it for the next instruction, memset'ing again, etc...

Those instructions don't have to take effect immediately. For slbie, they can wait for the next context synchronizing event. For tlbie, the next tlbsync.

This implements batching by keeping a flag that indicates that we have a TLB in need of flushing. We check it on interrupts, rfi's, isync's and tlbsync and flush the TLB if needed.

This reduces the number of tlb_flush() on a boot to a ubuntu installer first dialog screen from roughly 360K down to 36K.

---
 hw/ppc/spapr_hcall.c     | 12 +++++++++---
 target-ppc/cpu.h         |  2 ++
 target-ppc/excp_helper.c |  9 +++++++++
 target-ppc/helper.h      |  1 +
 target-ppc/helper_regs.h | 13 +++++++++++++
 target-ppc/mmu-hash64.c  | 12 +++---------
 target-ppc/mmu_helper.c  |  9 ++++++++-
 target-ppc/translate.c   | 39 ++++++++++++++++++++++++++++++++++++---
 8 files changed, 81 insertions(+), 16 deletions(-)
hw/ppc/spapr_hcall.c +9 -3
index cebceea..7e2cb4b 100644
@@ -220,6 +220,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
 
     switch (ret) {
     case REMOVE_SUCCESS:
+        check_tlb_flush(env);
         return H_SUCCESS;
 
     case REMOVE_NOT_FOUND:
@@ -257,6 +258,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                                   target_ulong opcode, target_ulong *args)
 {
     CPUPPCState *env = &cpu->env;
+    target_ulong rc = H_SUCCESS;
     int i;
 
     for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
@@ -290,14 +292,18 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
             break;
 
         case REMOVE_PARM:
-            return H_PARAMETER;
+            rc = H_PARAMETER;
+            goto exit;
 
         case REMOVE_HW:
-            return H_HARDWARE;
+            rc = H_HARDWARE;
+            goto exit;
         }
     }
+ exit:
+    check_tlb_flush(env);
 
-    return H_SUCCESS;
+    return rc;
 }
 
 static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target-ppc/cpu.h +2 -0
index aaa7117..e6c43f9 100644
@@ -1013,6 +1013,8 @@ struct CPUPPCState {
     /* PowerPC 64 SLB area */
     ppc_slb_t slb[MAX_SLB_ENTRIES];
     int32_t slb_nr;
+    /* tcg TLB needs flush (deferred slb inval instruction typically) */
+    uint32_t tlb_need_flush;
 #endif
     /* segment registers */
     hwaddr htab_base;
target-ppc/excp_helper.c +9 -0
index 3e39098..c1d6605 100644
@@ -671,6 +671,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     /* Reset exception state */
     cs->exception_index = POWERPC_EXCP_NONE;
     env->error_code = 0;
+
+    /* Any interrupt is context synchronizing, check if TCG TLB
+     * needs a delayed flush on ppc64
+     */
+    check_tlb_flush(env);
 }
 
 void ppc_cpu_do_interrupt(CPUState *cs)
@@ -692,6 +697,7 @@ static void ppc_hw_interrupt(CPUPPCState *env)
                   __func__, env, env->pending_interrupts,
                   cs->interrupt_request, (int)msr_me, (int)msr_ee);
 #endif
+
     /* External reset */
     if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
         env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
@@ -896,6 +902,9 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
      * as rfi is always the last insn of a TB
      */
     cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+
+    /* Context synchronizing: check if TCG TLB needs flush */
+    check_tlb_flush(env);
 }
 
 void helper_rfi(CPUPPCState *env)
target-ppc/helper.h +1 -0
index 869be15..ff2d50b 100644
@@ -16,6 +16,7 @@ DEF_HELPER_1(rfmci, void, env)
 DEF_HELPER_1(rfid, void, env)
 DEF_HELPER_1(hrfid, void, env)
 #endif
+DEF_HELPER_1(check_tlb_flush, void, env)
 #endif
 
 DEF_HELPER_3(lmw, void, env, tl, i32)
target-ppc/helper_regs.h +13 -0
index f7edd5b..57da931 100644
@@ -151,4 +151,17 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
     return excp;
 }
 
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
+static inline void check_tlb_flush(CPUPPCState *env)
+{
+    CPUState *cs = CPU(ppc_env_get_cpu(env));
+    if (env->tlb_need_flush) {
+        env->tlb_need_flush = 0;
+        tlb_flush(cs, 1);
+    }
+}
+#else
+static inline void check_tlb_flush(CPUPPCState *env) { }
+#endif
+
 #endif /* !defined(__HELPER_REGS_H__) */
target-ppc/mmu-hash64.c +3 -9
index 7df6ede..71e1d14 100644
@@ -97,10 +97,8 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env)
 
 void helper_slbia(CPUPPCState *env)
 {
-    PowerPCCPU *cpu = ppc_env_get_cpu(env);
-    int n, do_invalidate;
+    int n;
 
-    do_invalidate = 0;
     /* XXX: Warning: slbia never invalidates the first segment */
     for (n = 1; n < env->slb_nr; n++) {
         ppc_slb_t *slb = &env->slb[n];
@@ -111,17 +109,13 @@ void helper_slbia(CPUPPCState *env)
              *      and we still don't have a tlb_flush_mask(env, n, mask)
              *      in QEMU, we just invalidate all TLBs
              */
-            do_invalidate = 1;
+            env->tlb_need_flush = true;
         }
     }
-    if (do_invalidate) {
-        tlb_flush(CPU(cpu), 1);
-    }
 }
 
 void helper_slbie(CPUPPCState *env, target_ulong addr)
 {
-    PowerPCCPU *cpu = ppc_env_get_cpu(env);
     ppc_slb_t *slb;
 
     slb = slb_lookup(env, addr);
@@ -136,7 +130,7 @@ void helper_slbie(CPUPPCState *env, target_ulong addr)
          *      and we still don't have a tlb_flush_mask(env, n, mask)
          *      in QEMU, we just invalidate all TLBs
          */
-        tlb_flush(CPU(cpu), 1);
+        env->tlb_need_flush = true;
     }
 }
 
target-ppc/mmu_helper.c +1 -0
index e52d0e5..54bc5d1 100644
@@ -23,6 +23,7 @@
 #include "mmu-hash64.h"
 #include "mmu-hash32.h"
 #include "exec/cpu_ldst.h"
+#include "helper_regs.h"
 
 //#define DEBUG_MMU
 //#define DEBUG_BATS

target-ppc/mmu_helper.c void ppc_tlb_invalidate_all(CPUPPCState *env) · line 1947

Quoted 7 lines
target-ppc/mmu_helper.c +1 -0
@@ -1940,6 +1941,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)
     case POWERPC_MMU_2_03:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_07:
+        env->tlb_need_flush = 0;
 #endif /* defined(TARGET_PPC64) */
         tlb_flush(CPU(cpu), 1);
         break;

Any particular reason you're leaving this one as an immediate rather than deferred flush?

Quoted 79 lines
target-ppc/mmu_helper.c +6 -1
@@ -2019,7 +2021,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
          *      and we still don't have a tlb_flush_mask(env, n, mask) in QEMU,
          *      we just invalidate all TLBs
          */
-        tlb_flush(CPU(cpu), 1);
+        env->tlb_need_flush = 1;
         break;
 #endif /* defined(TARGET_PPC64) */
     default:
@@ -2904,6 +2906,11 @@ void helper_booke206_tlbflush(CPUPPCState *env, target_ulong type)
 }
 
 
+void helper_check_tlb_flush(CPUPPCState *env)
+{
+    check_tlb_flush(env);
+}
+
 /*****************************************************************************/
 
 /* try to fill the TLB and return an exception if error. If retaddr is
target-ppc/translate.c +32 -1
index 6d9f252..e18d204 100644
@@ -3299,9 +3299,32 @@ static void gen_eieio(DisasContext *ctx)
 {
 }
 
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
+static inline void gen_check_tlb_flush(DisasContext *ctx)
+{
+    TCGv_i32 t = tcg_temp_new_i32();
+    TCGLabel *l = gen_new_label();
+
+    tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, tlb_need_flush));
+    tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, l);
+    gen_helper_check_tlb_flush(cpu_env);
+    gen_set_label(l);
+    tcg_temp_free_i32(t);
+}
+#else
+static inline void gen_check_tlb_flush(DisasContext *ctx) { }
+#endif
+
 /* isync */
 static void gen_isync(DisasContext *ctx)
 {
+    /*
+     * We need to check for a pending TLB flush. This can only happen in
+     * kernel mode however so check MSR_PR
+     */
+    if (!ctx->pr) {
+        gen_check_tlb_flush(ctx);
+    }
     gen_stop_exception(ctx);
 }
 
@@ -3458,6 +3481,15 @@ STCX(stqcx_, 16);
 /* sync */
 static void gen_sync(DisasContext *ctx)
 {
+    uint32_t l = (ctx->opcode >> 21) & 3;
+
+    /*
+     * For l == 2, it's a ptesync, We need to check for a pending TLB flush.
+     * This can only happen in kernel mode however so check MSR_PR as well.
+     */
+    if (l == 2 && !ctx->pr) {
+        gen_check_tlb_flush(ctx);
+    }
 }
 
 /* wait */
@@ -4851,10 +4883,11 @@ static void gen_tlbsync(DisasContext *ctx)
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
-    /* This has no effect: it should ensure that all previous

target-ppc/translate.c static void gen_tlbsync(DisasContext *ctx) · line 4893

Quoted 10 lines
target-ppc/translate.c +4 -2
-     * tlbie have completed
+    /* tlbsync is a nop for server, ptesync handles delayed tlb flush,
+     * embedded however needs to deal with tlbsync. We don't try to be
+     * fancy and swallow the overhead of checking for both.
      */
-    gen_stop_exception(ctx);
+    gen_check_tlb_flush(ctx);
 #endif
 }
 

Should you be clearing the pending flush flag cpu_reset()?

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#10 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 03/77] ppc: Do some batching of TCG tlb flushes

Quoted 14 lines · David Gibson

Quoted 10 lines

 //#define DEBUG_MMU
 //#define DEBUG_BATS

(quoted diff) +0 -0
@@ -1940,6 +1941,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)

     case POWERPC_MMU_2_03:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_07:
+        env->tlb_need_flush = 0;
 #endif /* defined(TARGET_PPC64) */
         tlb_flush(CPU(cpu), 1);
         break;

Any particular reason you're leaving this one as an immediate rather than deferred flush?

A couple yes. It's mostly unused on server CPUs (we don't do tlbia), and it's used by ppc_cpu_reset(). In that latter case, I like having everything really cleaned up ...

Quoted 1 line

Should you be clearing the pending flush flag cpu_reset()?

That should happen as a result of the above.

Cheers,
Ben.

#11 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 03/77] ppc: Do some batching of TCG tlb flushes

Quoted 16 lines · Benjamin Herrenschmidt
Quoted 14 lines · David Gibson

Quoted 10 lines

 //#define DEBUG_MMU
 //#define DEBUG_BATS

(quoted diff) +0 -0
@@ -1940,6 +1941,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env)

     case POWERPC_MMU_2_03:
     case POWERPC_MMU_2_06:
     case POWERPC_MMU_2_07:
+        env->tlb_need_flush = 0;
 #endif /* defined(TARGET_PPC64) */
         tlb_flush(CPU(cpu), 1);
         break;

Any particular reason you're leaving this one as an immediate rather than deferred flush?

Quoted 7 lines

A couple yes. It's mostly unused on server CPUs (we don't do tlbia), and it's used by ppc_cpu_reset(). In that latter case, I like having everything really cleaned up ...

Quoted 1 line

Should you be clearing the pending flush flag cpu_reset()?

That should happen as a result of the above.

Ok.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#12 lore raw

PATCH 4/77 target-ppc: Use sensible POWER8/POWER8E versions

In reply to Benjamin Herrenschmidt

We never released anything older than POWER8 DD2.0 and POWER8E DD2.1, so let's use these versions, without that some firmware or Linux code might fail to use some HW features that were non functional in earlier internal only spins of the chip.

---
 target-ppc/cpu-models.c | 12 ++++++------
 target-ppc/cpu-models.h |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)
target-ppc/cpu-models.c +6 -6
index 4d5ab4b..349783e 100644
@@ -1138,10 +1138,10 @@
                 "POWER7 v2.3")
     POWERPC_DEF("POWER7+_v2.1",  CPU_POWERPC_POWER7P_v21,            POWER7,
                 "POWER7+ v2.1")
-    POWERPC_DEF("POWER8E_v1.0",  CPU_POWERPC_POWER8E_v10,            POWER8,
-                "POWER8E v1.0")
-    POWERPC_DEF("POWER8_v1.0",   CPU_POWERPC_POWER8_v10,             POWER8,
-                "POWER8 v1.0")
+    POWERPC_DEF("POWER8E_v2.1",  CPU_POWERPC_POWER8E_v21,            POWER8,
+                "POWER8E v2.1")
+    POWERPC_DEF("POWER8_v2.0",   CPU_POWERPC_POWER8_v20,             POWER8,
+                "POWER8 v2.0")
     POWERPC_DEF("970_v2.2",      CPU_POWERPC_970_v22,                970,
                 "PowerPC 970 v2.2")
     POWERPC_DEF("970fx_v1.0",    CPU_POWERPC_970FX_v10,              970,
@@ -1389,8 +1389,8 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
     { "POWER5gs", "POWER5+_v2.1" },
     { "POWER7", "POWER7_v2.3" },
     { "POWER7+", "POWER7+_v2.1" },
-    { "POWER8E", "POWER8E_v1.0" },
-    { "POWER8", "POWER8_v1.0" },
+    { "POWER8E", "POWER8E_v2.1" },
+    { "POWER8", "POWER8_v2.0" },
     { "970", "970_v2.2" },
     { "970fx", "970fx_v3.1" },
     { "970mp", "970mp_v1.1" },
target-ppc/cpu-models.h +2 -2
index 9d80e72..2992427 100644
@@ -557,9 +557,9 @@ enum {
     CPU_POWERPC_POWER7P_BASE       = 0x004A0000,
     CPU_POWERPC_POWER7P_v21        = 0x004A0201,
     CPU_POWERPC_POWER8E_BASE       = 0x004B0000,
-    CPU_POWERPC_POWER8E_v10        = 0x004B0100,
+    CPU_POWERPC_POWER8E_v21        = 0x004B0201,
     CPU_POWERPC_POWER8_BASE        = 0x004D0000,
-    CPU_POWERPC_POWER8_v10         = 0x004D0100,
+    CPU_POWERPC_POWER8_v20         = 0x004D0200,
     CPU_POWERPC_970_v22            = 0x00390202,
     CPU_POWERPC_970FX_v10          = 0x00391100,
     CPU_POWERPC_970FX_v20          = 0x003C0200,
Signature · 1 line
2.5.0
Acked-by on v1 4/77
#13 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 04/77] target-ppc: Use sensible POWER8/POWER8E versions

Quoted 4 lines · Benjamin Herrenschmidt

We never released anything older than POWER8 DD2.0 and POWER8E DD2.1, so let's use these versions, without that some firmware or Linux code might fail to use some HW features that were non functional in earlier internal only spins of the chip.

We were getting it wrong with mambo a while ago and it caused some oddness. There's zero reason for anyone to use pre-production processors these days, so:

#14 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 04/77] target-ppc: Use sensible POWER8/POWER8E versions

In reply to Benjamin Herrenschmidt
Quoted 6 lines · Benjamin Herrenschmidt

We never released anything older than POWER8 DD2.0 and POWER8E DD2.1, so let's use these versions, without that some firmware or Linux code might fail to use some HW features that were non functional in earlier internal only spins of the chip.

Hmm, I'm just wondering if replacing the old CPU models could have implications for migration from old images.

Quoted 51 lines
---
 target-ppc/cpu-models.c | 12 ++++++------
 target-ppc/cpu-models.h |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)
target-ppc/cpu-models.c +6 -6
index 4d5ab4b..349783e 100644
@@ -1138,10 +1138,10 @@
                 "POWER7 v2.3")
     POWERPC_DEF("POWER7+_v2.1",  CPU_POWERPC_POWER7P_v21,            POWER7,
                 "POWER7+ v2.1")
-    POWERPC_DEF("POWER8E_v1.0",  CPU_POWERPC_POWER8E_v10,            POWER8,
-                "POWER8E v1.0")
-    POWERPC_DEF("POWER8_v1.0",   CPU_POWERPC_POWER8_v10,             POWER8,
-                "POWER8 v1.0")
+    POWERPC_DEF("POWER8E_v2.1",  CPU_POWERPC_POWER8E_v21,            POWER8,
+                "POWER8E v2.1")
+    POWERPC_DEF("POWER8_v2.0",   CPU_POWERPC_POWER8_v20,             POWER8,
+                "POWER8 v2.0")
     POWERPC_DEF("970_v2.2",      CPU_POWERPC_970_v22,                970,
                 "PowerPC 970 v2.2")
     POWERPC_DEF("970fx_v1.0",    CPU_POWERPC_970FX_v10,              970,
@@ -1389,8 +1389,8 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
     { "POWER5gs", "POWER5+_v2.1" },
     { "POWER7", "POWER7_v2.3" },
     { "POWER7+", "POWER7+_v2.1" },
-    { "POWER8E", "POWER8E_v1.0" },
-    { "POWER8", "POWER8_v1.0" },
+    { "POWER8E", "POWER8E_v2.1" },
+    { "POWER8", "POWER8_v2.0" },
     { "970", "970_v2.2" },
     { "970fx", "970fx_v3.1" },
     { "970mp", "970mp_v1.1" },
target-ppc/cpu-models.h +2 -2
index 9d80e72..2992427 100644
@@ -557,9 +557,9 @@ enum {
     CPU_POWERPC_POWER7P_BASE       = 0x004A0000,
     CPU_POWERPC_POWER7P_v21        = 0x004A0201,
     CPU_POWERPC_POWER8E_BASE       = 0x004B0000,
-    CPU_POWERPC_POWER8E_v10        = 0x004B0100,
+    CPU_POWERPC_POWER8E_v21        = 0x004B0201,
     CPU_POWERPC_POWER8_BASE        = 0x004D0000,
-    CPU_POWERPC_POWER8_v10         = 0x004D0100,
+    CPU_POWERPC_POWER8_v20         = 0x004D0200,
     CPU_POWERPC_970_v22            = 0x00390202,
     CPU_POWERPC_970FX_v10          = 0x00391100,
     CPU_POWERPC_970FX_v20          = 0x003C0200,
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#15 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 04/77] target-ppc: Use sensible POWER8/POWER8E versions

Quoted 10 lines · David Gibson
Quoted 6 lines · Benjamin Herrenschmidt

We never released anything older than POWER8 DD2.0 and POWER8E DD2.1, so let's use these versions, without that some firmware or Linux code might fail to use some HW features that were non functional in earlier internal only spins of the chip.

Hmm, I'm just wondering if replacing the old CPU models could have implications for migration from old images.

I don't know, I'm not that familiar with migration. Alexey, what do you reckon ? I'd think KVM images use the real PVR so should be a non- issue, does anybody actually cares about migration of old TCG images ?

Quoted 52 lines
Quoted 51 lines

---
 target-ppc/cpu-models.c | 12 ++++++------
 target-ppc/cpu-models.h |  4 ++--

 2 files changed, 8 insertions(+), 8 deletions(-)
target-ppc/cpu-models.c +6 -6
index 4d5ab4b..349783e 100644
@@ -1138,10 +1138,10 @@
                  "POWER7 v2.3")
      POWERPC_DEF("POWER7+_v2.1",  CPU_POWERPC_POWER7P_v21,            POWER7,
                  "POWER7+ v2.1")
-    POWERPC_DEF("POWER8E_v1.0",  CPU_POWERPC_POWER8E_v10,            POWER8,
-                "POWER8E v1.0")
-    POWERPC_DEF("POWER8_v1.0",   CPU_POWERPC_POWER8_v10,             POWER8,
-                "POWER8 v1.0")
+    POWERPC_DEF("POWER8E_v2.1",  CPU_POWERPC_POWER8E_v21,            POWER8,
+                "POWER8E v2.1")
+    POWERPC_DEF("POWER8_v2.0",   CPU_POWERPC_POWER8_v20,             POWER8,
+                "POWER8 v2.0")
      POWERPC_DEF("970_v2.2",      CPU_POWERPC_970_v22,                970,
                  "PowerPC 970 v2.2")
      POWERPC_DEF("970fx_v1.0",    CPU_POWERPC_970FX_v10,              970,
@@ -1389,8 +1389,8 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
      { "POWER5gs", "POWER5+_v2.1" },
      { "POWER7", "POWER7_v2.3" },
      { "POWER7+", "POWER7+_v2.1" },
-    { "POWER8E", "POWER8E_v1.0" },
-    { "POWER8", "POWER8_v1.0" },
+    { "POWER8E", "POWER8E_v2.1" },
+    { "POWER8", "POWER8_v2.0" },
      { "970", "970_v2.2" },
      { "970fx", "970fx_v3.1" },
      { "970mp", "970mp_v1.1" },
target-ppc/cpu-models.h +2 -2
index 9d80e72..2992427 100644
@@ -557,9 +557,9 @@ enum {
      CPU_POWERPC_POWER7P_BASE       = 0x004A0000,
      CPU_POWERPC_POWER7P_v21        = 0x004A0201,
      CPU_POWERPC_POWER8E_BASE       = 0x004B0000,
-    CPU_POWERPC_POWER8E_v10        = 0x004B0100,
+    CPU_POWERPC_POWER8E_v21        = 0x004B0201,
      CPU_POWERPC_POWER8_BASE        = 0x004D0000,
-    CPU_POWERPC_POWER8_v10         = 0x004D0100,
+    CPU_POWERPC_POWER8_v20         = 0x004D0200,
      CPU_POWERPC_970_v22            = 0x00390202,
      CPU_POWERPC_970FX_v10          = 0x00391100,
      CPU_POWERPC_970FX_v20          = 0x003C0200,
#16 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 04/77] target-ppc: Use sensible POWER8/POWER8E versions

Quoted 7 lines · Benjamin Herrenschmidt
Quoted 6 lines · David Gibson
Quoted 5 lines · Benjamin Herrenschmidt

We never released anything older than POWER8 DD2.0 and POWER8E DD2.1, so let's use these versions, without that some firmware or Linux code might fail to use some HW features that were non functional in earlier internal only spins of the chip.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Hmm, I'm just wondering if replacing the old CPU models could have implications for migration from old images.

I don't know, I'm not that familiar with migration. Alexey, what do you reckon ? I'd think KVM images use the real PVR so should be a non- issue, does anybody actually cares about migration of old TCG images ?

I have never heard of anyone testing migration of TCG guests. KVM guests use host PVR anyway.

I just wonder that we "never released anything older than" but we still support them in the kernel - why do not we wipe these there too?

Quoted 53 lines
Quoted 53 lines
Quoted 52 lines
---
  target-ppc/cpu-models.c | 12 ++++++------
  target-ppc/cpu-models.h |  4 ++--
  2 files changed, 8 insertions(+), 8 deletions(-)
target-ppc/cpu-models.c +6 -6
index 4d5ab4b..349783e 100644
@@ -1138,10 +1138,10 @@
                  "POWER7 v2.3")
      POWERPC_DEF("POWER7+_v2.1",  CPU_POWERPC_POWER7P_v21,            POWER7,
                  "POWER7+ v2.1")
-    POWERPC_DEF("POWER8E_v1.0",  CPU_POWERPC_POWER8E_v10,            POWER8,
-                "POWER8E v1.0")
-    POWERPC_DEF("POWER8_v1.0",   CPU_POWERPC_POWER8_v10,             POWER8,
-                "POWER8 v1.0")
+    POWERPC_DEF("POWER8E_v2.1",  CPU_POWERPC_POWER8E_v21,            POWER8,
+                "POWER8E v2.1")
+    POWERPC_DEF("POWER8_v2.0",   CPU_POWERPC_POWER8_v20,             POWER8,
+                "POWER8 v2.0")
      POWERPC_DEF("970_v2.2",      CPU_POWERPC_970_v22,                970,
                  "PowerPC 970 v2.2")
      POWERPC_DEF("970fx_v1.0",    CPU_POWERPC_970FX_v10,              970,
@@ -1389,8 +1389,8 @@ PowerPCCPUAlias ppc_cpu_aliases[] = {
      { "POWER5gs", "POWER5+_v2.1" },
      { "POWER7", "POWER7_v2.3" },
      { "POWER7+", "POWER7+_v2.1" },
-    { "POWER8E", "POWER8E_v1.0" },
-    { "POWER8", "POWER8_v1.0" },
+    { "POWER8E", "POWER8E_v2.1" },
+    { "POWER8", "POWER8_v2.0" },
      { "970", "970_v2.2" },
      { "970fx", "970fx_v3.1" },
      { "970mp", "970mp_v1.1" },
target-ppc/cpu-models.h +2 -2
index 9d80e72..2992427 100644
@@ -557,9 +557,9 @@ enum {
      CPU_POWERPC_POWER7P_BASE       = 0x004A0000,
      CPU_POWERPC_POWER7P_v21        = 0x004A0201,
      CPU_POWERPC_POWER8E_BASE       = 0x004B0000,
-    CPU_POWERPC_POWER8E_v10        = 0x004B0100,
+    CPU_POWERPC_POWER8E_v21        = 0x004B0201,
      CPU_POWERPC_POWER8_BASE        = 0x004D0000,
-    CPU_POWERPC_POWER8_v10         = 0x004D0100,
+    CPU_POWERPC_POWER8_v20         = 0x004D0200,
      CPU_POWERPC_970_v22            = 0x00390202,
      CPU_POWERPC_970FX_v10          = 0x00391100,
      CPU_POWERPC_970FX_v20          = 0x003C0200,
Signature · 1 line
Alexey
#17 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 04/77] target-ppc: Use sensible POWER8/POWER8E versions

Quoted 6 lines · Alexey Kardashevskiy

I have never heard of anyone testing migration of TCG guests. KVM guests use host PVR anyway.

I just wonder that we "never released anything older than" but we still support them in the kernel - why do not we wipe these there too?

We could... it was handy in the early days when some selected partners had access to pre-release systems, to tell them to use upstream kernels :)

In the case of qemu, however, it's more harmful than anything else.

Cheers,
Ben.

#18 lore raw

PATCH 5/77 ppc: Update SPR definitions

In reply to Benjamin Herrenschmidt

Add definitions for additional SPR numbers and SPR bit definitions that will be relevant for subsequent improvements to POWER8 emulation

Also fix the definition of LPIDR which was incorrect (and is different for server and embedded).

---
 target-ppc/cpu.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 47 insertions(+), 7 deletions(-)
target-ppc/cpu.h +47 -7
index e6c43f9..611367f 100644
@@ -470,9 +470,17 @@ struct ppc_slb_t {
 #define MSR_RI   1  /* Recoverable interrupt                        1        */
 #define MSR_LE   0  /* Little-endian mode                           1 hflags */
 
-#define LPCR_ILE (1 << (63-38))
-#define LPCR_AIL_SHIFT (63-40)      /* Alternate interrupt location */
-#define LPCR_AIL (3 << LPCR_AIL_SHIFT)
+/* LPCR bits */
+#define LPCR_VPM0         (1ull << (63-0))
+#define LPCR_VPM1         (1ull << (63-1))
+#define LPCR_ISL          (1ull << (63-2))
+#define LPCR_KBV          (1ull << (63-3))
+#define LPCR_ILE          (1ull << (63-38))
+#define LPCR_MER          (1ull << (63-52))
+#define LPCR_LPES0        (1ull << (63-60))
+#define LPCR_LPES1        (1ull << (63-61))
+#define LPCR_AIL_SHIFT    (63-40)      /* Alternate interrupt location */
+#define LPCR_AIL          (3ull << LPCR_AIL_SHIFT)
 
 #define msr_sf   ((env->msr >> MSR_SF)   & 1)
 #define msr_isf  ((env->msr >> MSR_ISF)  & 1)
@@ -1338,6 +1346,10 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_UAMOR             (0x09D)
 #define SPR_MPC_ICTRL         (0x09E)
 #define SPR_MPC_BAR           (0x09F)
+#define SPR_DAWR              (0x0B4)
+#define SPR_RPR               (0x0BA)
+#define SPR_DAWRX             (0x0BC)
+#define SPR_HFSCR             (0x0BE)
 #define SPR_VRSAVE            (0x100)
 #define SPR_USPRG0            (0x100)
 #define SPR_USPRG1            (0x101)
@@ -1392,19 +1404,25 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_HSRR1             (0x13B)
 #define SPR_BOOKE_IAC4        (0x13B)
 #define SPR_BOOKE_DAC1        (0x13C)
-#define SPR_LPIDR             (0x13D)
+#define SPR_MMCRH             (0x13C)
 #define SPR_DABR2             (0x13D)
 #define SPR_BOOKE_DAC2        (0x13D)
+#define SPR_TFMR              (0x13D)
 #define SPR_BOOKE_DVC1        (0x13E)
 #define SPR_LPCR              (0x13E)
 #define SPR_BOOKE_DVC2        (0x13F)
+#define SPR_LPIDR             (0x13F)
 #define SPR_BOOKE_TSR         (0x150)
+#define SPR_HMER              (0x150)
+#define SPR_HMEER             (0x151)
 #define SPR_PCR               (0x152)
+#define SPR_BOOKE_LPIDR       (0x152)
 #define SPR_BOOKE_TCR         (0x154)
 #define SPR_BOOKE_TLB0PS      (0x158)
 #define SPR_BOOKE_TLB1PS      (0x159)
 #define SPR_BOOKE_TLB2PS      (0x15A)
 #define SPR_BOOKE_TLB3PS      (0x15B)
+#define SPR_AMOR	      (0x15D)
 #define SPR_BOOKE_MAS7_MAS3   (0x174)
 #define SPR_BOOKE_IVOR0       (0x190)
 #define SPR_BOOKE_IVOR1       (0x191)
@@ -1622,6 +1640,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_RCPU_L2U_RA3      (0x32B)
 #define SPR_TAR               (0x32F)
 #define SPR_VTB               (0x351)
+#define SPR_MMCRC             (0x353)
 #define SPR_440_INV0          (0x370)
 #define SPR_440_INV1          (0x371)
 #define SPR_440_INV2          (0x372)
@@ -1655,6 +1674,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_440_DVLIM         (0x398)
 #define SPR_750_WPAR          (0x399)
 #define SPR_440_IVLIM         (0x399)
+#define SPR_TSCR	      (0x399)
 #define SPR_750_DMAU          (0x39A)
 #define SPR_750_DMAL          (0x39B)
 #define SPR_440_RSTCFG        (0x39B)
@@ -1829,9 +1849,10 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define   L1CSR1_ICE		0x00000001	/* Instruction Cache Enable */
 
 /* HID0 bits */
-#define HID0_DEEPNAP        (1 << 24)
-#define HID0_DOZE           (1 << 23)
-#define HID0_NAP            (1 << 22)
+#define HID0_DEEPNAP        (1 << 24)	        /* pre-2.06 */
+#define HID0_DOZE           (1 << 23)	        /* pre-2.06 */
+#define HID0_NAP            (1 << 22)	        /* pre-2.06 */
+#define HID0_HILE           (1ull << (63-19))   /* POWER8 */
 
 /*****************************************************************************/
 /* PowerPC Instructions types definitions                                    */
@@ -2180,6 +2201,25 @@ enum {
     PCR_TM_DIS          = 1ull << (63-2), /* Trans. memory disable (POWER8) */
 };
 
+/* HMER/HMEER */
+enum {
+    HMER_MALFUNCTION_ALERT      = 1ull << (63-0),
+    HMER_PROC_RECV_DONE         = 1ull << (63-2),
+    HMER_PROC_RECV_ERROR_MASKED = 1ull << (63-3),
+    HMER_TFAC_ERROR             = 1ull << (63-4),
+    HMER_TFMR_PARITY_ERROR      = 1ull << (63-5),
+    HMER_XSCOM_FAIL             = 1ull << (63-8),
+    HMER_XSCOM_DONE             = 1ull << (63-9),
+    HMER_PROC_RECV_AGAIN        = 1ull << (63-11),
+    HMER_WARN_RISE              = 1ull << (63-14),
+    HMER_WARN_FALL              = 1ull << (63-15),
+    HMER_SCOM_FIR_HMI           = 1ull << (63-16),
+    HMER_TRIG_FIR_HMI           = 1ull << (63-17),
+    HMER_HYP_RESOURCE_ERR       = 1ull << (63-20),
+    HMER_XSCOM_STATUS_MASK      = 7ull << (63-23),
+    HMER_XSCOM_STATUS_LSH       = (63-23),
+};
+
 /*****************************************************************************/
 
 static inline target_ulong cpu_read_xer(CPUPPCState *env)
Signature · 1 line
2.5.0
#19 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 05/77] ppc: Update SPR definitions

Quoted 105 lines · Benjamin Herrenschmidt

Add definitions for additional SPR numbers and SPR bit definitions that will be relevant for subsequent improvements to POWER8 emulation

Also fix the definition of LPIDR which was incorrect (and is different for server and embedded).

---
 target-ppc/cpu.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 47 insertions(+), 7 deletions(-)
target-ppc/cpu.h +28 -7
index e6c43f9..611367f 100644
@@ -470,9 +470,17 @@ struct ppc_slb_t {
 #define MSR_RI   1  /* Recoverable interrupt                        1        */
 #define MSR_LE   0  /* Little-endian mode                           1 hflags */
 
-#define LPCR_ILE (1 << (63-38))
-#define LPCR_AIL_SHIFT (63-40)      /* Alternate interrupt location */
-#define LPCR_AIL (3 << LPCR_AIL_SHIFT)
+/* LPCR bits */
+#define LPCR_VPM0         (1ull << (63-0))
+#define LPCR_VPM1         (1ull << (63-1))
+#define LPCR_ISL          (1ull << (63-2))
+#define LPCR_KBV          (1ull << (63-3))
+#define LPCR_ILE          (1ull << (63-38))
+#define LPCR_MER          (1ull << (63-52))
+#define LPCR_LPES0        (1ull << (63-60))
+#define LPCR_LPES1        (1ull << (63-61))
+#define LPCR_AIL_SHIFT    (63-40)      /* Alternate interrupt location */
+#define LPCR_AIL          (3ull << LPCR_AIL_SHIFT)
 
 #define msr_sf   ((env->msr >> MSR_SF)   & 1)
 #define msr_isf  ((env->msr >> MSR_ISF)  & 1)
@@ -1338,6 +1346,10 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_UAMOR             (0x09D)
 #define SPR_MPC_ICTRL         (0x09E)
 #define SPR_MPC_BAR           (0x09F)
+#define SPR_DAWR              (0x0B4)
+#define SPR_RPR               (0x0BA)
+#define SPR_DAWRX             (0x0BC)
+#define SPR_HFSCR             (0x0BE)
 #define SPR_VRSAVE            (0x100)
 #define SPR_USPRG0            (0x100)
 #define SPR_USPRG1            (0x101)
@@ -1392,19 +1404,25 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_HSRR1             (0x13B)
 #define SPR_BOOKE_IAC4        (0x13B)
 #define SPR_BOOKE_DAC1        (0x13C)
-#define SPR_LPIDR             (0x13D)
+#define SPR_MMCRH             (0x13C)
 #define SPR_DABR2             (0x13D)
 #define SPR_BOOKE_DAC2        (0x13D)
+#define SPR_TFMR              (0x13D)
 #define SPR_BOOKE_DVC1        (0x13E)
 #define SPR_LPCR              (0x13E)
 #define SPR_BOOKE_DVC2        (0x13F)
+#define SPR_LPIDR             (0x13F)
 #define SPR_BOOKE_TSR         (0x150)
+#define SPR_HMER              (0x150)
+#define SPR_HMEER             (0x151)
 #define SPR_PCR               (0x152)
+#define SPR_BOOKE_LPIDR       (0x152)
 #define SPR_BOOKE_TCR         (0x154)
 #define SPR_BOOKE_TLB0PS      (0x158)
 #define SPR_BOOKE_TLB1PS      (0x159)
 #define SPR_BOOKE_TLB2PS      (0x15A)
 #define SPR_BOOKE_TLB3PS      (0x15B)
+#define SPR_AMOR	      (0x15D)
 #define SPR_BOOKE_MAS7_MAS3   (0x174)
 #define SPR_BOOKE_IVOR0       (0x190)
 #define SPR_BOOKE_IVOR1       (0x191)
@@ -1622,6 +1640,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_RCPU_L2U_RA3      (0x32B)
 #define SPR_TAR               (0x32F)
 #define SPR_VTB               (0x351)
+#define SPR_MMCRC             (0x353)
 #define SPR_440_INV0          (0x370)
 #define SPR_440_INV1          (0x371)
 #define SPR_440_INV2          (0x372)
@@ -1655,6 +1674,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define SPR_440_DVLIM         (0x398)
 #define SPR_750_WPAR          (0x399)
 #define SPR_440_IVLIM         (0x399)
+#define SPR_TSCR	      (0x399)
 #define SPR_750_DMAU          (0x39A)
 #define SPR_750_DMAL          (0x39B)
 #define SPR_440_RSTCFG        (0x39B)
@@ -1829,9 +1849,10 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch)
 #define   L1CSR1_ICE		0x00000001	/* Instruction Cache Enable */
 
 /* HID0 bits */
-#define HID0_DEEPNAP        (1 << 24)
-#define HID0_DOZE           (1 << 23)
-#define HID0_NAP            (1 << 22)
+#define HID0_DEEPNAP        (1 << 24)	        /* pre-2.06 */
+#define HID0_DOZE           (1 << 23)	        /* pre-2.06 */
+#define HID0_NAP            (1 << 22)	        /* pre-2.06 */
+#define HID0_HILE           (1ull << (63-19))   /* POWER8 */
 
 /*****************************************************************************/
 /* PowerPC Instructions types definitions                                    */

target-ppc/cpu.h enum { · line 2205

Quoted 5 lines
target-ppc/cpu.h +2 -0
@@ -2180,6 +2201,25 @@ enum {
     PCR_TM_DIS          = 1ull << (63-2), /* Trans. memory disable (POWER8) */
 };
 
+/* HMER/HMEER */
+enum {

Any particular reason for using an enum rather than defines like the rest?

Quoted 5 lines
target-ppc/cpu.h +5 -0
+    HMER_MALFUNCTION_ALERT      = 1ull << (63-0),
+    HMER_PROC_RECV_DONE         = 1ull << (63-2),
+    HMER_PROC_RECV_ERROR_MASKED = 1ull << (63-3),
+    HMER_TFAC_ERROR             = 1ull << (63-4),
+    HMER_TFMR_PARITY_ERROR      = 1ull << (63-5),

target-ppc/cpu.h enum { · line 2220

Quoted 10 lines
target-ppc/cpu.h +10 -0
+    HMER_XSCOM_FAIL             = 1ull << (63-8),
+    HMER_XSCOM_DONE             = 1ull << (63-9),
+    HMER_PROC_RECV_AGAIN        = 1ull << (63-11),
+    HMER_WARN_RISE              = 1ull << (63-14),
+    HMER_WARN_FALL              = 1ull << (63-15),
+    HMER_SCOM_FIR_HMI           = 1ull << (63-16),
+    HMER_TRIG_FIR_HMI           = 1ull << (63-17),
+    HMER_HYP_RESOURCE_ERR       = 1ull << (63-20),
+    HMER_XSCOM_STATUS_MASK      = 7ull << (63-23),
+    HMER_XSCOM_STATUS_LSH       = (63-23),

Is that last one correct?

Quoted 5 lines
target-ppc/cpu.h +2 -0
+};
+
 /*****************************************************************************/
 
 static inline target_ulong cpu_read_xer(CPUPPCState *env)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#20 lore raw

PATCH 6/77 ppc: Add macros to register hypervisor mode SPRs

In reply to Benjamin Herrenschmidt

The current set of spr_register_* macros only take the user and supervisor function pointers. To make the transition easy, we don't change that but we add "_hv" variants that can be used to register all 3 sets.

To simplify the transition, users of the "old" macro will set the hypervisor callback to be the same as the supervisor one. The new registration function only needs to be used for registers that are either hypervisor only or behave differently in HV mode.

---
 target-ppc/translate.c      | 26 ++++++++++++++++----------
 target-ppc/translate_init.c | 35 +++++++++++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 14 deletions(-)
target-ppc/translate.c +16 -10
index e18d204..a2fe1b5 100644
@@ -4299,14 +4299,17 @@ static inline void gen_op_mfspr(DisasContext *ctx)
     void (*read_cb)(DisasContext *ctx, int gprn, int sprn);
     uint32_t sprn = SPR(ctx->opcode);
 
-#if !defined(CONFIG_USER_ONLY)
-    if (ctx->hv)
+#if defined(CONFIG_USER_ONLY)
+    read_cb = ctx->spr_cb[sprn].uea_read;
+#else
+    if (ctx->pr) {
+        read_cb = ctx->spr_cb[sprn].uea_read;
+    } else if (ctx->hv) {
         read_cb = ctx->spr_cb[sprn].hea_read;
-    else if (!ctx->pr)
+    } else if (!ctx->pr) {
         read_cb = ctx->spr_cb[sprn].oea_read;
-    else
+    }
 #endif
-        read_cb = ctx->spr_cb[sprn].uea_read;
     if (likely(read_cb != NULL)) {
         if (likely(read_cb != SPR_NOACCESS)) {
             (*read_cb)(ctx, rD(ctx->opcode), sprn);
@@ -4450,14 +4453,17 @@ static void gen_mtspr(DisasContext *ctx)
     void (*write_cb)(DisasContext *ctx, int sprn, int gprn);
     uint32_t sprn = SPR(ctx->opcode);
 
-#if !defined(CONFIG_USER_ONLY)
-    if (ctx->hv)
+#if defined(CONFIG_USER_ONLY)
+    write_cb = ctx->spr_cb[sprn].uea_write;
+#else
+    if (ctx->pr) {
+        write_cb = ctx->spr_cb[sprn].uea_write;
+    } else if (ctx->hv) {
         write_cb = ctx->spr_cb[sprn].hea_write;
-    else if (!ctx->pr)
+    } else {
         write_cb = ctx->spr_cb[sprn].oea_write;
-    else
+    }
 #endif
-        write_cb = ctx->spr_cb[sprn].uea_write;
     if (likely(write_cb != NULL)) {
         if (likely(write_cb != SPR_NOACCESS)) {
             (*write_cb)(ctx, sprn, rS(ctx->opcode));
target-ppc/translate_init.c +31 -4
index e88dc7f..30a03ce 100644
@@ -578,17 +578,33 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val)
 #define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
                          oea_read, oea_write, one_reg_id, initial_value)       \
     _spr_register(env, num, name, uea_read, uea_write, initial_value)
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write,               \
+                            oea_read, oea_write, hea_read, hea_write,          \
+                            one_reg_id, initial_value)                         \
+    _spr_register(env, num, name, uea_read, uea_write, initial_value)
 #else
 #if !defined(CONFIG_KVM)
 #define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
-                         oea_read, oea_write, one_reg_id, initial_value) \
+                         oea_read, oea_write, one_reg_id, initial_value)       \
+    _spr_register(env, num, name, uea_read, uea_write,                         \
+                  oea_read, oea_write, oea_read, oea_write, initial_value)
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write,               \
+                            oea_read, oea_write, hea_read, hea_write,          \
+                            one_reg_id, initial_value)                         \
     _spr_register(env, num, name, uea_read, uea_write,                         \
-                  oea_read, oea_write, initial_value)
+                  oea_read, oea_write, hea_read, hea_write, initial_value)
 #else
 #define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
-                         oea_read, oea_write, one_reg_id, initial_value) \
+                         oea_read, oea_write, one_reg_id, initial_value)       \
+    _spr_register(env, num, name, uea_read, uea_write,                         \
+                  oea_read, oea_write, oea_read, oea_write,                    \
+                  one_reg_id, initial_value)
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write,               \
+                            oea_read, oea_write, hea_read, hea_write,          \
+                            one_reg_id, initial_value)                         \
     _spr_register(env, num, name, uea_read, uea_write,                         \
-                  oea_read, oea_write, one_reg_id, initial_value)
+                  oea_read, oea_write, hea_read, hea_write,                    \
+                  one_reg_id, initial_value)
 #endif
 #endif
 
@@ -597,6 +613,13 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val)
     spr_register_kvm(env, num, name, uea_read, uea_write,                      \
                      oea_read, oea_write, 0, initial_value)
 
+#define spr_register_hv(env, num, name, uea_read, uea_write,                   \
+                        oea_read, oea_write, hea_read, hea_write,              \
+                        initial_value)                                         \
+    spr_register_kvm_hv(env, num, name, uea_read, uea_write,                   \
+                        oea_read, oea_write, hea_read, hea_write,              \
+                        0, initial_value)
+
 static inline void _spr_register(CPUPPCState *env, int num,
                                  const char *name,
                                  void (*uea_read)(DisasContext *ctx, int gprn, int sprn),
@@ -605,6 +628,8 @@ static inline void _spr_register(CPUPPCState *env, int num,
 
                                  void (*oea_read)(DisasContext *ctx, int gprn, int sprn),
                                  void (*oea_write)(DisasContext *ctx, int sprn, int gprn),
+                                 void (*hea_read)(DisasContext *opaque, int gprn, int sprn),
+                                 void (*hea_write)(DisasContext *opaque, int sprn, int gprn),
 #endif
 #if defined(CONFIG_KVM)
                                  uint64_t one_reg_id,
@@ -632,6 +657,8 @@ static inline void _spr_register(CPUPPCState *env, int num,
 #if !defined(CONFIG_USER_ONLY)
     spr->oea_read = oea_read;
     spr->oea_write = oea_write;
+    spr->hea_read = hea_read;
+    spr->hea_write = hea_write;
 #endif
 #if defined(CONFIG_KVM)
     spr->one_reg_id = one_reg_id,
Signature · 1 line
2.5.0
Reviewed-by on v1 6/77
#21 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 06/77] ppc: Add macros to register hypervisor mode SPRs

Quoted 11 lines · Benjamin Herrenschmidt

The current set of spr_register_* macros only take the user and supervisor function pointers. To make the transition easy, we don't change that but we add "_hv" variants that can be used to register all 3 sets.

To simplify the transition, users of the "old" macro will set the hypervisor callback to be the same as the supervisor one. The new registration function only needs to be used for registers that are either hypervisor only or behave differently in HV mode.

Quoted 129 lines
---
 target-ppc/translate.c      | 26 ++++++++++++++++----------
 target-ppc/translate_init.c | 35 +++++++++++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 14 deletions(-)
target-ppc/translate.c +16 -10
index e18d204..a2fe1b5 100644
@@ -4299,14 +4299,17 @@ static inline void gen_op_mfspr(DisasContext *ctx)
     void (*read_cb)(DisasContext *ctx, int gprn, int sprn);
     uint32_t sprn = SPR(ctx->opcode);
 
-#if !defined(CONFIG_USER_ONLY)
-    if (ctx->hv)
+#if defined(CONFIG_USER_ONLY)
+    read_cb = ctx->spr_cb[sprn].uea_read;
+#else
+    if (ctx->pr) {
+        read_cb = ctx->spr_cb[sprn].uea_read;
+    } else if (ctx->hv) {
         read_cb = ctx->spr_cb[sprn].hea_read;
-    else if (!ctx->pr)
+    } else if (!ctx->pr) {
         read_cb = ctx->spr_cb[sprn].oea_read;
-    else
+    }
 #endif
-        read_cb = ctx->spr_cb[sprn].uea_read;
     if (likely(read_cb != NULL)) {
         if (likely(read_cb != SPR_NOACCESS)) {
             (*read_cb)(ctx, rD(ctx->opcode), sprn);
@@ -4450,14 +4453,17 @@ static void gen_mtspr(DisasContext *ctx)
     void (*write_cb)(DisasContext *ctx, int sprn, int gprn);
     uint32_t sprn = SPR(ctx->opcode);
 
-#if !defined(CONFIG_USER_ONLY)
-    if (ctx->hv)
+#if defined(CONFIG_USER_ONLY)
+    write_cb = ctx->spr_cb[sprn].uea_write;
+#else
+    if (ctx->pr) {
+        write_cb = ctx->spr_cb[sprn].uea_write;
+    } else if (ctx->hv) {
         write_cb = ctx->spr_cb[sprn].hea_write;
-    else if (!ctx->pr)
+    } else {
         write_cb = ctx->spr_cb[sprn].oea_write;
-    else
+    }
 #endif
-        write_cb = ctx->spr_cb[sprn].uea_write;
     if (likely(write_cb != NULL)) {
         if (likely(write_cb != SPR_NOACCESS)) {
             (*write_cb)(ctx, sprn, rS(ctx->opcode));
target-ppc/translate_init.c +31 -4
index e88dc7f..30a03ce 100644
@@ -578,17 +578,33 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val)
 #define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
                          oea_read, oea_write, one_reg_id, initial_value)       \
     _spr_register(env, num, name, uea_read, uea_write, initial_value)
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write,               \
+                            oea_read, oea_write, hea_read, hea_write,          \
+                            one_reg_id, initial_value)                         \
+    _spr_register(env, num, name, uea_read, uea_write, initial_value)
 #else
 #if !defined(CONFIG_KVM)
 #define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
-                         oea_read, oea_write, one_reg_id, initial_value) \
+                         oea_read, oea_write, one_reg_id, initial_value)       \
+    _spr_register(env, num, name, uea_read, uea_write,                         \
+                  oea_read, oea_write, oea_read, oea_write, initial_value)
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write,               \
+                            oea_read, oea_write, hea_read, hea_write,          \
+                            one_reg_id, initial_value)                         \
     _spr_register(env, num, name, uea_read, uea_write,                         \
-                  oea_read, oea_write, initial_value)
+                  oea_read, oea_write, hea_read, hea_write, initial_value)
 #else
 #define spr_register_kvm(env, num, name, uea_read, uea_write,                  \
-                         oea_read, oea_write, one_reg_id, initial_value) \
+                         oea_read, oea_write, one_reg_id, initial_value)       \
+    _spr_register(env, num, name, uea_read, uea_write,                         \
+                  oea_read, oea_write, oea_read, oea_write,                    \
+                  one_reg_id, initial_value)
+#define spr_register_kvm_hv(env, num, name, uea_read, uea_write,               \
+                            oea_read, oea_write, hea_read, hea_write,          \
+                            one_reg_id, initial_value)                         \
     _spr_register(env, num, name, uea_read, uea_write,                         \
-                  oea_read, oea_write, one_reg_id, initial_value)
+                  oea_read, oea_write, hea_read, hea_write,                    \
+                  one_reg_id, initial_value)
 #endif
 #endif
 
@@ -597,6 +613,13 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val)
     spr_register_kvm(env, num, name, uea_read, uea_write,                      \
                      oea_read, oea_write, 0, initial_value)
 
+#define spr_register_hv(env, num, name, uea_read, uea_write,                   \
+                        oea_read, oea_write, hea_read, hea_write,              \
+                        initial_value)                                         \
+    spr_register_kvm_hv(env, num, name, uea_read, uea_write,                   \
+                        oea_read, oea_write, hea_read, hea_write,              \
+                        0, initial_value)
+
 static inline void _spr_register(CPUPPCState *env, int num,
                                  const char *name,
                                  void (*uea_read)(DisasContext *ctx, int gprn, int sprn),
@@ -605,6 +628,8 @@ static inline void _spr_register(CPUPPCState *env, int num,
 
                                  void (*oea_read)(DisasContext *ctx, int gprn, int sprn),
                                  void (*oea_write)(DisasContext *ctx, int sprn, int gprn),
+                                 void (*hea_read)(DisasContext *opaque, int gprn, int sprn),
+                                 void (*hea_write)(DisasContext *opaque, int sprn, int gprn),
 #endif
 #if defined(CONFIG_KVM)
                                  uint64_t one_reg_id,
@@ -632,6 +657,8 @@ static inline void _spr_register(CPUPPCState *env, int num,
 #if !defined(CONFIG_USER_ONLY)
     spr->oea_read = oea_read;
     spr->oea_write = oea_write;
+    spr->hea_read = hea_read;
+    spr->hea_write = hea_write;
 #endif
 #if defined(CONFIG_KVM)
     spr->one_reg_id = one_reg_id,
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#22 lore raw

PATCH 7/77 ppc: Add a bunch of hypervisor SPRs to Book3s

In reply to Benjamin Herrenschmidt

We don't give them a KVM reg number yet as no current KVM version supports HV mode.

---
 target-ppc/translate_init.c | 140 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 3 deletions(-)
target-ppc/translate_init.c +137 -3
index 30a03ce..c743eb1 100644
@@ -1104,6 +1104,11 @@ static void gen_spr_amr (CPUPPCState *env)
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, &spr_write_generic,
                      KVM_REG_PPC_UAMOR, 0);
+    spr_register_hv(env, SPR_AMOR, "AMOR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0);
 #endif /* !CONFIG_USER_ONLY */
 }
 #endif /* TARGET_PPC64 */
@@ -7490,6 +7495,20 @@ static void gen_spr_book3s_dbg(CPUPPCState *env)
                      KVM_REG_PPC_DABRX, 0x00000000);
 }
 
+static void gen_spr_book3s_207_dbg(CPUPPCState *env)
+{
+    spr_register_hv(env, SPR_DAWR, "DAWR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000000);
+    spr_register_hv(env, SPR_DAWRX, "DAWRX",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000000);
+}
+
 static void gen_spr_970_dbg(CPUPPCState *env)
 {
     /* Breakpoints */
@@ -7654,15 +7673,116 @@ static void gen_spr_power5p_lpar(CPUPPCState *env)
     spr_register_kvm(env, SPR_LPCR, "LPCR",
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, &spr_write_generic,
-                     KVM_REG_PPC_LPCR, 0x00000000);
+                     KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1);
 }
 
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn)
+{
+    TCGv hmer = tcg_temp_new();
+
+    gen_load_spr(hmer, sprn);
+    tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer);
+    gen_store_spr(sprn, hmer);
+    spr_store_dump_spr(sprn);
+    tcg_temp_free(hmer);
+}
+#endif
+
 static void gen_spr_book3s_ids(CPUPPCState *env)
 {
+    /* FIXME: Will need to deal with thread vs core only SPRs */
+
     /* Processor identification */
-    spr_register(env, SPR_PIR, "PIR",
+    spr_register_hv(env, SPR_PIR, "PIR",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_pir,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, NULL,
+                 0x00000000);
+    spr_register_hv(env, SPR_HID0, "HID0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_TSCR, "TSCR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HMER, "HMER",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_hmer,
+                 0x00000000);
+    spr_register_hv(env, SPR_HMEER, "HMEER",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_TFMR, "TFMR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_LPIDR, "LPIDR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HFSCR, "HFSCR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_MMCRC, "MMCRC",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_MMCRH, "MMCRH",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSPRG0, "HSPRG0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSPRG1, "HSPRG1",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSRR0, "HSRR0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSRR1, "HSRR1",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HDAR, "HDAR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HDSISR, "HDSISR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_RMOR, "RMOR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HRMOR, "HRMOR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
                  0x00000000);
 }
 
@@ -7868,6 +7988,17 @@ static void gen_spr_power8_fscr(CPUPPCState *env)
                      KVM_REG_PPC_FSCR, initval);
 }
 
+static void gen_spr_power8_rpr(CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+    spr_register_hv(env, SPR_RPR, "RPR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000103070F1F3F);
+#endif
+}
+
 static void init_proc_book3s_64(CPUPPCState *env, int version)
 {
     gen_spr_ne_601(env);
@@ -7919,9 +8050,12 @@ static void init_proc_book3s_64(CPUPPCState *env, int version)
         gen_spr_power8_pmu_user(env);
         gen_spr_power8_tm(env);
         gen_spr_vtb(env);
+        gen_spr_power8_rpr(env);
     }
     if (version < BOOK3S_CPU_POWER8) {
         gen_spr_book3s_dbg(env);
+    } else {
+        gen_spr_book3s_207_dbg(env);
     }
 #if !defined(CONFIG_USER_ONLY)
     switch (version) {
Signature · 1 line
2.5.0
#23 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 07/77] ppc: Add a bunch of hypervisor SPRs to Book3s

Quoted 4 lines · Benjamin Herrenschmidt

We don't give them a KVM reg number yet as no current KVM version supports HV mode.

Looks sane, though I haven't explicitly checked each of the implementations against the architecture.

Quoted 192 lines
---
 target-ppc/translate_init.c | 140 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 3 deletions(-)
target-ppc/translate_init.c +137 -3
index 30a03ce..c743eb1 100644
@@ -1104,6 +1104,11 @@ static void gen_spr_amr (CPUPPCState *env)
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, &spr_write_generic,
                      KVM_REG_PPC_UAMOR, 0);
+    spr_register_hv(env, SPR_AMOR, "AMOR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0);
 #endif /* !CONFIG_USER_ONLY */
 }
 #endif /* TARGET_PPC64 */
@@ -7490,6 +7495,20 @@ static void gen_spr_book3s_dbg(CPUPPCState *env)
                      KVM_REG_PPC_DABRX, 0x00000000);
 }
 
+static void gen_spr_book3s_207_dbg(CPUPPCState *env)
+{
+    spr_register_hv(env, SPR_DAWR, "DAWR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000000);
+    spr_register_hv(env, SPR_DAWRX, "DAWRX",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000000);
+}
+
 static void gen_spr_970_dbg(CPUPPCState *env)
 {
     /* Breakpoints */
@@ -7654,15 +7673,116 @@ static void gen_spr_power5p_lpar(CPUPPCState *env)
     spr_register_kvm(env, SPR_LPCR, "LPCR",
                      SPR_NOACCESS, SPR_NOACCESS,
                      &spr_read_generic, &spr_write_generic,
-                     KVM_REG_PPC_LPCR, 0x00000000);
+                     KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1);
 }
 
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn)
+{
+    TCGv hmer = tcg_temp_new();
+
+    gen_load_spr(hmer, sprn);
+    tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer);
+    gen_store_spr(sprn, hmer);
+    spr_store_dump_spr(sprn);
+    tcg_temp_free(hmer);
+}
+#endif
+
 static void gen_spr_book3s_ids(CPUPPCState *env)
 {
+    /* FIXME: Will need to deal with thread vs core only SPRs */
+
     /* Processor identification */
-    spr_register(env, SPR_PIR, "PIR",
+    spr_register_hv(env, SPR_PIR, "PIR",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_pir,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, NULL,
+                 0x00000000);
+    spr_register_hv(env, SPR_HID0, "HID0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_TSCR, "TSCR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HMER, "HMER",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_hmer,
+                 0x00000000);
+    spr_register_hv(env, SPR_HMEER, "HMEER",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_TFMR, "TFMR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_LPIDR, "LPIDR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HFSCR, "HFSCR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_MMCRC, "MMCRC",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_MMCRH, "MMCRH",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSPRG0, "HSPRG0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSPRG1, "HSPRG1",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSRR0, "HSRR0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HSRR1, "HSRR1",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HDAR, "HDAR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HDSISR, "HDSISR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_RMOR, "RMOR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
+                 0x00000000);
+    spr_register_hv(env, SPR_HRMOR, "HRMOR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic,
                  0x00000000);
 }
 
@@ -7868,6 +7988,17 @@ static void gen_spr_power8_fscr(CPUPPCState *env)
                      KVM_REG_PPC_FSCR, initval);
 }
 
+static void gen_spr_power8_rpr(CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+    spr_register_hv(env, SPR_RPR, "RPR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000103070F1F3F);
+#endif
+}
+
 static void init_proc_book3s_64(CPUPPCState *env, int version)
 {
     gen_spr_ne_601(env);
@@ -7919,9 +8050,12 @@ static void init_proc_book3s_64(CPUPPCState *env, int version)
         gen_spr_power8_pmu_user(env);
         gen_spr_power8_tm(env);
         gen_spr_vtb(env);
+        gen_spr_power8_rpr(env);
     }
     if (version < BOOK3S_CPU_POWER8) {
         gen_spr_book3s_dbg(env);
+    } else {
+        gen_spr_book3s_207_dbg(env);
     }
 #if !defined(CONFIG_USER_ONLY)
     switch (version) {
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#24 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 07/77] ppc: Add a bunch of hypervisor SPRs to Book3s

Quoted 9 lines · David Gibson
Quoted 4 lines · Benjamin Herrenschmidt

We don't give them a KVM reg number yet as no current KVM version supports HV mode.

Looks sane, though I haven't explicitly checked each of the implementations against the architecture.

Almost all of those are just "placeholders". IE, we just read and write whatever is passed into the SPR array. What might be interesting is to what extend we actually use the values later on, which happens by bits and pieces in separate patches, or in many cases, not at all.

I think it might be worth adding to the SPR array a bitmask of the implemented bits though, so that we force the unimplemented ones to 0 rather than storing them, and possibly warn on attempts to write 1 to an unimplemented one.

Something to put on the TODO list...

Cheers,
Ben.

Quoted 199 lines
Quoted 198 lines

---
 target-ppc/translate_init.c | 140
+++++++++++++++++++++++++++++++++++++++++++-

 1 file changed, 137 insertions(+), 3 deletions(-)
target- renamed from target-ppc/translate_init.c +0 -0

ppc/translate_init.c
index 30a03ce..c743eb1 100644

target-ppc/translate_init.c +5 -0
@@ -1104,6 +1104,11 @@ static void gen_spr_amr (CPUPPCState *env)
                       SPR_NOACCESS, SPR_NOACCESS,
                       &spr_read_generic, &spr_write_generic,
                       KVM_REG_PPC_UAMOR, 0);
+    spr_register_hv(env, SPR_AMOR, "AMOR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0);
  #endif /* !CONFIG_USER_ONLY */
  }
  #endif /* TARGET_PPC64 */
@@ -7490,6 +7495,20 @@ static void gen_spr_book3s_dbg(CPUPPCState
 *env)
                       KVM_REG_PPC_DABRX, 0x00000000);
  }

target-ppc/translate_init.c +14 -0
+static void gen_spr_book3s_207_dbg(CPUPPCState *env)
+{
+    spr_register_hv(env, SPR_DAWR, "DAWR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000000);
+    spr_register_hv(env, SPR_DAWRX, "DAWRX",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000000);
+}
+

 static void gen_spr_970_dbg(CPUPPCState *env)
 {
     /* Breakpoints */

target-ppc/translate_init.c +1 -1
@@ -7654,15 +7673,116 @@ static void
 gen_spr_power5p_lpar(CPUPPCState *env)
      spr_register_kvm(env, SPR_LPCR, "LPCR",
                       SPR_NOACCESS, SPR_NOACCESS,
                       &spr_read_generic, &spr_write_generic,
-                     KVM_REG_PPC_LPCR, 0x00000000);
+                     KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1);
  }

target-ppc/translate_init.c +13 -0
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn)
+{
+    TCGv hmer = tcg_temp_new();
+
+    gen_load_spr(hmer, sprn);
+    tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer);
+    gen_store_spr(sprn, hmer);
+    spr_store_dump_spr(sprn);
+    tcg_temp_free(hmer);
+}
+#endif
+

 static void gen_spr_book3s_ids(CPUPPCState *env)
 {
+    /* FIXME: Will need to deal with thread vs core only SPRs */ +
     /* Processor identification */
-    spr_register(env, SPR_PIR, "PIR",
+    spr_register_hv(env, SPR_PIR, "PIR",
                  SPR_NOACCESS, SPR_NOACCESS,
-                 &spr_read_generic, &spr_write_pir,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, NULL,
+                 0x00000000);
+    spr_register_hv(env, SPR_HID0, "HID0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_TSCR, "TSCR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HMER, "HMER",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_hmer,
+                 0x00000000);
+    spr_register_hv(env, SPR_HMEER, "HMEER",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_TFMR, "TFMR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_LPIDR, "LPIDR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HFSCR, "HFSCR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_MMCRC, "MMCRC",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_MMCRH, "MMCRH",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HSPRG0, "HSPRG0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HSPRG1, "HSPRG1",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HSRR0, "HSRR0",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HSRR1, "HSRR1",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HDAR, "HDAR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HDSISR, "HDSISR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_RMOR, "RMOR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, +                 0x00000000);
+    spr_register_hv(env, SPR_HRMOR, "HRMOR",
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 SPR_NOACCESS, SPR_NOACCESS,
+                 &spr_read_generic, &spr_write_generic, 0x00000000);
 }

target-ppc/translate_init.c +0 -0
@@ -7868,6 +7988,17 @@ static void gen_spr_power8_fscr(CPUPPCState
 *env)
                       KVM_REG_PPC_FSCR, initval);
  }

target-ppc/translate_init.c +11 -0
+static void gen_spr_power8_rpr(CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+    spr_register_hv(env, SPR_RPR, "RPR",
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    SPR_NOACCESS, SPR_NOACCESS,
+                    &spr_read_generic, &spr_write_generic,
+                    0x00000103070F1F3F);
+#endif
+}
+

 static void init_proc_book3s_64(CPUPPCState *env, int version) {
     gen_spr_ne_601(env);

target-ppc/translate_init.c +3 -0
@@ -7919,9 +8050,12 @@ static void init_proc_book3s_64(CPUPPCState
 *env, int version)
          gen_spr_power8_pmu_user(env);
          gen_spr_power8_tm(env);
          gen_spr_vtb(env);
+        gen_spr_power8_rpr(env);
      }
      if (version < BOOK3S_CPU_POWER8) {
          gen_spr_book3s_dbg(env);
+    } else {
+        gen_spr_book3s_207_dbg(env);
      }
  #if !defined(CONFIG_USER_ONLY)

     switch (version) {

#25 lore raw

PATCH 8/77 ppc: Add number of threads per core to the processor definition

In reply to Benjamin Herrenschmidt

Also use it to clamp the max SMT mode and ensure that the cpu_dt_id are offset by that value in order to preserve consistency with the HW implementations.

---
 target-ppc/cpu-qom.h        | 1 +
 target-ppc/translate_init.c | 8 +++++++-
 2 files changed, 8 insertions(+), 1 deletion(-)
target-ppc/cpu-qom.h +1 -0
index 6967a80..fef23fd 100644
@@ -68,6 +68,7 @@ typedef struct PowerPCCPUClass {
     uint32_t flags;
     int bfd_mach;
     uint32_t l1_dcache_size, l1_icache_size;
+    uint32_t threads_per_core;
 #if defined(TARGET_PPC64)
     const struct ppc_segment_page_sizes *sps;
 #endif
target-ppc/translate_init.c +7 -1
index c743eb1..1d402e1 100644
@@ -8193,6 +8193,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
                  POWERPC_FLAG_BUS_CLK;
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x10000;
+    pcc->threads_per_core = 2;
 }
 
 static void powerpc_get_compat(Object *obj, Visitor *v,
@@ -8339,6 +8340,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x8000;
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
+    pcc->threads_per_core = 4;
 }
 
 static void init_proc_POWER8(CPUPPCState *env)
@@ -8419,6 +8421,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x8000;
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
+    pcc->threads_per_core = 8;
 }
 #endif /* defined (TARGET_PPC64) */
 
@@ -9074,6 +9077,9 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
 #endif
 
 #if !defined(CONFIG_USER_ONLY)
+    if (max_smt > pcc->threads_per_core) {
+        max_smt = pcc->threads_per_core;
+    }
     if (smp_threads > max_smt) {
         error_setg(errp, "Cannot support more than %d threads on PPC with %s",
                    max_smt, kvm_enabled() ? "KVM" : "TCG");
@@ -9094,7 +9100,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 
 #if !defined(CONFIG_USER_ONLY)
-    cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt
+    cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * pcc->threads_per_core
         + (cs->cpu_index % smp_threads);
 #endif
 
Signature · 1 line
2.5.0
Reviewed-by on v1 8/77
#26 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 08/77] ppc: Add number of threads per core to the processor definition

Quoted 5 lines · Benjamin Herrenschmidt

Also use it to clamp the max SMT mode and ensure that the cpu_dt_id are offset by that value in order to preserve consistency with the HW implementations.

Quoted 64 lines
---
 target-ppc/cpu-qom.h        | 1 +
 target-ppc/translate_init.c | 8 +++++++-
 2 files changed, 8 insertions(+), 1 deletion(-)
target-ppc/cpu-qom.h +1 -0
index 6967a80..fef23fd 100644
@@ -68,6 +68,7 @@ typedef struct PowerPCCPUClass {
     uint32_t flags;
     int bfd_mach;
     uint32_t l1_dcache_size, l1_icache_size;
+    uint32_t threads_per_core;
 #if defined(TARGET_PPC64)
     const struct ppc_segment_page_sizes *sps;
 #endif
target-ppc/translate_init.c +7 -1
index c743eb1..1d402e1 100644
@@ -8193,6 +8193,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
                  POWERPC_FLAG_BUS_CLK;
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x10000;
+    pcc->threads_per_core = 2;
 }
 
 static void powerpc_get_compat(Object *obj, Visitor *v,
@@ -8339,6 +8340,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x8000;
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
+    pcc->threads_per_core = 4;
 }
 
 static void init_proc_POWER8(CPUPPCState *env)
@@ -8419,6 +8421,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x8000;
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
+    pcc->threads_per_core = 8;
 }
 #endif /* defined (TARGET_PPC64) */
 
@@ -9074,6 +9077,9 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
 #endif
 
 #if !defined(CONFIG_USER_ONLY)
+    if (max_smt > pcc->threads_per_core) {
+        max_smt = pcc->threads_per_core;
+    }
     if (smp_threads > max_smt) {
         error_setg(errp, "Cannot support more than %d threads on PPC with %s",
                    max_smt, kvm_enabled() ? "KVM" : "TCG");
@@ -9094,7 +9100,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
     }
 
 #if !defined(CONFIG_USER_ONLY)
-    cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt
+    cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * pcc->threads_per_core
         + (cs->cpu_index % smp_threads);
 #endif
 
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#27 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 08/77] ppc: Add number of threads per core to the processor definition

Quoted 9 lines · David Gibson
Quoted 5 lines · Benjamin Herrenschmidt

Also use it to clamp the max SMT mode and ensure that the cpu_dt_id are offset by that value in order to preserve consistency with the HW implementations.

In fact this is broken ;-) All the CPUs without thread get 0 in the new field which trips at init time since we can create 0 threads :-) It broke for example 32-bit stuff. I've fixed that in github.

Quoted 75 lines
Quoted 74 lines

---
 target-ppc/cpu-qom.h        | 1 +
 target-ppc/translate_init.c | 8 +++++++-

 2 files changed, 8 insertions(+), 1 deletion(-)
target-ppc/cpu-qom.h +1 -0
index 6967a80..fef23fd 100644
@@ -68,6 +68,7 @@ typedef struct PowerPCCPUClass {
      uint32_t flags;
      int bfd_mach;
      uint32_t l1_dcache_size, l1_icache_size;
+    uint32_t threads_per_core;
  #if defined(TARGET_PPC64)
      const struct ppc_segment_page_sizes *sps;
  #endif
target- renamed from target-ppc/translate_init.c +0 -0

ppc/translate_init.c
index c743eb1..1d402e1 100644

target-ppc/translate_init.c +1 -0
@@ -8193,6 +8193,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void
 *data)
                   POWERPC_FLAG_BUS_CLK;
      pcc->l1_dcache_size = 0x8000;
      pcc->l1_icache_size = 0x10000;
+    pcc->threads_per_core = 2;
  }


 static void powerpc_get_compat(Object *obj, Visitor *v,

target-ppc/translate_init.c +0 -0
@@ -8339,6 +8340,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void
 *data)
      pcc->l1_dcache_size = 0x8000;
      pcc->l1_icache_size = 0x8000;

     pcc->interrupts_big_endian =
ppc_cpu_interrupts_big_endian_lpcr;
+    pcc->threads_per_core = 4;
 }

 static void init_proc_POWER8(CPUPPCState *env)

target-ppc/translate_init.c +0 -0
@@ -8419,6 +8421,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void
 *data)
      pcc->l1_dcache_size = 0x8000;
      pcc->l1_icache_size = 0x8000;

     pcc->interrupts_big_endian =
ppc_cpu_interrupts_big_endian_lpcr;
+    pcc->threads_per_core = 8;
 }
 #endif /* defined (TARGET_PPC64) */

target-ppc/translate_init.c +0 -0
@@ -9074,6 +9077,9 @@ static void ppc_cpu_realizefn(DeviceState
 *dev, Error **errp)
  #endif


 #if !defined(CONFIG_USER_ONLY)
+    if (max_smt > pcc->threads_per_core) {
+        max_smt = pcc->threads_per_core;
+    }
     if (smp_threads > max_smt) {
         error_setg(errp, "Cannot support more than %d threads on PPC with %s",
                    max_smt, kvm_enabled() ? "KVM" : "TCG");

target-ppc/translate_init.c +0 -0
@@ -9094,7 +9100,7 @@ static void ppc_cpu_realizefn(DeviceState
 *dev, Error **errp)
      }


 #if !defined(CONFIG_USER_ONLY)
-    cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt +    cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * pcc-

Quoted 1 line

threads_per_core

         + (cs->cpu_index % smp_threads);
 #endif

#28 lore raw

PATCH 9/77 ppc: Fix do_rfi() for rfi emulation

In reply to Benjamin Herrenschmidt

XXX This patch needs double checking... It fixed 32-bit userspace but I'm not sure it's right. I wonder whether msr_is_64bit() should be applied to env->msr, not msr, but I need to double check the architecture.

---
 target-ppc/excp_helper.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/excp_helper.c +2 -2
index c1d6605..00fae60 100644
@@ -878,13 +878,13 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
     CPUState *cs = CPU(ppc_env_get_cpu(env));
 
 #if defined(TARGET_PPC64)
+    msr = msr & msrm;
     if (msr_is_64bit(env, msr)) {
         nip = (uint64_t)nip;
-        msr &= (uint64_t)msrm;
     } else {
         nip = (uint32_t)nip;
-        msr = (uint32_t)(msr & msrm);
         if (keep_msrh) {
+	    msr &= 0xffffffff;
             msr |= env->msr & ~((uint64_t)0xFFFFFFFF);
         }
     }
Signature · 1 line
2.5.0
#29 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 09/77] ppc: Fix do_rfi() for rfi emulation

Quoted 4 lines · Benjamin Herrenschmidt

XXX This patch needs double checking... It fixed 32-bit userspace but I'm not sure it's right. I wonder whether msr_is_64bit() should be applied to env->msr, not msr, but I need to double check the architecture.

Hrm, I'm not really sure where I'd look in the arch, but msr_is_64bit(env->msr) seems like it would make more sense to me. The current logic means that rfi, ostensibly a 32-bit instruction will have different behaviour depending on the upper bits of SRR1, which seems a unexpected.

Quoted 26 lines
---
 target-ppc/excp_helper.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/excp_helper.c +2 -2
index c1d6605..00fae60 100644
@@ -878,13 +878,13 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
     CPUState *cs = CPU(ppc_env_get_cpu(env));
 
 #if defined(TARGET_PPC64)
+    msr = msr & msrm;
     if (msr_is_64bit(env, msr)) {
         nip = (uint64_t)nip;
-        msr &= (uint64_t)msrm;
     } else {
         nip = (uint32_t)nip;
-        msr = (uint32_t)(msr & msrm);
         if (keep_msrh) {
+	    msr &= 0xffffffff;
             msr |= env->msr & ~((uint64_t)0xFFFFFFFF);
         }
     }
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#30 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 09/77] ppc: Fix do_rfi() for rfi emulation

Quoted 7 lines · David Gibson
Quoted 4 lines · Benjamin Herrenschmidt

XXX This patch needs double checking... It fixed 32-bit userspace but I'm not sure it's right. I wonder whether msr_is_64bit() should be applied to env->msr, not msr, but I need to double check the architecture.

Quoted 6 lines

Hrm, I'm not really sure where I'd look in the arch, but msr_is_64bit(env->msr) seems like it would make more sense to me. The current logic means that rfi, ostensibly a 32-bit instruction will
have different behaviour depending on the upper bits of SRR1, which seems a unexpected.

I only just discovered that rfi is actually gone from arch 2.07 :-)

I'll dig a bit more tomorrow.

Cheers,
Ben.

Quoted 29 lines
Quoted 28 lines

---
 target-ppc/excp_helper.c | 4 ++--

 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/excp_helper.c +0 -0
index c1d6605..00fae60 100644
@@ -878,13 +878,13 @@ static inline void do_rfi(CPUPPCState *env,
 target_ulong nip, target_ulong msr,
      CPUState *cs = CPU(ppc_env_get_cpu(env));


 #if defined(TARGET_PPC64)
+    msr = msr & msrm;
     if (msr_is_64bit(env, msr)) {
         nip = (uint64_t)nip;
-        msr &= (uint64_t)msrm;
     } else {
         nip = (uint32_t)nip;
-        msr = (uint32_t)(msr & msrm);
         if (keep_msrh) {

target-ppc/excp_helper.c +1 -0
+	    msr &= 0xffffffff;

             msr |= env->msr & ~((uint64_t)0xFFFFFFFF); }
     }

#31 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 09/77] ppc: Fix do_rfi() for rfi emulation

Quoted 4 lines · Benjamin Herrenschmidt


I only just discovered that rfi is actually gone from arch 2.07 :-)

I'll dig a bit more tomorrow.

Ok, so I had a closer look and tore that stuff appart even more :-)

If you are curious, feel free to check out github. I've removed the MSR mask completely, I can't figure out what it's supposed to be about. I've quickly tested 64-bit powernv/pseries, 32-bit userspace on 64-bit pseries kernel, and 32-bit Mac99 (ubuntu).

Cheers,
Ben.

Quoted 31 lines

Cheers,
Ben.

Quoted 28 lines
Quoted 28 lines

---
 target-ppc/excp_helper.c | 4 ++--

 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/excp_helper.c +0 -0
index c1d6605..00fae60 100644
@@ -878,13 +878,13 @@ static inline void do_rfi(CPUPPCState *env,
 target_ulong nip, target_ulong msr,
      CPUState *cs = CPU(ppc_env_get_cpu(env));


 #if defined(TARGET_PPC64)
+    msr = msr & msrm;
     if (msr_is_64bit(env, msr)) {
         nip = (uint64_t)nip;
-        msr &= (uint64_t)msrm;
     } else {
         nip = (uint32_t)nip;
-        msr = (uint32_t)(msr & msrm);
         if (keep_msrh) {

target-ppc/excp_helper.c +1 -0
+	    msr &= 0xffffffff;

             msr |= env->msr & ~((uint64_t)0xFFFFFFFF); }
     }

#32 lore raw

PATCH 10/77 ppc: Fix hreg_store_msr() so that non-HV mode cannot alter MSR:HV

In reply to Benjamin Herrenschmidt

This helper is only used by the various instructions that can alter MSR and not interrupts. Add a comment to that effect to the interrupt code as well in case somebody wants to change this

---
 target-ppc/excp_helper.c | 8 ++++++--
 target-ppc/helper_regs.h | 4 ++--
 2 files changed, 8 insertions(+), 4 deletions(-)
target-ppc/excp_helper.c +6 -2
index 00fae60..83e6c07 100644
@@ -662,8 +662,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
     }
 #endif
-    /* XXX: we don't use hreg_store_msr here as already have treated
-     *      any special case that could occur. Just store MSR and update hflags
+    /* We don't use hreg_store_msr here as already have treated
+     * any special case that could occur. Just store MSR and update hflags
+     *
+     * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
+     * will prevent setting of the HV bit which some exceptions might need
+     * to do.
      */
     env->msr = new_msr & env->msr_mask;
     hreg_compute_hflags(env);
target-ppc/helper_regs.h +2 -2
index 57da931..12af61c 100644
@@ -114,8 +114,8 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
     excp = 0;
     value &= env->msr_mask;
 #if !defined(CONFIG_USER_ONLY)
-    if (!alter_hv) {
-        /* mtmsr cannot alter the hypervisor state */
+    /* Neither mtmsr nor guest state can alter HV */
+    if (!alter_hv || !(env->msr & MSR_HVB)) {
         value &= ~MSR_HVB;
         value |= env->msr & MSR_HVB;
     }
Signature · 1 line
2.5.0
Reviewed-by on v1 10/77
#33 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 10/77] ppc: Fix hreg_store_msr() so that non-HV mode cannot alter MSR:HV

Quoted 5 lines · Benjamin Herrenschmidt

This helper is only used by the various instructions that can alter MSR and not interrupts. Add a comment to that effect to the interrupt code as well in case somebody wants to change this

Quoted 39 lines
---
 target-ppc/excp_helper.c | 8 ++++++--
 target-ppc/helper_regs.h | 4 ++--
 2 files changed, 8 insertions(+), 4 deletions(-)
target-ppc/excp_helper.c +6 -2
index 00fae60..83e6c07 100644
@@ -662,8 +662,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
     }
 #endif
-    /* XXX: we don't use hreg_store_msr here as already have treated
-     *      any special case that could occur. Just store MSR and update hflags
+    /* We don't use hreg_store_msr here as already have treated
+     * any special case that could occur. Just store MSR and update hflags
+     *
+     * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
+     * will prevent setting of the HV bit which some exceptions might need
+     * to do.
      */
     env->msr = new_msr & env->msr_mask;
     hreg_compute_hflags(env);
target-ppc/helper_regs.h +2 -2
index 57da931..12af61c 100644
@@ -114,8 +114,8 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
     excp = 0;
     value &= env->msr_mask;
 #if !defined(CONFIG_USER_ONLY)
-    if (!alter_hv) {
-        /* mtmsr cannot alter the hypervisor state */
+    /* Neither mtmsr nor guest state can alter HV */
+    if (!alter_hv || !(env->msr & MSR_HVB)) {
         value &= ~MSR_HVB;
         value |= env->msr & MSR_HVB;
     }
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#34 lore raw

PATCH 11/77 ppc: Create cpu_ppc_set_papr() helper

In reply to Benjamin Herrenschmidt

And move the code adjusting the MSR mask and calling kvmppc_set_papr() to it. This allows us to add a few more things such as disabling setting of MSR:HV and appropriate LPCR bits which will be used when fixing the exception model.

---
 hw/ppc/spapr.c              | 12 +++---------
 target-ppc/cpu.h            |  1 +
 target-ppc/translate_init.c | 37 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 40 insertions(+), 10 deletions(-)
hw/ppc/spapr.c +3 -9
index 37d071e..610629e 100644
@@ -1606,15 +1606,8 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
     /* Set time-base frequency to 512 MHz */
     cpu_ppc_tb_init(env, TIMEBASE_FREQ);
 
-    /* PAPR always has exception vectors in RAM not ROM. To ensure this,
-     * MSR[IP] should never be set.
-     */
-    env->msr_mask &= ~(1 << 6);
-
-    /* Tell KVM that we're in PAPR mode */
-    if (kvm_enabled()) {
-        kvmppc_set_papr(cpu);
-    }
+    /* Enable PAPR mode in TCG or KVM */
+    cpu_ppc_set_papr(cpu);
 
     if (cpu->max_compat) {
         if (ppc_set_compat(cpu, cpu->max_compat) < 0) {
@@ -1791,6 +1784,7 @@ static void ppc_spapr_init(MachineState *machine)
             fprintf(stderr, "Unable to find PowerPC CPU definition\n");
             exit(1);
         }
+
         spapr_cpu_init(spapr, cpu);
     }
 
target-ppc/cpu.h +1 -0
index 611367f..357b6e7 100644
@@ -1229,6 +1229,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val);
 void store_booke_tsr (CPUPPCState *env, target_ulong val);
 void ppc_tlb_invalidate_all (CPUPPCState *env);
 void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
+void cpu_ppc_set_papr(PowerPCCPU *cpu);
 #endif
 #endif
 
target-ppc/translate_init.c +36 -1
index 1d402e1..7bcfbc0 100644
@@ -8423,8 +8423,43 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
     pcc->threads_per_core = 8;
 }
-#endif /* defined (TARGET_PPC64) */
 
+#if !defined(CONFIG_USER_ONLY)
+
+void cpu_ppc_set_papr(PowerPCCPU *cpu)
+{
+    CPUPPCState *env = &cpu->env;
+    ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR];
+
+    /* PAPR always has exception vectors in RAM not ROM. To ensure this,
+     * MSR[IP] should never be set.
+     *
+     * We also disallow setting of MSR_HV
+     */
+    env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB);
+
+    /* Set emulated LPCR to not send interrupts to hypervisor. Note that
+     * under KVM, the actual HW LPCR will be set differently by KVM itself,
+     * the settings below ensure proper operations with TCG in absence of
+     * a real hypervisor
+     */
+    lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
+    lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
+
+    /* We should be followed by a CPU reset but update the active value
+     * just in case...
+     */
+    env->spr[SPR_LPCR] = lpcr->default_value;
+
+    /* Tell KVM that we're in PAPR mode */
+    if (kvm_enabled()) {
+        kvmppc_set_papr(cpu);
+    }
+}
+
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+#endif /* defined (TARGET_PPC64) */
 
 /*****************************************************************************/
 /* Generic CPU instantiation routine                                         */
Signature · 1 line
2.5.0
Reviewed-by on v1 11/77
#35 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 11/77] ppc: Create cpu_ppc_set_papr() helper

Quoted 6 lines · Benjamin Herrenschmidt

And move the code adjusting the MSR mask and calling kvmppc_set_papr() to it. This allows us to add a few more things such as disabling setting of MSR:HV and appropriate LPCR bits which will be used when fixing the exception model.

Quoted 97 lines
---
 hw/ppc/spapr.c              | 12 +++---------
 target-ppc/cpu.h            |  1 +
 target-ppc/translate_init.c | 37 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 40 insertions(+), 10 deletions(-)
hw/ppc/spapr.c +3 -9
index 37d071e..610629e 100644
@@ -1606,15 +1606,8 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
     /* Set time-base frequency to 512 MHz */
     cpu_ppc_tb_init(env, TIMEBASE_FREQ);
 
-    /* PAPR always has exception vectors in RAM not ROM. To ensure this,
-     * MSR[IP] should never be set.
-     */
-    env->msr_mask &= ~(1 << 6);
-
-    /* Tell KVM that we're in PAPR mode */
-    if (kvm_enabled()) {
-        kvmppc_set_papr(cpu);
-    }
+    /* Enable PAPR mode in TCG or KVM */
+    cpu_ppc_set_papr(cpu);
 
     if (cpu->max_compat) {
         if (ppc_set_compat(cpu, cpu->max_compat) < 0) {
@@ -1791,6 +1784,7 @@ static void ppc_spapr_init(MachineState *machine)
             fprintf(stderr, "Unable to find PowerPC CPU definition\n");
             exit(1);
         }
+
         spapr_cpu_init(spapr, cpu);
     }
 
target-ppc/cpu.h +1 -0
index 611367f..357b6e7 100644
@@ -1229,6 +1229,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val);
 void store_booke_tsr (CPUPPCState *env, target_ulong val);
 void ppc_tlb_invalidate_all (CPUPPCState *env);
 void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
+void cpu_ppc_set_papr(PowerPCCPU *cpu);
 #endif
 #endif
 
target-ppc/translate_init.c +36 -1
index 1d402e1..7bcfbc0 100644
@@ -8423,8 +8423,43 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
     pcc->threads_per_core = 8;
 }
-#endif /* defined (TARGET_PPC64) */
 
+#if !defined(CONFIG_USER_ONLY)
+
+void cpu_ppc_set_papr(PowerPCCPU *cpu)
+{
+    CPUPPCState *env = &cpu->env;
+    ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR];
+
+    /* PAPR always has exception vectors in RAM not ROM. To ensure this,
+     * MSR[IP] should never be set.
+     *
+     * We also disallow setting of MSR_HV
+     */
+    env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB);
+
+    /* Set emulated LPCR to not send interrupts to hypervisor. Note that
+     * under KVM, the actual HW LPCR will be set differently by KVM itself,
+     * the settings below ensure proper operations with TCG in absence of
+     * a real hypervisor
+     */
+    lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
+    lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
+
+    /* We should be followed by a CPU reset but update the active value
+     * just in case...
+     */
+    env->spr[SPR_LPCR] = lpcr->default_value;
+
+    /* Tell KVM that we're in PAPR mode */
+    if (kvm_enabled()) {
+        kvmppc_set_papr(cpu);
+    }
+}
+
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+#endif /* defined (TARGET_PPC64) */
 
 /*****************************************************************************/
 /* Generic CPU instantiation routine                                         */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#36 lore raw

PATCH 12/77 ppc: Better figure out if processor has HV mode

In reply to Benjamin Herrenschmidt

We use an env. flag which is set to the initial value of MSR_HVB in the msr_mask. We also adjust the POWER8 mask to set SHV.

Also use this to adjust ctx.hv so that it is *set* when the processor doesn't have an HV mode (970 with Apple mode for example), thus enabling hypervisor instructions/SPRs.

---
 target-ppc/cpu.h            |  4 ++++
 target-ppc/translate.c      |  4 +++-
 target-ppc/translate_init.c | 21 ++++++++++++++++-----
 3 files changed, 23 insertions(+), 6 deletions(-)
target-ppc/cpu.h +4 -0
index 357b6e7..062644e 100644
@@ -1113,6 +1113,10 @@ struct CPUPPCState {
     hwaddr mpic_iack;
     /* true when the external proxy facility mode is enabled */
     bool mpic_proxy;
+    /* set when the processor has an HV mode, thus HV priv
+     * instructions and SPRs are diallowed if MSR:HV is 0
+     */
+    bool has_hv_mode;
 #endif
 
     /* Those resources are used only during code translation */
target-ppc/translate.c +3 -1
index a2fe1b5..10eb9e3 100644
@@ -11465,8 +11465,10 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.exception = POWERPC_EXCP_NONE;
     ctx.spr_cb = env->spr_cb;
     ctx.pr = msr_pr;
-    ctx.hv = !msr_pr && msr_hv;
     ctx.mem_idx = env->dmmu_idx;
+#if !defined(CONFIG_USER_ONLY)
+    ctx.hv = !msr_pr && (msr_hv || !env->has_hv_mode);
+#endif
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
target-ppc/translate_init.c +16 -5
index 7bcfbc0..76f20ea 100644
@@ -8391,7 +8391,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                         PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
                         PPC2_TM;
     pcc->msr_mask = (1ull << MSR_SF) |
-                    (1ull << MSR_TM) |
+                    (1ull << MSR_SHV) |
+		    (1ull << MSR_TM) |
                     (1ull << MSR_VR) |
                     (1ull << MSR_VSX) |
                     (1ull << MSR_EE) |
@@ -9748,10 +9749,7 @@ static void ppc_cpu_reset(CPUState *s)
     pcc->parent_reset(s);
 
     msr = (target_ulong)0;
-    if (0) {
-        /* XXX: find a suitable condition to enable the hypervisor mode */
-        msr |= (target_ulong)MSR_HVB;
-    }
+    msr |= (target_ulong)MSR_HVB;
     msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */
     msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */
     msr |= (target_ulong)1 << MSR_EP;
@@ -9852,6 +9850,19 @@ static void ppc_cpu_initfn(Object *obj)
     env->bfd_mach = pcc->bfd_mach;
     env->check_pow = pcc->check_pow;
 
+    /* Mark HV mode as supported if the CPU has an MSR_HV bit
+     * in the msr_mask. The mask can later be cleared by PAPR
+     * mode but the hv mode support will remain, thus enforcing
+     * that we cannot use priv. instructions in guest in PAPR
+     * mode. For 970 we currently simply don't set HV in msr_mask
+     * thus simulating an "Apple mode" 970. If we ever want to
+     * support 970 HV mode, we'll have to add a processor attribute
+     * of some sort.
+     */
+#if !defined(CONFIG_USER_ONLY)
+    env->has_hv_mode = !!(env->msr_mask & MSR_HVB);
+#endif
+
 #if defined(TARGET_PPC64)
     if (pcc->sps) {
         env->sps = *pcc->sps;
Signature · 1 line
2.5.0
Reviewed-by on v1 12/77
#37 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 12/77] ppc: Better figure out if processor has HV mode

Quoted 8 lines · Benjamin Herrenschmidt

We use an env. flag which is set to the initial value of MSR_HVB in the msr_mask. We also adjust the POWER8 mask to set SHV.

Also use this to adjust ctx.hv so that it is *set* when the processor doesn't have an HV mode (970 with Apple mode for example), thus enabling hypervisor instructions/SPRs.

Quoted 83 lines
---
 target-ppc/cpu.h            |  4 ++++
 target-ppc/translate.c      |  4 +++-
 target-ppc/translate_init.c | 21 ++++++++++++++++-----
 3 files changed, 23 insertions(+), 6 deletions(-)
target-ppc/cpu.h +4 -0
index 357b6e7..062644e 100644
@@ -1113,6 +1113,10 @@ struct CPUPPCState {
     hwaddr mpic_iack;
     /* true when the external proxy facility mode is enabled */
     bool mpic_proxy;
+    /* set when the processor has an HV mode, thus HV priv
+     * instructions and SPRs are diallowed if MSR:HV is 0
+     */
+    bool has_hv_mode;
 #endif
 
     /* Those resources are used only during code translation */
target-ppc/translate.c +3 -1
index a2fe1b5..10eb9e3 100644
@@ -11465,8 +11465,10 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.exception = POWERPC_EXCP_NONE;
     ctx.spr_cb = env->spr_cb;
     ctx.pr = msr_pr;
-    ctx.hv = !msr_pr && msr_hv;
     ctx.mem_idx = env->dmmu_idx;
+#if !defined(CONFIG_USER_ONLY)
+    ctx.hv = !msr_pr && (msr_hv || !env->has_hv_mode);
+#endif
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
target-ppc/translate_init.c +16 -5
index 7bcfbc0..76f20ea 100644
@@ -8391,7 +8391,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                         PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
                         PPC2_TM;
     pcc->msr_mask = (1ull << MSR_SF) |
-                    (1ull << MSR_TM) |
+                    (1ull << MSR_SHV) |
+		    (1ull << MSR_TM) |
                     (1ull << MSR_VR) |
                     (1ull << MSR_VSX) |
                     (1ull << MSR_EE) |
@@ -9748,10 +9749,7 @@ static void ppc_cpu_reset(CPUState *s)
     pcc->parent_reset(s);
 
     msr = (target_ulong)0;
-    if (0) {
-        /* XXX: find a suitable condition to enable the hypervisor mode */
-        msr |= (target_ulong)MSR_HVB;
-    }
+    msr |= (target_ulong)MSR_HVB;
     msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */
     msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */
     msr |= (target_ulong)1 << MSR_EP;
@@ -9852,6 +9850,19 @@ static void ppc_cpu_initfn(Object *obj)
     env->bfd_mach = pcc->bfd_mach;
     env->check_pow = pcc->check_pow;
 
+    /* Mark HV mode as supported if the CPU has an MSR_HV bit
+     * in the msr_mask. The mask can later be cleared by PAPR
+     * mode but the hv mode support will remain, thus enforcing
+     * that we cannot use priv. instructions in guest in PAPR
+     * mode. For 970 we currently simply don't set HV in msr_mask
+     * thus simulating an "Apple mode" 970. If we ever want to
+     * support 970 HV mode, we'll have to add a processor attribute
+     * of some sort.
+     */
+#if !defined(CONFIG_USER_ONLY)
+    env->has_hv_mode = !!(env->msr_mask & MSR_HVB);
+#endif
+
 #if defined(TARGET_PPC64)
     if (pcc->sps) {
         env->sps = *pcc->sps;
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#38 lore raw

PATCH 13/77 ppc: tlbie, tlbia and tlbisync are HV only

In reply to Benjamin Herrenschmidt

Not that anything remotely recent supports tlbia but ...

---
 target-ppc/translate.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
target-ppc/translate.c +3 -3
index 10eb9e3..014fe5e 100644
@@ -4836,7 +4836,7 @@ static void gen_tlbia(DisasContext *ctx)
 #if defined(CONFIG_USER_ONLY)
     gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
 #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
@@ -4850,7 +4850,7 @@ static void gen_tlbiel(DisasContext *ctx)
 #if defined(CONFIG_USER_ONLY)
     gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
 #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
@@ -4864,7 +4864,7 @@ static void gen_tlbie(DisasContext *ctx)
 #if defined(CONFIG_USER_ONLY)
     gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
 #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
Signature · 1 line
2.5.0
#39 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 13/77] ppc: tlbie, tlbia and tlbisync are HV only

Quoted 12 lines · Benjamin Herrenschmidt

Not that anything remotely recent supports tlbia but ...

---
 target-ppc/translate.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
target-ppc/translate.c +0 -0
index 10eb9e3..014fe5e 100644

target-ppc/translate.c static void gen_tlbia(DisasContext *ctx) · line 4839

Quoted 5 lines
target-ppc/translate.c +1 -1
@@ -4836,7 +4836,7 @@ static void gen_tlbia(DisasContext *ctx)
 #if defined(CONFIG_USER_ONLY)
     gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
 #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {

If I'm reading your previous patch correctly, ctx->hv won't be set with in problem state, so I think the ctx->pr check is redundant.

Quoted 21 lines
target-ppc/translate.c +2 -2
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
@@ -4850,7 +4850,7 @@ static void gen_tlbiel(DisasContext *ctx)
 #if defined(CONFIG_USER_ONLY)
     gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
 #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
@@ -4864,7 +4864,7 @@ static void gen_tlbie(DisasContext *ctx)
 #if defined(CONFIG_USER_ONLY)
     gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
 #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
         gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
         return;
     }
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#40 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 13/77] ppc: tlbie, tlbia and tlbisync are HV only

Quoted 9 lines · David Gibson
Quoted 7 lines · Benjamin Herrenschmidt

Not that anything remotely recent supports tlbia but ...

---
 target-ppc/translate.c | 6 +++---

 1 file changed, 3 insertions(+), 3 deletions(-)
Quoted 13 lines

target-ppc/translate.c static void gen_tlbia(DisasContext *ctx) · line 4839

Quoted 10 lines
target-ppc/translate.c +1 -1
index 10eb9e3..014fe5e 100644
@@ -4836,7 +4836,7 @@ static void gen_tlbia(DisasContext *ctx)
  #if defined(CONFIG_USER_ONLY)
      gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
  #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {

If I'm reading your previous patch correctly, ctx->hv won't be set with in problem state, so I think the ctx->pr check is redundant.

Ah you are right. I do have second thoughts about that previous patch now that you mention it however. In the real MSR, HV and PR are independant, I wonder if I'm better off making the check explicit...

The reason I did it this way is that afaik, there is no such thing as a usermode hypervisor resource in the architecture, so any hypervisor resource is also a supervisor mode one, but having ctx->hv be 0 when MSR:HV=1 + MSR:PR=1 might make it easy to write incorrect code in other places when deciding for example how to direct interrupts.

I'll need to think a bit more about this one.

Quoted 22 lines
Quoted 21 lines
target-ppc/translate.c +2 -2
          gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
          return;
      }
@@ -4850,7 +4850,7 @@ static void gen_tlbiel(DisasContext *ctx)
  #if defined(CONFIG_USER_ONLY)
      gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
  #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
          gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
          return;
      }
@@ -4864,7 +4864,7 @@ static void gen_tlbie(DisasContext *ctx)
  #if defined(CONFIG_USER_ONLY)
      gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
  #else
-    if (unlikely(ctx->pr)) {
+    if (unlikely(ctx->pr || !ctx->hv)) {
          gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
          return;
      }
#41 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 13/77] ppc: tlbie, tlbia and tlbisync are HV only

Quoted 13 lines · Benjamin Herrenschmidt

Ah you are right. I do have second thoughts about that previous patch now that you mention it however. In the real MSR, HV and PR are independant, I wonder if I'm better off making the check explicit...

The reason I did it this way is that afaik, there is no such thing as a usermode hypervisor resource in the architecture, so any hypervisor resource is also a supervisor mode one, but having ctx->hv be 0 when MSR:HV=1 + MSR:PR=1 might make it easy to write incorrect code in other places when deciding for example how to direct
interrupts.

Quoted 1 line

I'll need to think a bit more about this one.

So I took out that bit in the previous patch, since we already seem to check ctx.pr explicitly in most places anyway. There was one where we didn't which I fixed (in the SMT ops).

Cheers,
Ben.

#42 lore raw

PATCH 14/77 ppc: Change 'invalid' bit mask of tlbiel and tlbie

In reply to Benjamin Herrenschmidt

Otherwise it will trip on the forms used in recent architecture.

Ideally, we should have different handlers for different architecture levels but our current implementation of TLB flushing is dumb enough that this will do for now.

---
 target-ppc/translate.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
target-ppc/translate.c +4 -2
index 014fe5e..bd5df40 100644
@@ -9952,8 +9952,10 @@ GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B),
 GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B),
 #endif
 GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA),
-GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE),
-GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM_TLBIE),
+/* XXX Those instructions will need to be handled differently for
+ * different ISA versions */
+GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE),
+GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE),
 GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC),
 #if defined(TARGET_PPC64)
 GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x03FFFC01, PPC_SLBI),
Signature · 1 line
2.5.0
Reviewed-by on v1 14/77
#43 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 14/77] ppc: Change 'invalid' bit mask of tlbiel and tlbie

Quoted 7 lines · Benjamin Herrenschmidt

Otherwise it will trip on the forms used in recent architecture.

Ideally, we should have different handlers for different architecture levels but our current implementation of TLB flushing is dumb enough that this will do for now.

Quoted 21 lines
---
 target-ppc/translate.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
target-ppc/translate.c +4 -2
index 014fe5e..bd5df40 100644
@@ -9952,8 +9952,10 @@ GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B),
 GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B),
 #endif
 GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA),
-GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE),
-GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM_TLBIE),
+/* XXX Those instructions will need to be handled differently for
+ * different ISA versions */
+GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x001F0001, PPC_MEM_TLBIE),
+GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x001F0001, PPC_MEM_TLBIE),
 GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC),
 #if defined(TARGET_PPC64)
 GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x03FFFC01, PPC_SLBI),
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#44 lore raw

PATCH 15/77 ppc: Fix sign extension issue in mtmsr(d) emulation

In reply to Benjamin Herrenschmidt

From: Michael Neuling <[email protected]>

---
 target-ppc/translate.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/translate.c +2 -2
index bd5df40..3974cd2 100644
@@ -4391,7 +4391,7 @@ static void gen_mtmsrd(DisasContext *ctx)
         /* Special form that does not need any synchronisation */
         TCGv t0 = tcg_temp_new();
         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
-        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE)));
         tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
         tcg_temp_free(t0);
     } else {
@@ -4422,7 +4422,7 @@ static void gen_mtmsr(DisasContext *ctx)
         /* Special form that does not need any synchronisation */
         TCGv t0 = tcg_temp_new();
         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
-        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE)));
         tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
         tcg_temp_free(t0);
     } else {
Signature · 1 line
2.5.0
Reviewed-by on v1 15/77
#45 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 15/77] ppc: Fix sign extension issue in mtmsr(d) emulation

Quoted 4 lines · Benjamin Herrenschmidt

From: Michael Neuling <[email protected]>

Looks correct, though my memory of C promotion rules is obviously a bit stale, since I'm not immediately seeing why the original was wrong.

Quoted 26 lines
---
 target-ppc/translate.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/translate.c +2 -2
index bd5df40..3974cd2 100644
@@ -4391,7 +4391,7 @@ static void gen_mtmsrd(DisasContext *ctx)
         /* Special form that does not need any synchronisation */
         TCGv t0 = tcg_temp_new();
         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
-        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE)));
         tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
         tcg_temp_free(t0);
     } else {
@@ -4422,7 +4422,7 @@ static void gen_mtmsr(DisasContext *ctx)
         /* Special form that does not need any synchronisation */
         TCGv t0 = tcg_temp_new();
         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
-        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE)));
         tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
         tcg_temp_free(t0);
     } else {
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#46 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 15/77] ppc: Fix sign extension issue in mtmsr(d) emulation

Quoted 4 lines · David Gibson
Quoted 2 lines · Benjamin Herrenschmidt

From: Michael Neuling <[email protected]>

Quoted 8 lines
Quoted 2 lines

Looks correct, though my memory of C promotion rules is obviously a bit stale, since I'm not immediately seeing why the original was wrong.

Well, at least it makes things work :-)

Quoted 33 lines
Quoted 32 lines

---
 target-ppc/translate.c | 4 ++--

 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/translate.c +0 -0
index bd5df40..3974cd2 100644
@@ -4391,7 +4391,7 @@ static void gen_mtmsrd(DisasContext *ctx)
          /* Special form that does not need any synchronisation */
          TCGv t0 = tcg_temp_new();

         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
-        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE)));
         tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0);
     } else {

target-ppc/translate.c +0 -0
@@ -4422,7 +4422,7 @@ static void gen_mtmsr(DisasContext *ctx)
          /* Special form that does not need any synchronisation */
          TCGv t0 = tcg_temp_new();

         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
-        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+        tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(target_ulong)((1 << MSR_RI) | (1 << MSR_EE)));
         tcg_gen_or_tl(cpu_msr, cpu_msr, t0); tcg_temp_free(t0);
     } else {

#47 lore raw

PATCH 16/77 ppc: Get out of emulation on SMT "OR" ops

In reply to Benjamin Herrenschmidt

Otherwise tight loops at smt_low for example, which OPAL does, eat so much CPU that we can't boot a kernel anymore. With that, I can boot 8 CPUs just fine with powernv.

---
 target-ppc/translate.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)
target-ppc/translate.c +17 -2
index 3974cd2..e8bbd59 100644
@@ -1396,6 +1396,19 @@ GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER);
 /* nor & nor. */
 GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER);
 
+#if defined(TARGET_PPC64)
+static void gen_pause(DisasContext *ctx)
+{
+    TCGv_i32 t0 = tcg_const_i32(0);
+    tcg_gen_st_i32(t0, cpu_env,
+                   -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted));
+    tcg_temp_free_i32(t0);
+
+    /* Stop translation, this gives other CPUs a chance to run */
+    gen_exception_err(ctx, EXCP_HLT, 1);
+}
+#endif /* defined(TARGET_PPC64) */
+
 /* or & or. */
 static void gen_or(DisasContext *ctx)
 {
@@ -1468,6 +1481,10 @@ static void gen_or(DisasContext *ctx)
             tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50);
             gen_store_spr(SPR_PPR, t0);
             tcg_temp_free(t0);
+            /* Pause us out of TCG otherwise spin loops with smt_low
+             * eat too much CPU and the kernel hangs
+             */
+            gen_pause(ctx);
         }
 #endif
     }
@@ -1493,8 +1510,6 @@ static void gen_ori(DisasContext *ctx)
     target_ulong uimm = UIMM(ctx->opcode);
 
     if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
-        /* NOP */
-        /* XXX: should handle special NOPs for POWER series */
         return;
     }
     tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm);
Signature · 1 line
2.5.0
Reviewed-by on v1 16/77
#48 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 16/77] ppc: Get out of emulation on SMT "OR" ops

Quoted 5 lines · Benjamin Herrenschmidt

Otherwise tight loops at smt_low for example, which OPAL does, eat so much CPU that we can't boot a kernel anymore. With that, I can boot 8 CPUs just fine with powernv.

Quoted 48 lines
---
 target-ppc/translate.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)
target-ppc/translate.c +17 -2
index 3974cd2..e8bbd59 100644
@@ -1396,6 +1396,19 @@ GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER);
 /* nor & nor. */
 GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER);
 
+#if defined(TARGET_PPC64)
+static void gen_pause(DisasContext *ctx)
+{
+    TCGv_i32 t0 = tcg_const_i32(0);
+    tcg_gen_st_i32(t0, cpu_env,
+                   -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted));
+    tcg_temp_free_i32(t0);
+
+    /* Stop translation, this gives other CPUs a chance to run */
+    gen_exception_err(ctx, EXCP_HLT, 1);
+}
+#endif /* defined(TARGET_PPC64) */
+
 /* or & or. */
 static void gen_or(DisasContext *ctx)
 {
@@ -1468,6 +1481,10 @@ static void gen_or(DisasContext *ctx)
             tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50);
             gen_store_spr(SPR_PPR, t0);
             tcg_temp_free(t0);
+            /* Pause us out of TCG otherwise spin loops with smt_low
+             * eat too much CPU and the kernel hangs
+             */
+            gen_pause(ctx);
         }
 #endif
     }
@@ -1493,8 +1510,6 @@ static void gen_ori(DisasContext *ctx)
     target_ulong uimm = UIMM(ctx->opcode);
 
     if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
-        /* NOP */
-        /* XXX: should handle special NOPs for POWER series */
         return;
     }
     tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm);
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#49 lore raw

PATCH 17/77 ppc: Add PPC_64H instruction flag to POWER7 and POWER8

In reply to Benjamin Herrenschmidt

This will enable decoding of hrfid

---
 target-ppc/translate_init.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/translate_init.c +2 -2
index 76f20ea..f11e7d0 100644
@@ -8303,7 +8303,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
                        PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
                        PPC_MEM_SYNC | PPC_MEM_EIEIO |
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
-                       PPC_64B | PPC_ALTIVEC |
+                       PPC_64B | PPC_64H | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
                        PPC_POPCNTB | PPC_POPCNTWD;
     pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
@@ -8380,7 +8380,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                        PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
                        PPC_MEM_SYNC | PPC_MEM_EIEIO |
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
-                       PPC_64B | PPC_64BX | PPC_ALTIVEC |
+                       PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
                        PPC_POPCNTB | PPC_POPCNTWD;
     pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
Signature · 1 line
2.5.0
Reviewed-by on v1 17/77
#50 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 17/77] ppc: Add PPC_64H instruction flag to POWER7 and POWER8

Quoted 3 lines · Benjamin Herrenschmidt

This will enable decoding of hrfid

Quoted 26 lines
---
 target-ppc/translate_init.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
target-ppc/translate_init.c +2 -2
index 76f20ea..f11e7d0 100644
@@ -8303,7 +8303,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
                        PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
                        PPC_MEM_SYNC | PPC_MEM_EIEIO |
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
-                       PPC_64B | PPC_ALTIVEC |
+                       PPC_64B | PPC_64H | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
                        PPC_POPCNTB | PPC_POPCNTWD;
     pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
@@ -8380,7 +8380,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                        PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
                        PPC_MEM_SYNC | PPC_MEM_EIEIO |
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
-                       PPC_64B | PPC_64BX | PPC_ALTIVEC |
+                       PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
                        PPC_POPCNTB | PPC_POPCNTWD;
     pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#51 lore raw

PATCH 18/77 ppc: Rework POWER7 & POWER8 exception model

In reply to Benjamin Herrenschmidt

Properly implement LPES0/1 handling for HV vs. !HV mode and fix AIL implementation.

---
 target-ppc/cpu.h            |   2 +
 target-ppc/excp_helper.c    | 175 ++++++++++++++++++++++----------------------
 target-ppc/translate_init.c |   2 +-
 3 files changed, 92 insertions(+), 87 deletions(-)
target-ppc/cpu.h +2 -0
index 062644e..8185812 100644
@@ -162,6 +162,8 @@ enum powerpc_excp_t {
     POWERPC_EXCP_970,
     /* POWER7 exception model           */
     POWERPC_EXCP_POWER7,
+    /* POWER8 exception model           */
+    POWERPC_EXCP_POWER8,
 #endif /* defined(TARGET_PPC64) */
 };
 
target-ppc/excp_helper.c +89 -86

348 lines not on this page · raw message

target-ppc/translate_init.c +1 -1
index f11e7d0..8a50273 100644
@@ -8412,7 +8412,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
 #if defined(CONFIG_SOFTMMU)
     pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
 #endif
-    pcc->excp_model = POWERPC_EXCP_POWER7;
+    pcc->excp_model = POWERPC_EXCP_POWER8;
     pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
     pcc->bfd_mach = bfd_mach_ppc64;
     pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
Signature · 1 line
2.5.0
#52 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 18/77] ppc: Rework POWER7 & POWER8 exception model

Quoted 54 lines · Benjamin Herrenschmidt

Properly implement LPES0/1 handling for HV vs. !HV mode and fix AIL implementation.

---
 target-ppc/cpu.h            |   2 +
 target-ppc/excp_helper.c    | 175 ++++++++++++++++++++++----------------------
 target-ppc/translate_init.c |   2 +-
 3 files changed, 92 insertions(+), 87 deletions(-)
target-ppc/cpu.h +2 -0
index 062644e..8185812 100644
@@ -162,6 +162,8 @@ enum powerpc_excp_t {
     POWERPC_EXCP_970,
     /* POWER7 exception model           */
     POWERPC_EXCP_POWER7,
+    /* POWER8 exception model           */
+    POWERPC_EXCP_POWER8,
 #endif /* defined(TARGET_PPC64) */
 };
 
target-ppc/excp_helper.c +3 -11
index 83e6c07..716b27b 100644
@@ -74,22 +74,14 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
     target_ulong msr, new_msr, vector;
-    int srr0, srr1, asrr0, asrr1;
-    int lpes0, lpes1, lev;
+    int srr0, srr1, asrr0, asrr1, lev, ail;
+    bool lpes0;
 
-    if (0) {
-        /* XXX: find a suitable condition to enable the hypervisor mode */
-        lpes0 = (env->spr[SPR_LPCR] >> 1) & 1;
-        lpes1 = (env->spr[SPR_LPCR] >> 2) & 1;
-    } else {
-        /* Those values ensure we won't enter the hypervisor mode */
-        lpes0 = 0;
-        lpes1 = 1;
-    }
 
     qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
                   " => %08x (%02x)\n", env->nip, excp, env->error_code);
 
+
     /* new srr1 value excluding must-be-zero bits */
     if (excp_model == POWERPC_EXCP_BOOKE) {
         msr = env->msr;

target-ppc/excp_helper.c static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) · line 95

Quoted 9 lines
target-ppc/excp_helper.c +4 -2
@@ -97,8 +89,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         msr = env->msr & ~0x783f0000ULL;
     }
 
-    /* new interrupt handler msr */
-    new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+    /* new interrupt handler msr preserves existing HV and ME unless
+     * explicitly overriden
+     */
+    new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);

Ouch. The fact that MSR_ME is a bit number, but MSR_HVB is a mask is certainly confusing, but that's a pre-existing problem.

Quoted 180 lines
target-ppc/excp_helper.c +38 -44
     /* target registers */
     srr0 = SPR_SRR0;
@@ -106,6 +100,33 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     asrr0 = -1;
     asrr1 = -1;
 
+    /* Exception targetting modifiers
+     *
+     * LPES0 is supported on POWER7/8
+     * LPES1 is not supported (old iSeries mode)
+     *
+     * On anything else, we behave as if LPES0 is 1
+     * (externals don't alter MSR:HV)
+     *
+     * AIL is initialized here but can be cleared by
+     * selected exceptions
+     */
+#if defined(TARGET_PPC64)
+    if (excp_model == POWERPC_EXCP_POWER7 ||
+        excp_model == POWERPC_EXCP_POWER8) {
+        lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
+        if (excp_model == POWERPC_EXCP_POWER8) {
+            ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+        } else {
+            ail = 0;
+        }
+    } else
+#endif /* defined(TARGET_PPC64) */
+    {
+        lpes0 = true;
+        ail = 0;
+    }
+
     switch (excp) {
     case POWERPC_EXCP_NONE:
         /* Should never happen */
@@ -141,10 +162,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
             cs->halted = 1;
             cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
         }
-        if (0) {
-            /* XXX: find a suitable condition to enable the hypervisor mode */
-            new_msr |= (target_ulong)MSR_HVB;
-        }
+        new_msr |= (target_ulong)MSR_HVB;
+        ail = 0;
 
         /* machine check exceptions don't have ME set */
         new_msr &= ~((target_ulong)1 << MSR_ME);
@@ -169,23 +188,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     case POWERPC_EXCP_DSI:       /* Data storage exception                   */
         LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx
                  "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_next;
     case POWERPC_EXCP_ISI:       /* Instruction storage exception            */
         LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx
                  "\n", msr, env->nip);
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         msr |= env->error_code;
         goto store_next;
     case POWERPC_EXCP_EXTERNAL:  /* External input                           */
         cs = CPU(cpu);
 
-        if (lpes0 == 1) {
+        if (!lpes0) {
             new_msr |= (target_ulong)MSR_HVB;
+            new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+            srr0 = SPR_HSRR0;
+            srr1 = SPR_HSRR1;
         }
         if (env->mpic_proxy) {
             /* IACK the IRQ on delivery */
@@ -193,9 +209,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
         goto store_next;
     case POWERPC_EXCP_ALIGN:     /* Alignment exception                      */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         /* XXX: this is false */
         /* Get rS/rD and rA from faulting opcode */
         env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4))
@@ -210,9 +223,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
                 env->error_code = 0;
                 return;
             }
-            if (lpes1 == 0) {
-                new_msr |= (target_ulong)MSR_HVB;
-            }
             msr |= 0x00100000;
             if (msr_fe0 == msr_fe1) {
                 goto store_next;
@@ -221,23 +231,14 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
             break;
         case POWERPC_EXCP_INVAL:
             LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip);
-            if (lpes1 == 0) {
-                new_msr |= (target_ulong)MSR_HVB;
-            }
             msr |= 0x00080000;
             env->spr[SPR_BOOKE_ESR] = ESR_PIL;
             break;
         case POWERPC_EXCP_PRIV:
-            if (lpes1 == 0) {
-                new_msr |= (target_ulong)MSR_HVB;
-            }
             msr |= 0x00040000;
             env->spr[SPR_BOOKE_ESR] = ESR_PPR;
             break;
         case POWERPC_EXCP_TRAP:
-            if (lpes1 == 0) {
-                new_msr |= (target_ulong)MSR_HVB;
-            }
             msr |= 0x00020000;
             env->spr[SPR_BOOKE_ESR] = ESR_PTR;
             break;
@@ -249,27 +250,23 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
         goto store_current;
     case POWERPC_EXCP_FPU:       /* Floating-point unavailable exception     */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_current;
     case POWERPC_EXCP_SYSCALL:   /* System call exception                    */
         dump_syscall(env);
         lev = env->error_code;
+
+        /* "PAPR mode" built-in hypercall emulation */
         if ((lev == 1) && cpu_ppc_hypercall) {
             cpu_ppc_hypercall(cpu);
             return;
         }
-        if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) {
+        if (lev == 1) {
             new_msr |= (target_ulong)MSR_HVB;
         }
         goto store_next;
     case POWERPC_EXCP_APU:       /* Auxiliary processor unavailable          */
         goto store_current;
     case POWERPC_EXCP_DECR:      /* Decrementer exception                    */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_next;
     case POWERPC_EXCP_FIT:       /* Fixed-interval timer interrupt           */
         /* FIT on 4xx */
@@ -338,21 +335,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         } else {
             new_msr &= ~((target_ulong)1 << MSR_ME);
         }
-
-        if (0) {
-            /* XXX: find a suitable condition to enable the hypervisor mode */
-            new_msr |= (target_ulong)MSR_HVB;
-        }
+        new_msr |= (target_ulong)MSR_HVB;
+        ail = 0;
         goto store_next;
     case POWERPC_EXCP_DSEG:      /* Data segment exception                   */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_next;
     case POWERPC_EXCP_ISEG:      /* Instruction segment exception            */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_next;
     case POWERPC_EXCP_HDECR:     /* Hypervisor decrementer exception         */
         srr0 = SPR_HSRR0;
@@ -361,21 +349,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
         goto store_next;
     case POWERPC_EXCP_TRACE:     /* Trace exception                          */

target-ppc/excp_helper.c static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) · line 358

Quoted 10 lines
target-ppc/excp_helper.c +1 -3
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_next;
     case POWERPC_EXCP_HDSI:      /* Hypervisor data storage exception        */
         srr0 = SPR_HSRR0;
         srr1 = SPR_HSRR1;
         new_msr |= (target_ulong)MSR_HVB;
         new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+        ail = 0;

Do you need to set ail explicitly here, given the general ail logic below?

Quoted 153 lines
target-ppc/excp_helper.c +43 -26
         goto store_next;
     case POWERPC_EXCP_HISI:      /* Hypervisor instruction storage exception */
         srr0 = SPR_HSRR0;
         srr1 = SPR_HSRR1;
         new_msr |= (target_ulong)MSR_HVB;
         new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+        ail = 0;
         goto store_next;
     case POWERPC_EXCP_HDSEG:     /* Hypervisor data segment exception        */
         srr0 = SPR_HSRR0;
@@ -390,19 +377,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
         goto store_next;
     case POWERPC_EXCP_VPU:       /* Vector unavailable exception             */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_current;
     case POWERPC_EXCP_VSXU:       /* VSX unavailable exception               */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_current;
     case POWERPC_EXCP_FU:         /* Facility unavailable exception          */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         goto store_current;
     case POWERPC_EXCP_PIT:       /* Programmable interval timer interrupt    */
         LOG_EXCP("PIT exception\n");
@@ -421,9 +399,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
                   "is not implemented yet !\n");
         goto store_next;
     case POWERPC_EXCP_IFTLB:     /* Instruction fetch TLB error              */
-        if (lpes1 == 0) { /* XXX: check this */
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         switch (excp_model) {
         case POWERPC_EXCP_602:
         case POWERPC_EXCP_603:
@@ -440,9 +415,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
         break;
     case POWERPC_EXCP_DLTLB:     /* Data load TLB miss                       */
-        if (lpes1 == 0) { /* XXX: check this */
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         switch (excp_model) {
         case POWERPC_EXCP_602:
         case POWERPC_EXCP_603:
@@ -459,9 +431,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
         break;
     case POWERPC_EXCP_DSTLB:     /* Data store TLB miss                      */
-        if (lpes1 == 0) { /* XXX: check this */
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         switch (excp_model) {
         case POWERPC_EXCP_602:
         case POWERPC_EXCP_603:
@@ -567,9 +536,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
                   "is not implemented yet !\n");
         goto store_next;
     case POWERPC_EXCP_PERFM:     /* Embedded performance monitor interrupt   */
-        if (lpes1 == 0) {
-            new_msr |= (target_ulong)MSR_HVB;
-        }
         /* XXX: TODO */
         cpu_abort(cs,
                   "Performance counter exception is not implemented yet !\n");
@@ -613,6 +579,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     }
     /* Save MSR */
     env->spr[srr1] = msr;
+
+    /* Sanity check */
+    if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) {
+        cpu_abort(cs, "Trying to deliver HV exception %d with no HV support\n", excp);
+    }
+
     /* If any alternate SRR register are defined, duplicate saved values */
     if (asrr0 != -1) {
         env->spr[asrr0] = env->spr[srr0];
@@ -621,13 +593,20 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         env->spr[asrr1] = env->spr[srr1];
     }
 
-    if (env->spr[SPR_LPCR] & LPCR_AIL) {
-        new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-    }
-
+    /* Sort out endianness of interrupt, this differs depending on the
+     * CPU, the HV mode, etc...
+     */
 #ifdef TARGET_PPC64
     if (excp_model == POWERPC_EXCP_POWER7) {
-        if (env->spr[SPR_LPCR] & LPCR_ILE) {
+        if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
+            new_msr |= (target_ulong)1 << MSR_LE;
+        }
+    } else if (excp_model == POWERPC_EXCP_POWER8) {
+        if (new_msr & MSR_HVB) {
+            if (env->spr[SPR_HID0] & HID0_HILE) {
+                new_msr |= (target_ulong)1 << MSR_LE;
+            }
+        } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
             new_msr |= (target_ulong)1 << MSR_LE;
         }
     } else if (msr_ile) {
@@ -646,6 +625,30 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
                   excp);
     }
     vector |= env->excp_prefix;
+
+    /* AIL only works if there is no HV transition and we are running with
+     * translations enabled
+     */
+    if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) ||
+        ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
+        ail = 0;
+    }
+    /* Handle AIL */
+    if (ail) {
+        new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
+        switch(ail) {
+        case 2:
+            vector |= 0x18000;
+            break;
+        case 3:
+            vector |= 0xc000000000004000ull;
+            break;
+        default:
+            cpu_abort(cs, "Invalid AIL combination %d\n", ail);
+            break;
+        }
+    }
+
 #if defined(TARGET_PPC64)
     if (excp_model == POWERPC_EXCP_BOOKE) {
         if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
target-ppc/translate_init.c +1 -1
index f11e7d0..8a50273 100644
@@ -8412,7 +8412,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
 #if defined(CONFIG_SOFTMMU)
     pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
 #endif
-    pcc->excp_model = POWERPC_EXCP_POWER7;
+    pcc->excp_model = POWERPC_EXCP_POWER8;
     pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
     pcc->bfd_mach = bfd_mach_ppc64;
     pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#53 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 18/77] ppc: Rework POWER7 & POWER8 exception model

Quoted 10 lines · David Gibson
Quoted 7 lines

-    /* new interrupt handler msr */
-    new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+    /* new interrupt handler msr preserves existing HV and ME unless +     * explicitly overriden
+     */
+    new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);

Ouch.  The fact that MSR_ME is a bit number, but MSR_HVB is a mask is certainly confusing, but that's a pre-existing problem.

That shit bit me more than once indeed, but it's a fix for another day.

.../...

Quoted 9 lines
Quoted 7 lines

     case POWERPC_EXCP_HDSI:      /* Hypervisor data storage exception        */ srr0 = SPR_HSRR0;
         srr1 = SPR_HSRR1;
         new_msr |= (target_ulong)MSR_HVB;
         new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+        ail = 0;

Do you need to set ail explicitly here, given the general ail logic below?

Not on this indeed. I think that's a remnant of how that patch evolved. We do need to clear unconditionally on other things like machine checks, I'll give that another sweep.

Cheers,
Ben.

#54 lore raw

PATCH 19/77 ppc: Fix POWER7 and POWER8 exception definitions

In reply to Benjamin Herrenschmidt

We were initializing unused ones and missing some

---
 target-ppc/cpu.h            | 11 ++++++++++-
 target-ppc/translate_init.c | 27 +++++++++++++++++++++------
 2 files changed, 31 insertions(+), 7 deletions(-)
target-ppc/cpu.h +10 -1
index 8185812..23479b1 100644
@@ -200,6 +200,9 @@ enum {
     POWERPC_EXCP_HYPPRIV  = 41, /* Embedded hypervisor priv instruction      */
     /* Vectors 42 to 63 are reserved                                         */
     /* Exceptions defined in the PowerPC server specification                */
+    /* Server doorbell variants */
+#define POWERPC_EXCP_SDOOR	POWERPC_EXCP_GDOORI
+#define POWERPC_EXCP_SDOOR_HV	POWERPC_EXCP_DOORI
     POWERPC_EXCP_RESET    = 64, /* System reset exception                    */
     POWERPC_EXCP_DSEG     = 65, /* Data segment exception                    */
     POWERPC_EXCP_ISEG     = 66, /* Instruction segment exception             */
@@ -242,8 +245,12 @@ enum {
     /* VSX Unavailable (Power ISA 2.06 and later)                            */
     POWERPC_EXCP_VSXU     = 94, /* VSX Unavailable                           */
     POWERPC_EXCP_FU       = 95, /* Facility Unavailable                      */
+    /* Additional ISA 2.06 and later server exceptions                       */
+    POWERPC_EXCP_HV_EMU   = 96, /* HV emulation assistance                   */
+    POWERPC_EXCP_HV_MAINT = 97, /* HMI                                       */
+    POWERPC_EXCP_HV_FU    = 98, /* Hypervisor Facility unavailable           */
     /* EOL                                                                   */
-    POWERPC_EXCP_NB       = 96,
+    POWERPC_EXCP_NB       = 99,
     /* QEMU exceptions: used internally during code translation              */
     POWERPC_EXCP_STOP         = 0x200, /* stop translation                   */
     POWERPC_EXCP_BRANCH       = 0x201, /* branch instruction                 */
@@ -2197,6 +2204,8 @@ enum {
     PPC_INTERRUPT_CDOORBELL,      /* Critical doorbell interrupt          */
     PPC_INTERRUPT_DOORBELL,       /* Doorbell interrupt                   */
     PPC_INTERRUPT_PERFM,          /* Performance monitor interrupt        */
+    PPC_INTERRUPT_HMI,            /* Hypervisor Maintainance interrupt    */
+    PPC_INTERRUPT_HDOORBELL,      /* Hypervisor Doorbell interrupt        */
 };
 
 /* Processor Compatibility mask (PCR) */
target-ppc/translate_init.c +21 -6
index 8a50273..5210b25 100644
@@ -3105,18 +3105,30 @@ static void init_excp_POWER7 (CPUPPCState *env)
     env->excp_vectors[POWERPC_EXCP_HDECR]    = 0x00000980;
     env->excp_vectors[POWERPC_EXCP_SYSCALL]  = 0x00000C00;
     env->excp_vectors[POWERPC_EXCP_TRACE]    = 0x00000D00;
+    env->excp_vectors[POWERPC_EXCP_HDSI]     = 0x00000E00;
+    env->excp_vectors[POWERPC_EXCP_HISI]     = 0x00000E20;
+    env->excp_vectors[POWERPC_EXCP_HV_EMU]   = 0x00000E40;
+    env->excp_vectors[POWERPC_EXCP_HV_MAINT] = 0x00000E60;
     env->excp_vectors[POWERPC_EXCP_PERFM]    = 0x00000F00;
     env->excp_vectors[POWERPC_EXCP_VPU]      = 0x00000F20;
     env->excp_vectors[POWERPC_EXCP_VSXU]     = 0x00000F40;
-    env->excp_vectors[POWERPC_EXCP_FU]       = 0x00000F60;
-    env->excp_vectors[POWERPC_EXCP_IABR]     = 0x00001300;
-    env->excp_vectors[POWERPC_EXCP_MAINT]    = 0x00001600;
-    env->excp_vectors[POWERPC_EXCP_VPUA]     = 0x00001700;
-    env->excp_vectors[POWERPC_EXCP_THERM]    = 0x00001800;
     /* Hardware reset vector */
     env->hreset_vector = 0x0000000000000100ULL;
 #endif
 }
+
+static void init_excp_POWER8 (CPUPPCState *env)
+{
+    init_excp_POWER7(env);
+
+#if !defined(CONFIG_USER_ONLY)
+    env->excp_vectors[POWERPC_EXCP_SDOOR]    = 0x00000A00;
+    env->excp_vectors[POWERPC_EXCP_FU]       = 0x00000F60;
+    env->excp_vectors[POWERPC_EXCP_HV_FU]    = 0x00000F80;
+    env->excp_vectors[POWERPC_EXCP_SDOOR_HV] = 0x00000E80;
+#endif
+}
+
 #endif
 
 /*****************************************************************************/
@@ -8078,10 +8090,13 @@ static void init_proc_book3s_64(CPUPPCState *env, int version)
         ppc970_irq_init(env);
         break;
     case BOOK3S_CPU_POWER7:
-    case BOOK3S_CPU_POWER8:
         init_excp_POWER7(env);
         ppcPOWER7_irq_init(env);
         break;
+    case BOOK3S_CPU_POWER8:
+        init_excp_POWER8(env);
+        ppcPOWER7_irq_init(env);
+        break;
     default:
         g_assert_not_reached();
     }
Signature · 1 line
2.5.0
Reviewed-by on v1 19/77
#55 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 19/77] ppc: Fix POWER7 and POWER8 exception definitions

Quoted 3 lines · Benjamin Herrenschmidt

We were initializing unused ones and missing some

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#56 lore raw

PATCH 20/77 ppc: Fix generation if ISI/DSI vs. HV mode

In reply to Benjamin Herrenschmidt

Under some circumstances, we need to direct ISI and DSI interrupts at the hypervisor, turning them into HISI/HDSI, and using different SPRs (HDSISR and HDAR) depending on the combination of MSR_DR and the corresponding VPM bits in LPCR.

This moves part of the code into helpers that are fixed to select the right exception type and registers. On pre-P7 processors, LPCR is 0 which provides the old behaviour of directing the interrupts at the supervisor.

Thanks to Andrei Warkentin for finding a bug when HV=1

---
 target-ppc/mmu-hash64.c | 66 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 47 insertions(+), 19 deletions(-)
target-ppc/mmu-hash64.c +47 -19
index 71e1d14..e489fa4 100644
@@ -466,6 +466,44 @@ static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte,
     return (rpn & ~mask) | (eaddr & mask);
 }
 
+static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, uint64_t error_code)
+{
+    bool vpm;
+
+    if (msr_ir) {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
+    } else {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+    }
+    if (vpm && !msr_hv) {
+        cs->exception_index = POWERPC_EXCP_HISI;
+    } else {
+        cs->exception_index = POWERPC_EXCP_ISI;
+    }
+    env->error_code = error_code;
+}
+
+static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, uint64_t dsisr)
+{
+    bool vpm;
+
+    if (msr_dr) {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
+    } else {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+    }
+    if (vpm && msr_hv) {
+        cs->exception_index = POWERPC_EXCP_HDSI;
+        env->spr[SPR_HDAR] = dar;
+        env->spr[SPR_HDSISR] = dsisr;
+    } else {
+        cs->exception_index = POWERPC_EXCP_DSI;
+        env->spr[SPR_DAR] = dar;
+        env->spr[SPR_DSISR] = dsisr;
+   }
+    env->error_code = 0;
+}
+
 int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
                                 int rwx, int mmu_idx)
 {
@@ -475,7 +513,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
     hwaddr pte_offset;
     ppc_hash_pte64_t pte;
     int pp_prot, amr_prot, prot;
-    uint64_t new_pte1;
+    uint64_t new_pte1, dsisr;
     const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
     hwaddr raddr;
 
@@ -509,26 +547,21 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
 
     /* 3. Check for segment level no-execute violation */
     if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
-        cs->exception_index = POWERPC_EXCP_ISI;
-        env->error_code = 0x10000000;
+        ppc_hash64_set_isi(cs, env, 0x10000000);
         return 1;
     }
 
     /* 4. Locate the PTE in the hash table */
     pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte);
     if (pte_offset == -1) {
+        dsisr = 0x40000000;
         if (rwx == 2) {
-            cs->exception_index = POWERPC_EXCP_ISI;
-            env->error_code = 0x40000000;
+            ppc_hash64_set_isi(cs, env, dsisr);
         } else {
-            cs->exception_index = POWERPC_EXCP_DSI;
-            env->error_code = 0;
-            env->spr[SPR_DAR] = eaddr;
             if (rwx == 1) {
-                env->spr[SPR_DSISR] = 0x42000000;
-            } else {
-                env->spr[SPR_DSISR] = 0x40000000;
+                dsisr |= 0x02000000;
             }
+            ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
         }
         return 1;
     }
@@ -545,14 +578,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
         /* Access right violation */
         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
         if (rwx == 2) {
-            cs->exception_index = POWERPC_EXCP_ISI;
-            env->error_code = 0x08000000;
+            ppc_hash64_set_isi(cs, env, 0x08000000);
         } else {
-            target_ulong dsisr = 0;
-
-            cs->exception_index = POWERPC_EXCP_DSI;
-            env->error_code = 0;
-            env->spr[SPR_DAR] = eaddr;
+            dsisr = 0;
             if (need_prot[rwx] & ~pp_prot) {
                 dsisr |= 0x08000000;
             }
@@ -562,7 +590,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
             if (need_prot[rwx] & ~amr_prot) {
                 dsisr |= 0x00200000;
             }
-            env->spr[SPR_DSISR] = dsisr;
+            ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
         }
         return 1;
     }
Signature · 1 line
2.5.0
Reviewed-by on v1 20/77
#57 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 20/77] ppc: Fix generation if ISI/DSI vs. HV mode

Quoted 5 lines · Benjamin Herrenschmidt

Under some circumstances, we need to direct ISI and DSI interrupts at the hypervisor, turning them into HISI/HDSI, and using different SPRs (HDSISR and HDAR) depending on the combination of MSR_DR and the corresponding VPM bits in LPCR.

Quoted 8 lines

This moves part of the code into helpers that are fixed to select the right exception type and registers. On pre-P7 processors, LPCR is 0 which provides the old behaviour of directing the interrupts at the supervisor.

Thanks to Andrei Warkentin for finding a bug when HV=1

Quoted 120 lines
---
 target-ppc/mmu-hash64.c | 66 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 47 insertions(+), 19 deletions(-)
target-ppc/mmu-hash64.c +47 -19
index 71e1d14..e489fa4 100644
@@ -466,6 +466,44 @@ static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte,
     return (rpn & ~mask) | (eaddr & mask);
 }
 
+static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, uint64_t error_code)
+{
+    bool vpm;
+
+    if (msr_ir) {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
+    } else {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+    }
+    if (vpm && !msr_hv) {
+        cs->exception_index = POWERPC_EXCP_HISI;
+    } else {
+        cs->exception_index = POWERPC_EXCP_ISI;
+    }
+    env->error_code = error_code;
+}
+
+static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, uint64_t dsisr)
+{
+    bool vpm;
+
+    if (msr_dr) {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
+    } else {
+        vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0);
+    }
+    if (vpm && msr_hv) {
+        cs->exception_index = POWERPC_EXCP_HDSI;
+        env->spr[SPR_HDAR] = dar;
+        env->spr[SPR_HDSISR] = dsisr;
+    } else {
+        cs->exception_index = POWERPC_EXCP_DSI;
+        env->spr[SPR_DAR] = dar;
+        env->spr[SPR_DSISR] = dsisr;
+   }
+    env->error_code = 0;
+}
+
 int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
                                 int rwx, int mmu_idx)
 {
@@ -475,7 +513,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
     hwaddr pte_offset;
     ppc_hash_pte64_t pte;
     int pp_prot, amr_prot, prot;
-    uint64_t new_pte1;
+    uint64_t new_pte1, dsisr;
     const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
     hwaddr raddr;
 
@@ -509,26 +547,21 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
 
     /* 3. Check for segment level no-execute violation */
     if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
-        cs->exception_index = POWERPC_EXCP_ISI;
-        env->error_code = 0x10000000;
+        ppc_hash64_set_isi(cs, env, 0x10000000);
         return 1;
     }
 
     /* 4. Locate the PTE in the hash table */
     pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte);
     if (pte_offset == -1) {
+        dsisr = 0x40000000;
         if (rwx == 2) {
-            cs->exception_index = POWERPC_EXCP_ISI;
-            env->error_code = 0x40000000;
+            ppc_hash64_set_isi(cs, env, dsisr);
         } else {
-            cs->exception_index = POWERPC_EXCP_DSI;
-            env->error_code = 0;
-            env->spr[SPR_DAR] = eaddr;
             if (rwx == 1) {
-                env->spr[SPR_DSISR] = 0x42000000;
-            } else {
-                env->spr[SPR_DSISR] = 0x40000000;
+                dsisr |= 0x02000000;
             }
+            ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
         }
         return 1;
     }
@@ -545,14 +578,9 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
         /* Access right violation */
         qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
         if (rwx == 2) {
-            cs->exception_index = POWERPC_EXCP_ISI;
-            env->error_code = 0x08000000;
+            ppc_hash64_set_isi(cs, env, 0x08000000);
         } else {
-            target_ulong dsisr = 0;
-
-            cs->exception_index = POWERPC_EXCP_DSI;
-            env->error_code = 0;
-            env->spr[SPR_DAR] = eaddr;
+            dsisr = 0;
             if (need_prot[rwx] & ~pp_prot) {
                 dsisr |= 0x08000000;
             }
@@ -562,7 +590,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
             if (need_prot[rwx] & ~amr_prot) {
                 dsisr |= 0x00200000;
             }
-            env->spr[SPR_DSISR] = dsisr;
+            ppc_hash64_set_dsi(cs, env, eaddr, dsisr);
         }
         return 1;
     }
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#58 lore raw

PATCH 21/77 ppc: Rework generation of priv and inval interrupts

In reply to Benjamin Herrenschmidt

Recent server processors use the Hypervisor Emulation Assistance interrupt for illegal instructions and *some* type of SPR accesses.

Also the code was always generating inval instructions even for priv violations due to setting the wrong flags

Finally, the checking for PR/HV was open coded everywhere.

This reworks it all, using little helper macros for checking, and adding the HV interrupt (which gets converted back to program check in the slow path of excp_helper.c on CPUs that don't want it).

---
 linux-user/main.c        |   1 +
 target-ppc/excp_helper.c |  19 ++
 target-ppc/translate.c   | 678 ++++++++++++++++++++---------------------------
 3 files changed, 302 insertions(+), 396 deletions(-)
linux-user/main.c +1 -0
index 8acfe0f..beb621f 100644
@@ -1654,6 +1654,7 @@ void cpu_loop(CPUPPCState *env)
             queue_signal(env, info.si_signo, &info);
             break;
         case POWERPC_EXCP_PROGRAM:  /* Program exception                     */
+        case POWERPC_EXCP_HV_EMU:   /* HV emulation                          */
             /* XXX: check this */
             switch (env->error_code & ~0xF) {
             case POWERPC_EXCP_FP:
target-ppc/excp_helper.c +19 -0
index 716b27b..80a70f4 100644
@@ -127,6 +127,19 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         ail = 0;
     }
 
+    /* Hypervisor emulation assistance interrupt only exists on server
+     * arch 2.05 server or later. We also don't want to generate it if
+     * we don't have HVB in msr_mask (PAPR mode).
+     */
+    if (excp == POWERPC_EXCP_HV_EMU
+#if defined(TARGET_PPC64)
+        && !((env->mmu_model & POWERPC_MMU_64) && (env->msr_mask & MSR_HVB))
+#endif /* defined(TARGET_PPC64) */
+
+    ) {
+        excp = POWERPC_EXCP_PROGRAM;
+    }
+
     switch (excp) {
     case POWERPC_EXCP_NONE:
         /* Should never happen */
@@ -249,6 +262,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
             break;
         }
         goto store_current;
+    case POWERPC_EXCP_HV_EMU:
+        srr0 = SPR_HSRR0;
+        srr1 = SPR_HSRR1;
+        new_msr |= (target_ulong)MSR_HVB;
+        new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+        goto store_current;
     case POWERPC_EXCP_FPU:       /* Floating-point unavailable exception     */
         goto store_current;
     case POWERPC_EXCP_SYSCALL:   /* System call exception                    */
target-ppc/translate.c +282 -396

1,376 lines not on this page · raw message

Signature · 1 line
2.5.0
#59 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 21/77] ppc: Rework generation of priv and inval interrupts

Quoted 13 lines · Benjamin Herrenschmidt

Recent server processors use the Hypervisor Emulation Assistance interrupt for illegal instructions and *some* type of SPR accesses.

Also the code was always generating inval instructions even for priv violations due to setting the wrong flags

Finally, the checking for PR/HV was open coded everywhere.

This reworks it all, using little helper macros for checking, and adding the HV interrupt (which gets converted back to program check in the slow path of excp_helper.c on CPUs that don't want it).

[snip]

Quoted 11 lines

static void spr_noaccess(DisasContext *ctx, int gprn, int sprn)

target-ppc/translate.c +1 -1
@@ -4340,7 +4350,7 @@ static inline void gen_op_mfspr(DisasContext *ctx)
                 printf("Trying to read privileged spr %d (0x%03x) at "
                        TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
             }
-            gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+            gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG);
         }
     } else {
         /* Not defined */

target-ppc/translate.c static inline void gen_op_mfspr(DisasContext *ctx) · line 4360

Quoted 3 lines
target-ppc/translate.c +0 -0
@@ -4348,7 +4358,25 @@ static inline void gen_op_mfspr(DisasContext *ctx)
                  TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
         printf("Trying to read invalid spr %d (0x%03x) at "
                TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);

So, I'm not 100% following the logic below, but it looks like the existing code used SPR_NOACCESS to mark things which generated a privilege exception compared to NULL for things which generated an invalid instruction exception. Using that encoding, can you simplify the logic here? Alternatively can you use the logic here to avoid the SPR_NOACESS encoding?

Quoted 9 lines
target-ppc/translate.c +8 -1
-        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+
+        /* The behaviour depends on MSR:PR and SPR# bit 0x10,
+         * it can generate a priv, a hv emu or a no-op
+         */
+        if (sprn & 0x10) {
+            if (ctx->pr) {
+                gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+            }

target-ppc/translate.c static inline void gen_op_mfspr(DisasContext *ctx) · line 4378

Quoted 10 lines
target-ppc/translate.c +10 -0
+        } else {
+            if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) {
+                gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+            }
+        }
+#if !defined(CONFIG_USER_ONLY)
+        /* HV priv */
+        if (ctx->spr_cb[sprn].hea_read) {
+            gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+        }

If you're in PR mode, and it's an SPR with an hea_read function and has the 0x10 bit set, won't this call gen_priv_exception twice?

I also see no path here which will call gen_inval_exception(), is that right? If you're in HV mode and it's a truly invalid SPRN, isn't that what you'd want?

Quoted 74 lines
target-ppc/translate.c +20 -19
+#endif
     }
 }
 
@@ -4395,13 +4423,9 @@ static void gen_mtcrf(DisasContext *ctx)
 #if defined(TARGET_PPC64)
 static void gen_mtmsrd(DisasContext *ctx)
 {
-#if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-        return;
-    }
+    CHK_SV;
+
+#if !defined(CONFIG_USER_ONLY)
     if (ctx->opcode & 0x00010000) {
         /* Special form that does not need any synchronisation */
         TCGv t0 = tcg_temp_new();
@@ -4420,20 +4444,16 @@ static void gen_mtmsrd(DisasContext *ctx)
         /* Note that mtmsr is not always defined as context-synchronizing */
         gen_stop_exception(ctx);
     }
-#endif
+#endif /* !defined(CONFIG_USER_ONLY) */
 }
-#endif
+#endif /* defined(TARGET_PPC64) */
 
 static void gen_mtmsr(DisasContext *ctx)
 {
-#if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-        return;
-    }
-    if (ctx->opcode & 0x00010000) {
+    CHK_SV;
+
+#if !defined(CONFIG_USER_ONLY)
+   if (ctx->opcode & 0x00010000) {
         /* Special form that does not need any synchronisation */
         TCGv t0 = tcg_temp_new();
         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
@@ -4488,7 +4508,7 @@ static void gen_mtspr(DisasContext *ctx)
                      TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
             printf("Trying to write privileged spr %d (0x%03x) at "
                    TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
-            gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+            gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG);
         }
     } else {
         /* Not defined */
@@ -4496,7 +4516,25 @@ static void gen_mtspr(DisasContext *ctx)
                  TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
         printf("Trying to write invalid spr %d (0x%03x) at "
                TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
-        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+
+        /* The behaviour depends on MSR:PR and SPR# bit 0x10,
+         * it can generate a priv, a hv emu or a no-op
+         */
+        if (sprn & 0x10) {
+            if (ctx->pr) {
+                gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+            }
+        } else {

target-ppc/translate.c static void gen_mtspr(DisasContext *ctx) · line 4537

Quoted 10 lines
target-ppc/translate.c +10 -0
+            if (ctx->pr || sprn == 0) {
+                gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+            }
+        }
+#if !defined(CONFIG_USER_ONLY)
+        /* HV priv */
+        if (ctx->spr_cb[sprn].hea_write) {
+            gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+        }
+#endif

Same concerns here as for mfspr.

[snip]

target-ppc/translate.c static void gen_slbmfev(DisasContext *ctx) · line 4880

Quoted 12 lines
target-ppc/translate.c +0 -0
 /* tlbiel */
 static void gen_tlbiel(DisasContext *ctx)
 {

#if defined(CONFIG_USER_ONLY)

target-ppc/translate.c +2 -5
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
 #else
-    if (unlikely(ctx->pr || !ctx->hv)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+    CHK_SV;

You have CHK_SV here, but the original code checks for HV, as does your new code for tlbia and tlbiel, is that right?

[snip]

Quoted 4 lines

/* tlbsync */
static void gen_tlbsync(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)

target-ppc/translate.c static void gen_tlbie(DisasContext *ctx) · line 4912

Quoted 10 lines
target-ppc/translate.c +4 -6
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+    GEN_PRIV;
+#else    
+    CHK_HV;
+

Old code didn't check for HV, mode, but AFAICT it should have, so this looks correct.

[snip]

Quoted 4 lines
target-ppc/translate.c +0 -0
@@ -5941,18 +5921,16 @@ static void gen_mfapidi(DisasContext *ctx)
 static void gen_tlbiva(DisasContext *ctx)
 {
 #if defined(CONFIG_USER_ONLY)

target-ppc/translate.c static void gen_mfapidi(DisasContext *ctx) · line 5928

Quoted 10 lines
target-ppc/translate.c +3 -5
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
 #else
     TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+
+    CHK_SV;

Is the same thing as tlbivax, or some ancient instruction? AFAICT the ISA says tlbivax is hypervisor privileged.

Quoted 7 lines
target-ppc/translate.c +1 -1
     t0 = tcg_temp_new();
     gen_addr_reg_index(ctx, t0);
     gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
     tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
 }

[snip]

target-ppc/translate.c static void gen_mfsri(DisasContext *ctx) · line 5767

Quoted 13 lines

static void gen_tlbivax_booke206(DisasContext *ctx) {
#if defined(CONFIG_USER_ONLY)

target-ppc/translate.c +2 -5
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
 #else
     TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
 
+    CHK_SV;

ISA says tlbivax is hypervisor privileged when the CPU has a hypervisor mode, which I guess booke206 probably doesn't?

Quoted 12 lines
     t0 = tcg_temp_new();
     gen_addr_reg_index(ctx, t0);
target-ppc/translate.c +1 -2
-
     gen_helper_booke206_tlbivax(cpu_env, t0);
     tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
 }
 
 static void gen_tlbilx_booke206(DisasContext *ctx)
 {
 #if defined(CONFIG_USER_ONLY)

target-ppc/translate.c static void gen_tlbsx_booke206(DisasContext *ctx) · line 6557

Quoted 10 lines
target-ppc/translate.c +2 -5
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
 #else
     TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
 
+    CHK_SV;

And apparently hv vs. sv privilege of tlbilx depends on the EPCR register. Again, may not be relevant for 2.06.

Quoted 10 lines
     t0 = tcg_temp_new();
     gen_addr_reg_index(ctx, t0);
target-ppc/translate.c +1 -1
@@ -6672,7 +6574,7 @@ static void gen_tlbilx_booke206(DisasContext *ctx)
     }
 
     tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
 }
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#60 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 21/77] ppc: Rework generation of priv and inval interrupts

Quoted 7 lines · David Gibson


So, I'm not 100% following the logic below, but it looks like the existing code used SPR_NOACCESS to mark things which generated a privilege exception compared to NULL for things which generated an invalid instruction exception.  Using that encoding, can you simplify the logic here?  Alternatively can you use the logic here to avoid the SPR_NOACESS encoding?

Well, so the SPR_NOACCESS has to do with how you react to a known SPR who has explicit access permissions. The logic below is described in the ISA for an unknown SPR number.

I don't know whether the access permission of "known" SPRs always honor the 0x10 bit trick, and changing that in qemu would be a fairly large patch. So I'd rather stick to the logic here for "unknown" SPRs which matches the ISA definition.

I'll update the patch though for arch 2.07 as it defines a few reserved SPRs as no-ops.

However:

Quoted 12 lines
Quoted 12 lines

-        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR); +
+        /* The behaviour depends on MSR:PR and SPR# bit 0x10, +         * it can generate a priv, a hv emu or a no-op +         */
+        if (sprn & 0x10) {
+            if (ctx->pr) {
+                gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); +            }
+        } else {
+            if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) {

Quoted 8 lines
Quoted 8 lines

+                gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); +            }
+        }
+#if !defined(CONFIG_USER_ONLY)
+        /* HV priv */
+        if (ctx->spr_cb[sprn].hea_read) {
+            gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); +        }

That latest bit is bogus.

Quoted 2 lines

If you're in PR mode, and it's an SPR with an hea_read function and has the 0x10 bit set, won't this call gen_priv_exception twice?

Yes, I've removed it. It should be handled by the SPR_NOACCESS.

Quoted 5 lines

I also see no path here which will call gen_inval_exception(), is that
right?  If you're in HV mode and it's a truly invalid SPRN, isn't that
what you'd want?

No, the ISA says it's a nop.

Cheers,
Ben.

Quoted 212 lines
Quoted 78 lines

+#endif
     }
 }

target-ppc/translate.c +3 -7
@@ -4395,13 +4423,9 @@ static void gen_mtcrf(DisasContext *ctx)
  #if defined(TARGET_PPC64)
  static void gen_mtmsrd(DisasContext *ctx)
  {
-#if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-        return;
-    }
+    CHK_SV;
+
+#if !defined(CONFIG_USER_ONLY)
      if (ctx->opcode & 0x00010000) {
          /* Special form that does not need any synchronisation */
          TCGv t0 = tcg_temp_new();
@@ -4420,20 +4444,16 @@ static void gen_mtmsrd(DisasContext *ctx)
          /* Note that mtmsr is not always defined as context-

synchronizing */
         gen_stop_exception(ctx);
     }

target-ppc/translate.c +1 -1
-#endif
+#endif /* !defined(CONFIG_USER_ONLY) */

 }

(quoted diff) +1 -1
-#endif
+#endif /* defined(TARGET_PPC64) */


 static void gen_mtmsr(DisasContext *ctx)
 {

target-ppc/translate.c +4 -8
-#if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
-        return;
-    }
-    if (ctx->opcode & 0x00010000) {
+    CHK_SV;
+
+#if !defined(CONFIG_USER_ONLY)
+   if (ctx->opcode & 0x00010000) {

         /* Special form that does not need any synchronisation */ TCGv t0 = tcg_temp_new();
         tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));

(quoted diff) +0 -0
@@ -4488,7 +4508,7 @@ static void gen_mtspr(DisasContext *ctx)

                      TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);
             printf("Trying to write privileged spr %d (0x%03x) at "
                    TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); -            gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); +            gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); }
     } else {
         /* Not defined */

(quoted diff) +0 -0
@@ -4496,7 +4516,25 @@ static void gen_mtspr(DisasContext *ctx)
                   TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4);

         printf("Trying to write invalid spr %d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); -        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);

target-ppc/translate.c +9 -0
+
+        /* The behaviour depends on MSR:PR and SPR# bit 0x10,
+         * it can generate a priv, a hv emu or a no-op
+         */
+        if (sprn & 0x10) {
+            if (ctx->pr) {
+                gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+            }
+        } else {

target-ppc/translate.c static void gen_mtspr(DisasContext *ctx) · line 4537

Quoted 10 lines
target-ppc/translate.c +10 -0
+            if (ctx->pr || sprn == 0) {
+                gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+            }
+        }
+#if !defined(CONFIG_USER_ONLY)
+        /* HV priv */
+        if (ctx->spr_cb[sprn].hea_write) {
+            gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+        }
+#endif

Same concerns here as for mfspr.

[snip]

Quoted 4 lines

 /* tlbiel */
 static void gen_tlbiel(DisasContext *ctx)
 {
 #if defined(CONFIG_USER_ONLY)

Quoted 8 lines

-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;
 #else
-    if (unlikely(ctx->pr || !ctx->hv)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }
+    CHK_SV;

You have CHK_SV here, but the original code checks for HV, as does your new code for tlbia and tlbiel, is that right?

[snip]

Quoted 5 lines

 /* tlbsync */
 static void gen_tlbsync(DisasContext *ctx)
 {
 #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);

target-ppc/translate.c static void gen_tlbie(DisasContext *ctx) · line 4912

Quoted 9 lines
target-ppc/translate.c +4 -5
-#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+    GEN_PRIV;
+#else    
+    CHK_HV;
+

Old code didn't check for HV, mode, but AFAICT it should have, so this
looks correct.

[snip]

Quoted 4 lines
target-ppc/translate.c +0 -0
@@ -5941,18 +5921,16 @@ static void gen_mfapidi(DisasContext *ctx)
  static void gen_tlbiva(DisasContext *ctx)
  {
  #if defined(CONFIG_USER_ONLY)

target-ppc/translate.c static void gen_mfapidi(DisasContext *ctx) · line 5928

Quoted 10 lines
target-ppc/translate.c +3 -5
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
  #else
      TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+
+    CHK_SV;

Is the same thing as tlbivax, or some ancient instruction?  AFAICT the
ISA says tlbivax is hypervisor privileged.

Quoted 7 lines
target-ppc/translate.c +1 -1
      t0 = tcg_temp_new();
      gen_addr_reg_index(ctx, t0);
      gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
      tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
  }

[snip]

Quoted 5 lines

 static void gen_tlbivax_booke206(DisasContext *ctx) {
 #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;

Quoted 8 lines

 #else
     TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }

+    CHK_SV;

ISA says tlbivax is hypervisor privileged when the CPU has a hypervisor mode, which I guess booke206 probably doesn't?

Quoted 14 lines

     t0 = tcg_temp_new();
     gen_addr_reg_index(ctx, t0);
-
     gen_helper_booke206_tlbivax(cpu_env, t0);
     tcg_temp_free(t0);

(quoted diff) +1 -1
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */

 }

 static void gen_tlbilx_booke206(DisasContext *ctx) {
 #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;

Quoted 8 lines

 #else
     TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }

+    CHK_SV;

And apparently hv vs. sv privilege of tlbilx depends on the EPCR register.  Again, may not be relevant for 2.06.

Quoted 11 lines

     t0 = tcg_temp_new();
     gen_addr_reg_index(ctx, t0);

(quoted diff) +0 -0
@@ -6672,7 +6574,7 @@ static void gen_tlbilx_booke206(DisasContext
 *ctx)
      }


     tcg_temp_free(t0);

(quoted diff) +1 -1
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */

 }

#61 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 21/77] ppc: Rework generation of priv and inval interrupts

Quoted 13 lines · Benjamin Herrenschmidt
Quoted 7 lines · David Gibson


So, I'm not 100% following the logic below, but it looks like the existing code used SPR_NOACCESS to mark things which generated a privilege exception compared to NULL for things which generated an invalid instruction exception.  Using that encoding, can you simplify the logic here?  Alternatively can you use the logic here to avoid the SPR_NOACESS encoding?

Well, so the SPR_NOACCESS has to do with how you react to a known SPR who has explicit access permissions. The logic below is described in the ISA for an unknown SPR number.

Quoted 7 lines

I don't know whether the access permission of "known" SPRs always honor the 0x10 bit trick, and changing that in qemu would be a fairly large patch. So I'd rather stick to the logic here for "unknown" SPRs which matches the ISA definition.

I'll update the patch though for arch 2.07 as it defines a few reserved SPRs as no-ops.

Ok, that makes sense.

Quoted 30 lines

However:

Quoted 12 lines
Quoted 12 lines

-        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR); +
+        /* The behaviour depends on MSR:PR and SPR# bit 0x10, +         * it can generate a priv, a hv emu or a no-op +         */
+        if (sprn & 0x10) {
+            if (ctx->pr) {
+                gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); +            }
+        } else {
+            if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) {

Quoted 8 lines
Quoted 8 lines

+                gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); +            }
+        }
+#if !defined(CONFIG_USER_ONLY)
+        /* HV priv */
+        if (ctx->spr_cb[sprn].hea_read) {
+            gen_priv_exception(ctx, POWERPC_EXCP_INVAL_SPR); +        }

That latest bit is bogus.

Quoted 2 lines

If you're in PR mode, and it's an SPR with an hea_read function and has the 0x10 bit set, won't this call gen_priv_exception twice?

Yes, I've removed it. It should be handled by the SPR_NOACCESS.

Quoted 7 lines
Quoted 5 lines

I also see no path here which will call gen_inval_exception(), is that
right?  If you're in HV mode and it's a truly invalid SPRN, isn't that
what you'd want?

No, the ISA says it's a nop.

Huh, ok.

Some comments referencing the ISA might be useful here.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#62 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 21/77] ppc: Rework generation of priv and inval interrupts

In reply to David Gibson
Quoted 14 lines · David Gibson

snip]

Quoted 4 lines

  /* tlbiel */
  static void gen_tlbiel(DisasContext *ctx)
  {
  #if defined(CONFIG_USER_ONLY)

Quoted 8 lines

-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;
  #else
-    if (unlikely(ctx->pr || !ctx->hv)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }
+    CHK_SV;

Quoted 2 lines

You have CHK_SV here, but the original code checks for HV, as does your new code for tlbia and tlbiel, is that right?

Yes. tlbiel is supervisor accessible (for weird reasons).

Quoted 10 lines

[snip]

Quoted 9 lines

  /* tlbsync */
  static void gen_tlbsync(DisasContext *ctx)
  {
  #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;

Quoted 8 lines

target-ppc/translate.c static void gen_tlbie(DisasContext *ctx) · line 4912

Quoted 5 lines

-    }
+    GEN_PRIV;

target-ppc/translate.c +3 -0
+#else    
+    CHK_HV;
+

Old code didn't check for HV, mode, but AFAICT it should have, so this looks correct.

Yes, this is a hypervisor instruction.

Quoted 10 lines

[snip]

Quoted 9 lines
target-ppc/translate.c +1 -2
@@ -5941,18 +5921,16 @@ static void gen_mfapidi(DisasContext *ctx)
   static void gen_tlbiva(DisasContext *ctx)
   {
   #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
   #else
       TCGv t0;
-    if (unlikely(ctx->pr)) {
Quoted 8 lines

target-ppc/translate.c static void gen_dcbf(DisasContext *ctx) · line 4563

Quoted 5 lines
target-ppc/translate.c +2 -3
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+
+    CHK_SV;

Is the same thing as tlbivax, or some ancient instruction?  AFAICT the ISA says tlbivax is hypervisor privileged.

"tlbiva" is the 4xx variant, there is no hypervisor mode on these things.

Quoted 23 lines
Quoted 7 lines
target-ppc/translate.c +1 -1
       t0 = tcg_temp_new();
       gen_addr_reg_index(ctx, t0);
       gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
       tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
   }

[snip]

Quoted 5 lines

  static void gen_tlbivax_booke206(DisasContext *ctx) {
  #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;

Quoted 8 lines

  #else
      TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }

+    CHK_SV;

Quoted 2 lines

ISA says tlbivax is hypervisor privileged when the CPU has a hypervisor mode, which I guess booke206 probably doesn't?

Right so here, the "problem" is that afaik, TCG doesn't implement the BookE hypervisor mode. So with my limited BookE testing ability I prefer sticking to a mechanical replacement that matches the existing code. It can be fixed later if necessary.

Quoted 17 lines
Quoted 17 lines

      t0 = tcg_temp_new();
      gen_addr_reg_index(ctx, t0);
-
      gen_helper_booke206_tlbivax(cpu_env, t0);
      tcg_temp_free(t0);

(quoted diff) +1 -1
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */

  }

  static void gen_tlbilx_booke206(DisasContext *ctx) {
  #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;
  #else
      TCGv t0;
-    if (unlikely(ctx->pr)) {

Quoted 8 lines
Quoted 5 lines

-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }

+    CHK_SV;

And apparently hv vs. sv privilege of tlbilx depends on the EPCR register.  Again, may not be relevant for 2.06.

Well, here too, I basically preserve existing BookE TCG behaviour, whether it's correct or not. That can be fixed separately if somebody cares about BookE HV mode.

Quoted 12 lines

Review static void gen_tlbilx_booke206(DisasContext

Quoted 4 lines

      t0 = tcg_temp_new();
      gen_addr_reg_index(ctx, t0);

(quoted diff) +0 -0
@@ -6672,7 +6574,7 @@ static void gen_tlbilx_booke206(DisasContext

*ctx)

Quoted 6 lines
(quoted diff) +0 -0
       }


      tcg_temp_free(t0);

(quoted diff) +1 -1
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */

  }

#63 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 21/77] ppc: Rework generation of priv and inval interrupts

Quoted 86 lines · Benjamin Herrenschmidt
Quoted 14 lines · David Gibson

snip]

Quoted 4 lines

  /* tlbiel */
  static void gen_tlbiel(DisasContext *ctx)
  {
  #if defined(CONFIG_USER_ONLY)

Quoted 8 lines

-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;
  #else
-    if (unlikely(ctx->pr || !ctx->hv)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }
+    CHK_SV;

Quoted 2 lines

You have CHK_SV here, but the original code checks for HV, as does your new code for tlbia and tlbiel, is that right?

Yes. tlbiel is supervisor accessible (for weird reasons).

Quoted 10 lines

[snip]

Quoted 9 lines

  /* tlbsync */
  static void gen_tlbsync(DisasContext *ctx)
  {
  #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -#else
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;

Quoted 8 lines

target-ppc/translate.c static void gen_tlbie(DisasContext *ctx) · line 4912

Quoted 5 lines

-    }
+    GEN_PRIV;

target-ppc/translate.c +3 -0
+#else    
+    CHK_HV;
+

Old code didn't check for HV, mode, but AFAICT it should have, so this looks correct.

Yes, this is a hypervisor instruction.

Quoted 10 lines

[snip]

Quoted 9 lines
target-ppc/translate.c +1 -2
@@ -5941,18 +5921,16 @@ static void gen_mfapidi(DisasContext *ctx)
   static void gen_tlbiva(DisasContext *ctx)
   {
   #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+    GEN_PRIV;
   #else
       TCGv t0;
-    if (unlikely(ctx->pr)) {
Quoted 8 lines

target-ppc/translate.c static void gen_dcbf(DisasContext *ctx) · line 4563

Quoted 5 lines
target-ppc/translate.c +2 -3
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
-        return;
-    }
+
+    CHK_SV;

Is the same thing as tlbivax, or some ancient instruction?  AFAICT the ISA says tlbivax is hypervisor privileged.

"tlbiva" is the 4xx variant, there is no hypervisor mode on these things.

Quoted 23 lines
Quoted 7 lines
target-ppc/translate.c +1 -1
       t0 = tcg_temp_new();
       gen_addr_reg_index(ctx, t0);
       gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]);
       tcg_temp_free(t0);
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */
   }

[snip]

Quoted 13 lines

  static void gen_tlbivax_booke206(DisasContext *ctx) {
  #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;
  #else
      TCGv t0;
-    if (unlikely(ctx->pr)) {
-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }

+    CHK_SV;

Quoted 7 lines
Quoted 2 lines

ISA says tlbivax is hypervisor privileged when the CPU has a hypervisor mode, which I guess booke206 probably doesn't?

Right so here, the "problem" is that afaik, TCG doesn't implement the BookE hypervisor mode. So with my limited BookE testing ability I prefer sticking to a mechanical replacement that matches the existing code. It can be fixed later if necessary.

Fair enough.

Quoted 43 lines
Quoted 17 lines
Quoted 17 lines

      t0 = tcg_temp_new();
      gen_addr_reg_index(ctx, t0);
-
      gen_helper_booke206_tlbivax(cpu_env, t0);
      tcg_temp_free(t0);

(quoted diff) +1 -1
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */

  }

  static void gen_tlbilx_booke206(DisasContext *ctx) {
  #if defined(CONFIG_USER_ONLY)
-    gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); +    GEN_PRIV;
  #else
      TCGv t0;
-    if (unlikely(ctx->pr)) {

Quoted 8 lines
Quoted 5 lines

-        gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); -        return;
-    }

+    CHK_SV;

And apparently hv vs. sv privilege of tlbilx depends on the EPCR register.  Again, may not be relevant for 2.06.

Well, here too, I basically preserve existing BookE TCG behaviour, whether it's correct or not. That can be fixed separately if somebody cares about BookE HV mode.

Quoted 12 lines

Review static void gen_tlbilx_booke206(DisasContext

Quoted 4 lines

      t0 = tcg_temp_new();
      gen_addr_reg_index(ctx, t0);

(quoted diff) +0 -0
@@ -6672,7 +6574,7 @@ static void gen_tlbilx_booke206(DisasContext

*ctx)

Quoted 6 lines
(quoted diff) +0 -0
       }


      tcg_temp_free(t0);

(quoted diff) +1 -1
-#endif
+#endif /* defined(CONFIG_USER_ONLY) */

  }

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#64 lore raw

PATCH 22/77 ppc: Add real mode CI load/store instructions for P7 and P8

In reply to Benjamin Herrenschmidt

Those instructions are only available in hypervisor real mode and allow cache inhibited garded access to devices in that mode.

---
 target-ppc/cpu.h            |  4 +++-
 target-ppc/translate.c      | 56 +++++++++++++++++++++++++++++++++++----------
 target-ppc/translate_init.c |  6 +++--
 3 files changed, 51 insertions(+), 15 deletions(-)
target-ppc/cpu.h +3 -1
index 23479b1..3d22a4f 100644
@@ -1913,6 +1913,8 @@ enum {
     PPC_POPCNTB        = 0x0000000000001000ULL,
     /*   string load / store                                                 */
     PPC_STRING         = 0x0000000000002000ULL,
+    /*   real mode cache inhibited load / store                              */
+    PPC_CILDST         = 0x0000000000004000ULL,
 
     /* Floating-point unit extensions                                        */
     /*   Optional floating point instructions                                */
@@ -2027,7 +2029,7 @@ enum {
                         | PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \
                         | PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \
                         | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \
-                        | PPC_POPCNTWD)
+                        | PPC_POPCNTWD | PPC_CILDST)
 
     /* extended type values */
 
target-ppc/translate.c +44 -12

162 lines not on this page · raw message

target-ppc/translate_init.c +4 -2
index 5210b25..8d82bc8 100644
@@ -8320,7 +8320,8 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
                        PPC_64B | PPC_64H | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
-                       PPC_POPCNTB | PPC_POPCNTWD;
+                       PPC_POPCNTB | PPC_POPCNTWD |
+                       PPC_CILDST;
     pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
                         PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
                         PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
@@ -8397,7 +8398,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
                        PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
-                       PPC_POPCNTB | PPC_POPCNTWD;
+                       PPC_POPCNTB | PPC_POPCNTWD |
+                       PPC_CILDST;
     pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
                         PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
                         PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
Signature · 1 line
2.5.0
#65 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 22/77] ppc: Add real mode CI load/store instructions for P7 and P8

Quoted 46 lines · Benjamin Herrenschmidt

Those instructions are only available in hypervisor real mode and allow cache inhibited garded access to devices in that mode.

---
 target-ppc/cpu.h            |  4 +++-
 target-ppc/translate.c      | 56 +++++++++++++++++++++++++++++++++++----------
 target-ppc/translate_init.c |  6 +++--
 3 files changed, 51 insertions(+), 15 deletions(-)
target-ppc/cpu.h +3 -1
index 23479b1..3d22a4f 100644
@@ -1913,6 +1913,8 @@ enum {
     PPC_POPCNTB        = 0x0000000000001000ULL,
     /*   string load / store                                                 */
     PPC_STRING         = 0x0000000000002000ULL,
+    /*   real mode cache inhibited load / store                              */
+    PPC_CILDST         = 0x0000000000004000ULL,
 
     /* Floating-point unit extensions                                        */
     /*   Optional floating point instructions                                */
@@ -2027,7 +2029,7 @@ enum {
                         | PPC_MFAPIDI | PPC_TLBIVA | PPC_TLBIVAX \
                         | PPC_4xx_COMMON | PPC_40x_ICBT | PPC_RFMCI \
                         | PPC_RFDI | PPC_DCR | PPC_DCRX | PPC_DCRUX \
-                        | PPC_POPCNTWD)
+                        | PPC_POPCNTWD | PPC_CILDST)
 
     /* extended type values */
 
target-ppc/translate.c +1 -1
index 3f657b1..4d01fd0 100644
@@ -189,7 +189,7 @@ struct DisasContext {
     uint32_t opcode;
     uint32_t exception;
     /* Routine used to access memory */
-    bool pr, hv;
+    bool pr, hv, dr;
     int mem_idx;
     int access_type;
     /* Translation flags */

target-ppc/translate.c typedef struct opcode_t { · line 383

Quoted 4 lines
target-ppc/translate.c +1 -0
@@ -380,9 +380,11 @@ typedef struct opcode_t {
 #if defined(CONFIG_USER_ONLY)
 #define CHK_HV GEN_PRIV
 #define CHK_SV GEN_PRIV
+#define CHK_HVDR GEN_PRIV

I'm guessing this is supposed to be CHK_HVRM as below.

target-ppc/translate.c static void glue(gen_, name##u)(DisasContext *ctx) · line 2892

Quoted 13 lines
target-ppc/translate.c +2 -1
 #else
 #define CHK_HV do { if (unlikely(ctx->pr || !ctx->hv)) GEN_PRIV; } while(0)
 #define CHK_SV do { if (unlikely(ctx->pr))  GEN_PRIV; }  while(0)
+#define CHK_HVRM do { if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) GEN_PRIV; } while(0)
 #endif
 
 #define CHK_NONE
@@ -2887,7 +2889,7 @@ static void glue(gen_, name##u)(DisasContext *ctx)
 }
 
 #define GEN_LDUX(name, ldop, opc2, opc3, type)                                \
-static void glue(gen_, name##ux)(DisasContext *ctx)                                   \
+static void glue(gen_, name##ux)(DisasContext *ctx)                           \

Extraneous change.

Quoted 174 lines
target-ppc/translate.c +40 -10
 {                                                                             \
     TCGv EA;                                                                  \
     if (unlikely(rA(ctx->opcode) == 0 ||                                      \
@@ -2903,18 +2905,23 @@ static void glue(gen_, name##ux)(DisasContext *ctx)
     tcg_temp_free(EA);                                                        \
 }
 
-#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2)                        \
+#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk)                   \
 static void glue(gen_, name##x)(DisasContext *ctx)                            \
 {                                                                             \
     TCGv EA;                                                                  \
+    chk;                                                                      \
     gen_set_access_type(ctx, ACCESS_INT);                                     \
     EA = tcg_temp_new();                                                      \
     gen_addr_reg_index(ctx, EA);                                              \
     gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA);                       \
     tcg_temp_free(EA);                                                        \
 }
+
 #define GEN_LDX(name, ldop, opc2, opc3, type)                                 \
-    GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE)
+    GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_NONE)
+
+#define GEN_LDX_HVRM(name, ldop, opc2, opc3, type)                            \
+    GEN_LDX_E(name, ldop, opc2, opc3, type, PPC_NONE, CHK_HVRM)
 
 #define GEN_LDS(name, ldop, op, type)                                         \
 GEN_LD(name, ldop, op | 0x20, type);                                          \
@@ -2940,6 +2947,12 @@ GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B);
 /* ldx */
 GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B);
 
+/* CI load/store variants */
+GEN_LDX_HVRM(ldcix, ld64, 0x15, 0x1b, PPC_CILDST)
+GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x15, PPC_CILDST)
+GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST)
+GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST)
+
 static void gen_ld(DisasContext *ctx)
 {
     TCGv EA;
@@ -3058,10 +3071,11 @@ static void glue(gen_, name##ux)(DisasContext *ctx)
     tcg_temp_free(EA);                                                        \
 }
 
-#define GEN_STX_E(name, stop, opc2, opc3, type, type2)                        \
+#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk)                   \
 static void glue(gen_, name##x)(DisasContext *ctx)                            \
 {                                                                             \
     TCGv EA;                                                                  \
+    chk;                                                                      \
     gen_set_access_type(ctx, ACCESS_INT);                                     \
     EA = tcg_temp_new();                                                      \
     gen_addr_reg_index(ctx, EA);                                              \
@@ -3069,7 +3083,10 @@ static void glue(gen_, name##x)(DisasContext *ctx)                            \
     tcg_temp_free(EA);                                                        \
 }
 #define GEN_STX(name, stop, opc2, opc3, type)                                 \
-    GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE)
+    GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_NONE)
+
+#define GEN_STX_HVRM(name, stop, opc2, opc3, type)                            \
+    GEN_STX_E(name, stop, opc2, opc3, type, PPC_NONE, CHK_HVRM)
 
 #define GEN_STS(name, stop, op, type)                                         \
 GEN_ST(name, stop, op | 0x20, type);                                          \
@@ -3086,6 +3103,10 @@ GEN_STS(stw, st32, 0x04, PPC_INTEGER);
 #if defined(TARGET_PPC64)
 GEN_STUX(std, st64, 0x15, 0x05, PPC_64B);
 GEN_STX(std, st64, 0x15, 0x04, PPC_64B);
+GEN_STX_HVRM(stdcix, st64, 0x15, 0x1f, PPC_CILDST)
+GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST)
+GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST)
+GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST)
 
 static void gen_std(DisasContext *ctx)
 {
@@ -3171,7 +3192,7 @@ static inline void gen_qemu_ld64ur(DisasContext *ctx, TCGv arg1, TCGv arg2)
     TCGMemOp op = MO_Q | (ctx->default_tcg_memop_mask ^ MO_BSWAP);
     tcg_gen_qemu_ld_i64(arg1, arg2, ctx->mem_idx, op);
 }
-GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX);
+GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE);
 #endif  /* TARGET_PPC64 */
 
 /* sthbrx */
@@ -3197,7 +3218,7 @@ static inline void gen_qemu_st64r(DisasContext *ctx, TCGv arg1, TCGv arg2)
     TCGMemOp op = MO_Q | (ctx->default_tcg_memop_mask ^ MO_BSWAP);
     tcg_gen_qemu_st_i64(arg1, arg2, ctx->mem_idx, op);
 }
-GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX);
+GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE);
 #endif  /* TARGET_PPC64 */
 
 /***                    Integer load and store multiple                    ***/
@@ -10156,7 +10177,7 @@ GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
 GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type),
 #define GEN_LDUX(name, ldop, opc2, opc3, type)                                \
 GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
-#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2)                        \
+#define GEN_LDX_E(name, ldop, opc2, opc3, type, type2, chk)                   \
 GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2),
 #define GEN_LDS(name, ldop, op, type)                                         \
 GEN_LD(name, ldop, op | 0x20, type)                                           \
@@ -10173,7 +10194,13 @@ GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B)
 GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B)
 GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B)
 GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B)
-GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX)
+GEN_LDX_E(ldbr, ld64ur, 0x14, 0x10, PPC_NONE, PPC2_DBRX, CHK_NONE)
+
+/* HV/P7 and later only */
+GEN_LDX_HVRM(ldcix, ld64, 0x15, 0x1b, PPC_CILDST)
+GEN_LDX_HVRM(lwzcix, ld32u, 0x15, 0x18, PPC_CILDST)
+GEN_LDX_HVRM(lhzcix, ld16u, 0x15, 0x19, PPC_CILDST)
+GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST)
 #endif
 GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER)
 GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER)
@@ -10189,7 +10216,7 @@ GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
 GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type),
 #define GEN_STUX(name, stop, opc2, opc3, type)                                \
 GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
-#define GEN_STX_E(name, stop, opc2, opc3, type, type2)                        \
+#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk)                   \
 GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2),
 #define GEN_STS(name, stop, op, type)                                         \
 GEN_ST(name, stop, op | 0x20, type)                                           \
@@ -10203,7 +10230,11 @@ GEN_STS(stw, st32, 0x04, PPC_INTEGER)
 #if defined(TARGET_PPC64)
 GEN_STUX(std, st64, 0x15, 0x05, PPC_64B)
 GEN_STX(std, st64, 0x15, 0x04, PPC_64B)
-GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX)
+GEN_STX_E(stdbr, st64r, 0x14, 0x14, PPC_NONE, PPC2_DBRX, CHK_NONE)
+GEN_STX_HVRM(stdcix, st64, 0x15, 0x1f, PPC_CILDST)
+GEN_STX_HVRM(stwcix, st32, 0x15, 0x1c, PPC_CILDST)
+GEN_STX_HVRM(sthcix, st16, 0x15, 0x1d, PPC_CILDST)
+GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST)
 #endif
 GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER)
 GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER)
@@ -11369,6 +11400,7 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.spr_cb = env->spr_cb;
     ctx.pr = msr_pr;
     ctx.mem_idx = env->dmmu_idx;
+    ctx.dr = msr_dr;
 #if !defined(CONFIG_USER_ONLY)
     ctx.hv = !msr_pr && (msr_hv || !env->has_hv_mode);
 #endif
target-ppc/translate_init.c +4 -2
index 5210b25..8d82bc8 100644
@@ -8320,7 +8320,8 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
                        PPC_64B | PPC_64H | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
-                       PPC_POPCNTB | PPC_POPCNTWD;
+                       PPC_POPCNTB | PPC_POPCNTWD |
+                       PPC_CILDST;
     pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
                         PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
                         PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
@@ -8397,7 +8398,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                        PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
                        PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC |
                        PPC_SEGMENT_64B | PPC_SLBI |
-                       PPC_POPCNTB | PPC_POPCNTWD;
+                       PPC_POPCNTB | PPC_POPCNTWD |
+                       PPC_CILDST;
     pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
                         PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
                         PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#66 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 22/77] ppc: Add real mode CI load/store instructions for P7 and P8

Quoted 8 lines · David Gibson

target-ppc/translate.c typedef struct opcode_t { · line 383

Quoted 6 lines
target-ppc/translate.c +1 -0
@@ -380,9 +380,11 @@ typedef struct opcode_t {
  #if defined(CONFIG_USER_ONLY)
  #define CHK_HV GEN_PRIV
  #define CHK_SV GEN_PRIV
+#define CHK_HVDR GEN_PRIV

I'm guessing this is supposed to be CHK_HVRM as below.

Indeed. I had test built ... odd.

Quoted 7 lines
Quoted 7 lines
target-ppc/translate.c +1 -0
  #else
  #define CHK_HV do { if (unlikely(ctx->pr || !ctx->hv)) GEN_PRIV; } while(0)
  #define CHK_SV do { if (unlikely(ctx->pr))  GEN_PRIV; }  while(0)
+#define CHK_HVRM do { if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) GEN_PRIV; } while(0)
  #endif


 #define CHK_NONE

Quoted 8 lines

target-ppc/translate.c static void glue(gen_, name##u)(DisasContext *ctx) · line 2892

Quoted 6 lines
(quoted diff) +0 -0
@@ -2887,7 +2889,7 @@ static void glue(gen_, name##u)(DisasContext *ctx)
  }


 #define GEN_LDUX(name, ldop, opc2, opc3, type)                                \

target-ppc/translate.c +1 -1
-static void glue(gen_, name##ux)(DisasContext *ctx)                                   \
+static void glue(gen_, name##ux)(DisasContext *ctx)                           \

Extraneous change.

Fixed. Thanks.

Cheers,
Ben.

#67 lore raw

PATCH 23/77 ppc: Turn a bunch of booleans from int to bool

In reply to Benjamin Herrenschmidt
---
 target-ppc/translate.c | 39 +++++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 20 deletions(-)
target-ppc/translate.c +19 -20
index 4d01fd0..a5ab2eb 100644
@@ -189,21 +189,20 @@ struct DisasContext {
     uint32_t opcode;
     uint32_t exception;
     /* Routine used to access memory */
-    bool pr, hv, dr;
+    bool pr, hv, dr, le_mode;
     int mem_idx;
     int access_type;
     /* Translation flags */
-    int le_mode;
     TCGMemOp default_tcg_memop_mask;
 #if defined(TARGET_PPC64)
-    int sf_mode;
-    int has_cfar;
+    bool sf_mode;
+    bool has_cfar;
 #endif
-    int fpu_enabled;
-    int altivec_enabled;
-    int vsx_enabled;
-    int spe_enabled;
-    int tm_enabled;
+    bool fpu_enabled;
+    bool altivec_enabled;
+    bool vsx_enabled;
+    bool spe_enabled;
+    bool tm_enabled;
     ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
     int singlestep_enabled;
     uint64_t insns_flags;
@@ -380,7 +379,7 @@ typedef struct opcode_t {
 #if defined(CONFIG_USER_ONLY)
 #define CHK_HV GEN_PRIV
 #define CHK_SV GEN_PRIV
-#define CHK_HVDR GEN_PRIV
+#define CHK_HVRM GEN_PRIV
 #else
 #define CHK_HV do { if (unlikely(ctx->pr || !ctx->hv)) GEN_PRIV; } while(0)
 #define CHK_SV do { if (unlikely(ctx->pr))  GEN_PRIV; }  while(0)
@@ -11407,31 +11406,31 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
-    ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0;
+    ctx.le_mode = !!(env->hflags & (1 << MSR_LE));
     ctx.default_tcg_memop_mask = ctx.le_mode ? MO_LE : MO_BE;
 #if defined(TARGET_PPC64)
     ctx.sf_mode = msr_is_64bit(env, env->msr);
     ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
 #endif
-    ctx.fpu_enabled = msr_fp;
+    ctx.fpu_enabled = !!msr_fp;
     if ((env->flags & POWERPC_FLAG_SPE) && msr_spe)
-        ctx.spe_enabled = msr_spe;
+        ctx.spe_enabled = !!msr_spe;
     else
-        ctx.spe_enabled = 0;
+        ctx.spe_enabled = false;
     if ((env->flags & POWERPC_FLAG_VRE) && msr_vr)
-        ctx.altivec_enabled = msr_vr;
+        ctx.altivec_enabled = !!msr_vr;
     else
-        ctx.altivec_enabled = 0;
+        ctx.altivec_enabled = false;
     if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
-        ctx.vsx_enabled = msr_vsx;
+        ctx.vsx_enabled = !!msr_vsx;
     } else {
-        ctx.vsx_enabled = 0;
+        ctx.vsx_enabled = false;
     }
 #if defined(TARGET_PPC64)
     if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
-        ctx.tm_enabled = msr_tm;
+        ctx.tm_enabled = !!msr_tm;
     } else {
-        ctx.tm_enabled = 0;
+        ctx.tm_enabled = false;
     }
 #endif
     if ((env->flags & POWERPC_FLAG_SE) && msr_se)
Signature · 1 line
2.5.0
Reviewed-by on v1 23/77
#68 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 23/77] ppc: Turn a bunch of booleans from int to bool

Quoted 1 line · Benjamin Herrenschmidt
Quoted 89 lines
---
 target-ppc/translate.c | 39 +++++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 20 deletions(-)
target-ppc/translate.c +19 -20
index 4d01fd0..a5ab2eb 100644
@@ -189,21 +189,20 @@ struct DisasContext {
     uint32_t opcode;
     uint32_t exception;
     /* Routine used to access memory */
-    bool pr, hv, dr;
+    bool pr, hv, dr, le_mode;
     int mem_idx;
     int access_type;
     /* Translation flags */
-    int le_mode;
     TCGMemOp default_tcg_memop_mask;
 #if defined(TARGET_PPC64)
-    int sf_mode;
-    int has_cfar;
+    bool sf_mode;
+    bool has_cfar;
 #endif
-    int fpu_enabled;
-    int altivec_enabled;
-    int vsx_enabled;
-    int spe_enabled;
-    int tm_enabled;
+    bool fpu_enabled;
+    bool altivec_enabled;
+    bool vsx_enabled;
+    bool spe_enabled;
+    bool tm_enabled;
     ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
     int singlestep_enabled;
     uint64_t insns_flags;
@@ -380,7 +379,7 @@ typedef struct opcode_t {
 #if defined(CONFIG_USER_ONLY)
 #define CHK_HV GEN_PRIV
 #define CHK_SV GEN_PRIV
-#define CHK_HVDR GEN_PRIV
+#define CHK_HVRM GEN_PRIV
 #else
 #define CHK_HV do { if (unlikely(ctx->pr || !ctx->hv)) GEN_PRIV; } while(0)
 #define CHK_SV do { if (unlikely(ctx->pr))  GEN_PRIV; }  while(0)
@@ -11407,31 +11406,31 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb)
     ctx.insns_flags = env->insns_flags;
     ctx.insns_flags2 = env->insns_flags2;
     ctx.access_type = -1;
-    ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0;
+    ctx.le_mode = !!(env->hflags & (1 << MSR_LE));
     ctx.default_tcg_memop_mask = ctx.le_mode ? MO_LE : MO_BE;
 #if defined(TARGET_PPC64)
     ctx.sf_mode = msr_is_64bit(env, env->msr);
     ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
 #endif
-    ctx.fpu_enabled = msr_fp;
+    ctx.fpu_enabled = !!msr_fp;
     if ((env->flags & POWERPC_FLAG_SPE) && msr_spe)
-        ctx.spe_enabled = msr_spe;
+        ctx.spe_enabled = !!msr_spe;
     else
-        ctx.spe_enabled = 0;
+        ctx.spe_enabled = false;
     if ((env->flags & POWERPC_FLAG_VRE) && msr_vr)
-        ctx.altivec_enabled = msr_vr;
+        ctx.altivec_enabled = !!msr_vr;
     else
-        ctx.altivec_enabled = 0;
+        ctx.altivec_enabled = false;
     if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
-        ctx.vsx_enabled = msr_vsx;
+        ctx.vsx_enabled = !!msr_vsx;
     } else {
-        ctx.vsx_enabled = 0;
+        ctx.vsx_enabled = false;
     }
 #if defined(TARGET_PPC64)
     if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
-        ctx.tm_enabled = msr_tm;
+        ctx.tm_enabled = !!msr_tm;
     } else {
-        ctx.tm_enabled = 0;
+        ctx.tm_enabled = false;
     }
 #endif
     if ((env->flags & POWERPC_FLAG_SE) && msr_se)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#69 lore raw

PATCH 24/77 ppc: Move exception generation code out of line

In reply to Benjamin Herrenschmidt

There's no point inlining this, if you hit the exception case you exit anyway, and not inlining saves about 100K of code size (and cache footprint).

---
 target-ppc/translate.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
target-ppc/translate.c +6 -3
index a5ab2eb..ac62942 100644
@@ -279,7 +279,8 @@ void gen_update_current_nip(void *opaque)
     tcg_gen_movi_tl(cpu_nip, ctx->nip);
 }
 
-static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
+static void __attribute__((noinline))
+gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
 {
     TCGv_i32 t0, t1;
     if (ctx->exception == POWERPC_EXCP_NONE) {
@@ -293,7 +294,8 @@ static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t
     ctx->exception = (excp);
 }
 
-static inline void gen_exception(DisasContext *ctx, uint32_t excp)
+static void __attribute__((noinline))
+gen_exception(DisasContext *ctx, uint32_t excp)
 {
     TCGv_i32 t0;
     if (ctx->exception == POWERPC_EXCP_NONE) {
@@ -305,7 +307,8 @@ static inline void gen_exception(DisasContext *ctx, uint32_t excp)
     ctx->exception = (excp);
 }
 
-static inline void gen_debug_exception(DisasContext *ctx)
+static void __attribute__((noinline))
+gen_debug_exception(DisasContext *ctx)
 {
     TCGv_i32 t0;
 
Signature · 1 line
2.5.0
#70 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 24/77] ppc: Move exception generation code out of line

Quoted 2 lines · Benjamin Herrenschmidt

There's no point inlining this, if you hit the exception case you exit anyway,

That doesn't quite seem relevant - IIUC this is affecting inlining in the code generation path, rather than the code execution path.

Quoted 2 lines

and not inlining saves about 100K of code size (and cache footprint).

That sounds like a win, though.

Quoted 11 lines
---
 target-ppc/translate.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
target-ppc/translate.c +0 -0
index a5ab2eb..ac62942 100644

target-ppc/translate.c void gen_update_current_nip(void *opaque) · line 283

Quoted 6 lines
target-ppc/translate.c +2 -1
@@ -279,7 +279,8 @@ void gen_update_current_nip(void *opaque)
     tcg_gen_movi_tl(cpu_nip, ctx->nip);
 }
 
-static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
+static void __attribute__((noinline))
+gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)

I thought we generally avoided bare gcc attributes in qemu, but I don't see a helper macro for it and I do see it used in a few other places, so I guess its ok.

Quoted 23 lines
target-ppc/translate.c +4 -2
 {
     TCGv_i32 t0, t1;
     if (ctx->exception == POWERPC_EXCP_NONE) {
@@ -293,7 +294,8 @@ static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t
     ctx->exception = (excp);
 }
 
-static inline void gen_exception(DisasContext *ctx, uint32_t excp)
+static void __attribute__((noinline))
+gen_exception(DisasContext *ctx, uint32_t excp)
 {
     TCGv_i32 t0;
     if (ctx->exception == POWERPC_EXCP_NONE) {
@@ -305,7 +307,8 @@ static inline void gen_exception(DisasContext *ctx, uint32_t excp)
     ctx->exception = (excp);
 }
 
-static inline void gen_debug_exception(DisasContext *ctx)
+static void __attribute__((noinline))
+gen_debug_exception(DisasContext *ctx)
 {
     TCGv_i32 t0;
 
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#71 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 24/77] ppc: Move exception generation code out of line

Quoted 7 lines · David Gibson
Quoted 2 lines · Benjamin Herrenschmidt

There's no point inlining this, if you hit the exception case you exit anyway,

That doesn't quite seem relevant - IIUC this is affecting inlining in the code generation path, rather than the code execution path.

Well, the generation path is also slightly performance sensitive. My point was making the error path inline wasn't buying us anything.

Quoted 4 lines
Quoted 2 lines

and not inlining saves about 100K of code size (and cache footprint).

That sounds like a win, though.

Yup.

  .../...

Quoted 9 lines
Quoted 5 lines

-static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)

(quoted diff) +2 -0
+static void __attribute__((noinline))
+gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t

error)

I thought we generally avoided bare gcc attributes in qemu, but I don't see a helper macro for it and I do see it used in a few other places, so I guess its ok.

That was my guess too...

Quoted 26 lines
Quoted 25 lines

 {
     TCGv_i32 t0, t1;
     if (ctx->exception == POWERPC_EXCP_NONE) {

(quoted diff) +0 -0
@@ -293,7 +294,8 @@ static inline void

gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t ctx->exception = (excp);
 }

target-ppc/translate.c +2 -1
-static inline void gen_exception(DisasContext *ctx, uint32_t excp)
+static void __attribute__((noinline))
+gen_exception(DisasContext *ctx, uint32_t excp)

 {
     TCGv_i32 t0;
     if (ctx->exception == POWERPC_EXCP_NONE) {

target-ppc/translate.c +0 -0
@@ -305,7 +307,8 @@ static inline void gen_exception(DisasContext
 *ctx, uint32_t excp)
      ctx->exception = (excp);
  }

target-ppc/translate.c +2 -1
-static inline void gen_debug_exception(DisasContext *ctx)
+static void __attribute__((noinline))
+gen_debug_exception(DisasContext *ctx)

 {
     TCGv_i32 t0;

#72 lore raw

PATCH 25/77 ppc: Add P7/P8 Power Management instructions

In reply to Benjamin Herrenschmidt

This adds the ISA 2.06 and later power management instructions (doze, nap, sleep and rvwinkle) and associated wakeup cause testing in LPCR

---
 target-ppc/cpu.h            | 26 ++++++++++++-
 target-ppc/excp_helper.c    | 59 +++++++++++++++++++++++++++++
 target-ppc/helper.h         |  1 +
 target-ppc/translate.c      | 66 ++++++++++++++++++++++++++++++++
 target-ppc/translate_init.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 241 insertions(+), 3 deletions(-)
target-ppc/cpu.h +25 -1
index 3d22a4f..a7236cf 100644
@@ -300,6 +300,15 @@ enum {
 };
 
 /*****************************************************************************/
+/* PM instructions */
+typedef enum {
+    PPC_PM_DOZE,
+    PPC_PM_NAP,
+    PPC_PM_SLEEP,
+    PPC_PM_RVWINKLE,
+} powerpc_pm_insn_t;
+
+/*****************************************************************************/
 /* Input pins model                                                          */
 typedef enum powerpc_input_t powerpc_input_t;
 enum powerpc_input_t {
@@ -490,6 +499,14 @@ struct ppc_slb_t {
 #define LPCR_LPES1        (1ull << (63-61))
 #define LPCR_AIL_SHIFT    (63-40)      /* Alternate interrupt location */
 #define LPCR_AIL          (3ull << LPCR_AIL_SHIFT)
+#define LPCR_P7_PECE0     (1ull << (63-49))
+#define LPCR_P7_PECE1     (1ull << (63-50))
+#define LPCR_P7_PECE2     (1ull << (63-51))
+#define LPCR_P8_PECE0     (1ull << (63-47))
+#define LPCR_P8_PECE1     (1ull << (63-48))
+#define LPCR_P8_PECE2     (1ull << (63-49))
+#define LPCR_P8_PECE3     (1ull << (63-50))
+#define LPCR_P8_PECE4     (1ull << (63-51))
 
 #define msr_sf   ((env->msr >> MSR_SF)   & 1)
 #define msr_isf  ((env->msr >> MSR_ISF)  & 1)
@@ -1126,6 +1143,11 @@ struct CPUPPCState {
      * instructions and SPRs are diallowed if MSR:HV is 0
      */
     bool has_hv_mode;
+    /* On P7/P8, set when in PM state, we need to handle resume
+     * in a special way (such as routing some resume causes to
+     * 0x100), so flag this here.
+     */
+    bool in_pm_state;
 #endif
 
     /* Those resources are used only during code translation */
@@ -2069,6 +2091,8 @@ enum {
     PPC2_FP_CVT_S64    = 0x0000000000010000ULL,
     /* Transactional Memory (ISA 2.07, Book II)                              */
     PPC2_TM            = 0x0000000000020000ULL,
+    /* Server PM instructgions (ISA 2.06, Book III)                          */
+    PPC2_PM_ISA206     = 0x0000000000040000ULL,
 
 #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
                         PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \
@@ -2076,7 +2100,7 @@ enum {
                         PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \
                         PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \
                         PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \
-                        PPC2_FP_CVT_S64 | PPC2_TM)
+                        PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206)
 };
 
 /*****************************************************************************/
target-ppc/excp_helper.c +59 -0

71 lines not on this page · raw message

target-ppc/helper.h +1 -0
index ff2d50b..8292dd8 100644
@@ -13,6 +13,7 @@ DEF_HELPER_1(rfci, void, env)
 DEF_HELPER_1(rfdi, void, env)
 DEF_HELPER_1(rfmci, void, env)
 #if defined(TARGET_PPC64)
+DEF_HELPER_2(pminsn, void, env, i32)
 DEF_HELPER_1(rfid, void, env)
 DEF_HELPER_1(hrfid, void, env)
 #endif
target-ppc/translate.c +66 -0

78 lines not on this page · raw message

target-ppc/translate_init.c +90 -2

142 lines not on this page · raw message

Signature · 1 line
2.5.0
#73 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 25/77] ppc: Add P7/P8 Power Management instructions

Quoted 5 lines · Benjamin Herrenschmidt

This adds the ISA 2.06 and later power management instructions (doze, nap, sleep and rvwinkle) and associated wakeup cause testing in LPCR

Looks fine, though I haven't checked against the ISA in detail.

Quoted 399 lines
---
 target-ppc/cpu.h            | 26 ++++++++++++-
 target-ppc/excp_helper.c    | 59 +++++++++++++++++++++++++++++
 target-ppc/helper.h         |  1 +
 target-ppc/translate.c      | 66 ++++++++++++++++++++++++++++++++
 target-ppc/translate_init.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 241 insertions(+), 3 deletions(-)
target-ppc/cpu.h +25 -1
index 3d22a4f..a7236cf 100644
@@ -300,6 +300,15 @@ enum {
 };
 
 /*****************************************************************************/
+/* PM instructions */
+typedef enum {
+    PPC_PM_DOZE,
+    PPC_PM_NAP,
+    PPC_PM_SLEEP,
+    PPC_PM_RVWINKLE,
+} powerpc_pm_insn_t;
+
+/*****************************************************************************/
 /* Input pins model                                                          */
 typedef enum powerpc_input_t powerpc_input_t;
 enum powerpc_input_t {
@@ -490,6 +499,14 @@ struct ppc_slb_t {
 #define LPCR_LPES1        (1ull << (63-61))
 #define LPCR_AIL_SHIFT    (63-40)      /* Alternate interrupt location */
 #define LPCR_AIL          (3ull << LPCR_AIL_SHIFT)
+#define LPCR_P7_PECE0     (1ull << (63-49))
+#define LPCR_P7_PECE1     (1ull << (63-50))
+#define LPCR_P7_PECE2     (1ull << (63-51))
+#define LPCR_P8_PECE0     (1ull << (63-47))
+#define LPCR_P8_PECE1     (1ull << (63-48))
+#define LPCR_P8_PECE2     (1ull << (63-49))
+#define LPCR_P8_PECE3     (1ull << (63-50))
+#define LPCR_P8_PECE4     (1ull << (63-51))
 
 #define msr_sf   ((env->msr >> MSR_SF)   & 1)
 #define msr_isf  ((env->msr >> MSR_ISF)  & 1)
@@ -1126,6 +1143,11 @@ struct CPUPPCState {
      * instructions and SPRs are diallowed if MSR:HV is 0
      */
     bool has_hv_mode;
+    /* On P7/P8, set when in PM state, we need to handle resume
+     * in a special way (such as routing some resume causes to
+     * 0x100), so flag this here.
+     */
+    bool in_pm_state;
 #endif
 
     /* Those resources are used only during code translation */
@@ -2069,6 +2091,8 @@ enum {
     PPC2_FP_CVT_S64    = 0x0000000000010000ULL,
     /* Transactional Memory (ISA 2.07, Book II)                              */
     PPC2_TM            = 0x0000000000020000ULL,
+    /* Server PM instructgions (ISA 2.06, Book III)                          */
+    PPC2_PM_ISA206     = 0x0000000000040000ULL,
 
 #define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
                         PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \
@@ -2076,7 +2100,7 @@ enum {
                         PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \
                         PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \
                         PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \
-                        PPC2_FP_CVT_S64 | PPC2_TM)
+                        PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206)
 };
 
 /*****************************************************************************/
target-ppc/excp_helper.c +59 -0
index 80a70f4..3f77df7 100644
@@ -100,6 +100,44 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     asrr0 = -1;
     asrr1 = -1;
 
+    /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
+    if (env->in_pm_state) {
+        env->in_pm_state = false;
+
+        /* Pretend to be returning from doze always as we don't lose state */
+        msr |= (0x1ull << (63 - 47));
+
+        /* Non-machine check are routed to 0x100 with a wakeup cause
+         * encoded in SRR1
+         */
+        if (excp != POWERPC_EXCP_MCHECK) {
+            switch(excp) {
+            case POWERPC_EXCP_RESET:
+                msr |= 0x4ull << (63-45);
+                break;
+            case POWERPC_EXCP_EXTERNAL:
+                msr |= 0x8ull << (63-45);
+                break;
+            case POWERPC_EXCP_DECR:
+                msr |= 0x6ull << (63-45);
+                break;
+            case POWERPC_EXCP_SDOOR:
+                msr |= 0x5ull << (63-45);
+                break;
+            case POWERPC_EXCP_SDOOR_HV:
+                msr |= 0x3ull << (63-45);
+                break;
+            case POWERPC_EXCP_HV_MAINT:
+                msr |= 0xaull << (63-45);
+                break;
+            default:
+                cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
+                          excp);
+            }
+            excp = POWERPC_EXCP_RESET;
+        }
+    }
+
     /* Exception targetting modifiers
      *
      * LPES0 is supported on POWER7/8
@@ -898,6 +936,27 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
     }
 }
 
+#if defined(TARGET_PPC64)
+void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
+{
+    CPUState *cs;
+
+    cs = CPU(ppc_env_get_cpu(env));
+    cs->halted = 1;
+    env->in_pm_state = true;
+
+    /* Technically, nap doesn't set EE, but if we don't set it
+     * then ppc_hw_interrupt() won't deliver. We could add some
+     * other tests there based on LPCR but it's simpler to just
+     * whack EE in. It will be cleared by the 0x100 at wakeup
+     * anyway. It will still be observable by the guest in SRR1
+     * but this doesn't seem to be a problem.
+     */
+    env->msr |= (1ull << MSR_EE);
+    helper_raise_exception(env, EXCP_HLT);
+}
+#endif /* defined(TARGET_PPC64) */
+
 static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
                           target_ulong msrm, int keep_msrh)
 {
target-ppc/helper.h +1 -0
index ff2d50b..8292dd8 100644
@@ -13,6 +13,7 @@ DEF_HELPER_1(rfci, void, env)
 DEF_HELPER_1(rfdi, void, env)
 DEF_HELPER_1(rfmci, void, env)
 #if defined(TARGET_PPC64)
+DEF_HELPER_2(pminsn, void, env, i32)
 DEF_HELPER_1(rfid, void, env)
 DEF_HELPER_1(hrfid, void, env)
 #endif
target-ppc/translate.c +66 -0
index ac62942..f76a0c3 100644
@@ -3567,6 +3567,68 @@ static void gen_wait(DisasContext *ctx)
     gen_exception_err(ctx, EXCP_HLT, 1);
 }
 
+#if defined(TARGET_PPC64)
+static void gen_doze(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    GEN_PRIV;
+#else
+    TCGv_i32 t;
+
+    CHK_HV;
+    t = tcg_const_i32(PPC_PM_DOZE);
+    gen_helper_pminsn(cpu_env, t);
+    tcg_temp_free_i32(t);
+    gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_nap(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    GEN_PRIV;
+#else
+    TCGv_i32 t;
+
+    CHK_HV;
+    t = tcg_const_i32(PPC_PM_NAP);
+    gen_helper_pminsn(cpu_env, t);
+    tcg_temp_free_i32(t);
+    gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_sleep(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    GEN_PRIV;
+#else
+    TCGv_i32 t;
+
+    CHK_HV;
+    t = tcg_const_i32(PPC_PM_SLEEP);
+    gen_helper_pminsn(cpu_env, t);
+    tcg_temp_free_i32(t);
+    gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_rvwinkle(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    GEN_PRIV;
+#else
+    TCGv_i32 t;
+
+    CHK_HV;
+    t = tcg_const_i32(PPC_PM_RVWINKLE);
+    gen_helper_pminsn(cpu_env, t);
+    tcg_temp_free_i32(t);
+    gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+#endif /* #if defined(TARGET_PPC64) */
+
 /***                         Floating-point load                           ***/
 #define GEN_LDF(name, ldop, opc, type)                                        \
 static void glue(gen_, name)(DisasContext *ctx)                                       \
@@ -9828,6 +9890,10 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
 GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
 #if defined(TARGET_PPC64)
 GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
+GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
 GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
 #endif
 GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
target-ppc/translate_init.c +90 -2
index 8d82bc8..8a1ce85 100644
@@ -8297,10 +8297,45 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr)
     return false;
 }
 
+static bool cpu_has_work_POWER7(CPUState *cs)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+
+    if (cs->halted) {
+        if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
+            return false;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
+            (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
+            (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
+            (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
+            (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+            return true;
+        }
+        if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
+            return true;
+        }
+        return false;
+    } else {
+        return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
+    }
+}
+
 POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
     PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
 
     dc->fw_name = "PowerPC,POWER7";
     dc->desc = "POWER7";
@@ -8309,6 +8344,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
     pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06;
     pcc->init_proc = init_proc_POWER7;
     pcc->check_pow = check_pow_nocheck;
+    cc->has_work = cpu_has_work_POWER7;
     pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
                        PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
                        PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@@ -8325,7 +8361,8 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
     pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
                         PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
                         PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
-                        PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64;
+                        PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 |
+                        PPC2_PM_ISA206;
     pcc->msr_mask = (1ull << MSR_SF) |
                     (1ull << MSR_VR) |
                     (1ull << MSR_VSX) |
@@ -8375,10 +8412,53 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr)
     return false;
 }
 
+static bool cpu_has_work_POWER8(CPUState *cs)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+
+    if (cs->halted) {
+        if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
+            return false;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
+            (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
+            (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
+            (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
+            (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
+            (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) {
+            return true;
+        }
+        if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
+            (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) {
+            return true;
+        }
+        if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
+            return true;
+        }
+        return false;
+    } else {
+        return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
+    }
+}
+
 POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
     PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
 
     dc->fw_name = "PowerPC,POWER8";
     dc->desc = "POWER8";
@@ -8387,6 +8467,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
     pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06;
     pcc->init_proc = init_proc_POWER8;
     pcc->check_pow = check_pow_nocheck;
+    cc->has_work = cpu_has_work_POWER8;
     pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
                        PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
                        PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@@ -8406,7 +8487,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
                         PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 |
                         PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 |
                         PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
-                        PPC2_TM;
+                        PPC2_TM | PPC2_PM_ISA206;
     pcc->msr_mask = (1ull << MSR_SF) |
                     (1ull << MSR_SHV) |
 		    (1ull << MSR_TM) |
@@ -8464,6 +8545,13 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu)
     lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
     lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
 
+    /* P7 and P8 has slightly different PECE bits, mostly because P8 adds
+     * bit 47 and 48 which are reserved on P7. Here we set them all, which
+     * will work as expected for both implementations
+     */
+    lpcr->default_value |= LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 |
+                           LPCR_P8_PECE3 | LPCR_P8_PECE4;
+
     /* We should be followed by a CPU reset but update the active value
      * just in case...
      */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#74 lore raw

PATCH 26/77 ppc/pnv: Add skeletton PowerNV platform

In reply to Benjamin Herrenschmidt

No devices yet, not even an interrupt controller, just to get started.

---
 default-configs/ppc64-softmmu.mak |   1 +
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 600 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  36 +++
 4 files changed, 639 insertions(+)
 create mode 100644 hw/ppc/pnv.c
 create mode 100644 include/hw/ppc/pnv.h
default-configs/ppc64-softmmu.mak +1 -0
index bb71b23..96574c8 100644
@@ -40,6 +40,7 @@ CONFIG_I8259=y
 CONFIG_XILINX=y
 CONFIG_XILINX_ETHLITE=y
 CONFIG_PSERIES=y
+CONFIG_POWERNV=y
 CONFIG_PREP=y
 CONFIG_MAC=y
 CONFIG_E500=y
hw/ppc/Makefile.objs +2 -0
index c1ffc77..cd74c96 100644
@@ -4,6 +4,8 @@ obj-y += ppc.o ppc_booke.o
 obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
+# IBM PowerNV
+obj-$(CONFIG_POWERNV) += pnv.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
hw/ppc/pnv.c new file +600 -0

600 lines not on this page · raw message

include/hw/ppc/pnv.h new file +36 -0
new file mode 100644
index 0000000..9a48c16
@@ -0,0 +1,36 @@
+#ifndef _HW_LPC_H
+#define _HW_LPC_H
+/*
+ * QEMU PowerNV various definitions
+ *
+ * Copyright (c) 2014 BenH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+
+/* Should we turn that into a QOjb of some sort ? */
+typedef struct PnvChip {
+    uint32_t         chip_id;
+} PnvChip;
+
+typedef struct PnvSystem {
+    uint32_t  num_chips;
+#define PNV_MAX_CHIPS		1
+    PnvChip   chips[PNV_MAX_CHIPS];
+} PnvSystem;
+
+#endif /* _HW_PNV_LPC_H */
+
Signature · 1 line
2.5.0
#75 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 26/77] ppc/pnv: Add skeletton PowerNV platform

Quoted 3 lines · Benjamin Herrenschmidt
hw/ppc/pnv.c new file +0 -0
@@ -0,0 +1,600 @@

<snip>

Quoted 2 lines
hw/ppc/pnv.c new file +2 -0
+static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t initrd_size)
+{

<snip>

hw/ppc/pnv.c

Quoted 6 lines
hw/ppc/pnv.c new file +6 -0
+    _FDT((fdt_begin_node(fdt, "chosen")));
+    _FDT((fdt_property(fdt, "linux,initrd-start",
+                       &start_prop, sizeof(start_prop))));
+    _FDT((fdt_property(fdt, "linux,initrd-end",
+                       &end_prop, sizeof(end_prop))));
+    _FDT((fdt_end_node(fdt)));

(this time replying to list as well)

This misses out kernel command line, which means -append doesn't work.

This patch fixes that:

hw/ppc/pnv.c +6 -2
index 1787dd19c67a..82d6321228f6 100644
@@ -379,7 +379,7 @@ static void powernv_create_cpu_node(void *fdt, CPUState *cs, int smt_threads)
     _FDT((fdt_end_node(fdt)));
 }
 
-static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t initrd_size)
+static void *powernv_create_fdt(PnvSystem *sys, const char *kernel_cmdline, uint32_t initrd_base, uint32_t initrd_size)
 {
     void *fdt;
     CPUState *cs;
@@ -423,6 +423,9 @@ static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t i
     g_free(buf);
 
     _FDT((fdt_begin_node(fdt, "chosen")));
+    if (kernel_cmdline) {
+        _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline)));
+    }
     _FDT((fdt_property(fdt, "linux,initrd-start",
                        &start_prop, sizeof(start_prop))));
     _FDT((fdt_property(fdt, "linux,initrd-end",
@@ -781,7 +784,8 @@ static void ppc_powernv_init(MachineState *machine)
             initrd_base = 0;
             initrd_size = 0;
     }
-    fdt = powernv_create_fdt(sys, initrd_base, initrd_size);
+    fdt = powernv_create_fdt(sys, machine->kernel_cmdline,
+                             initrd_base, initrd_size);
     cpu_physical_memory_write(FDT_ADDR, fdt, fdt_totalsize(fdt));
 }
 
#76 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 26/77] ppc/pnv: Add skeletton PowerNV platform

In reply to Benjamin Herrenschmidt
Quoted 4 lines · Benjamin Herrenschmidt

No devices yet, not even an interrupt controller, just to get started.

Quoted 8 lines
---
 default-configs/ppc64-softmmu.mak |   1 +
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 600 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  36 +++
 4 files changed, 639 insertions(+)
 create mode 100644 hw/ppc/pnv.c
 create mode 100644 include/hw/ppc/pnv.h

Many of my comments below may be made irrelevant by later patches in the series.

Quoted 145 lines
default-configs/ppc64-softmmu.mak +1 -0
index bb71b23..96574c8 100644
@@ -40,6 +40,7 @@ CONFIG_I8259=y
 CONFIG_XILINX=y
 CONFIG_XILINX_ETHLITE=y
 CONFIG_PSERIES=y
+CONFIG_POWERNV=y
 CONFIG_PREP=y
 CONFIG_MAC=y
 CONFIG_E500=y
hw/ppc/Makefile.objs +2 -0
index c1ffc77..cd74c96 100644
@@ -4,6 +4,8 @@ obj-y += ppc.o ppc_booke.o
 obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
+# IBM PowerNV
+obj-$(CONFIG_POWERNV) += pnv.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
hw/ppc/pnv.c new file +114 -0
new file mode 100644
index 0000000..e68c9b1
@@ -0,0 +1,600 @@
+/*
+ * QEMU PowerPC PowerNV model
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2010 David Gibson, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#include "sysemu/sysemu.h"
+#include "hw/hw.h"
+#include "hw/fw-path-provider.h"
+#include "elf.h"
+#include "net/net.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/cpus.h"
+#include "sysemu/kvm.h"
+#include "sysemu/numa.h"
+#include "kvm_ppc.h"
+#include "mmu-hash64.h"
+#include "qom/cpu.h"
+
+#include "hw/boards.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/loader.h"
+
+#include "exec/address-spaces.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+#include "hw/nmi.h"
+
+#include "hw/compat.h"
+
+#include <libfdt.h>
+
+#define FDT_ADDR                0x01000000
+#define FDT_MAX_SIZE            0x00100000
+#define FW_MAX_SIZE             0x00400000
+#define FW_FILE_NAME            "skiboot.lid"
+#define KERNEL_FILE_NAME        "skiroot.lid"
+#define KERNEL_LOAD_ADDR        0x20000000
+
+#define TIMEBASE_FREQ           512000000ULL
+
+#define MAX_CPUS                255
+
+#define PHANDLE_XICP            0x00001111
+
+typedef struct sPowerNVMachineState sPowerNVMachineState;
+
+#define TYPE_POWERNV_MACHINE      "powernv-machine"
+#define POWERNV_MACHINE(obj) \
+    OBJECT_CHECK(sPowerNVMachineState, (obj), TYPE_POWERNV_MACHINE)
+
+/**
+ * sPowerNVMachineState:
+ */
+struct sPowerNVMachineState {
+    /*< private >*/
+    MachineState parent_obj;
+    PnvSystem sys;
+};
+
+static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
+                                     size_t maxsize)
+{
+    size_t maxcells = maxsize / sizeof(uint32_t);
+    int i, j, count;
+    uint32_t *p = prop;
+
+    for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
+        struct ppc_one_seg_page_size *sps = &env->sps.sps[i];
+
+        if (!sps->page_shift) {
+            break;
+        }
+        for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) {
+            if (sps->enc[count].page_shift == 0) {
+                break;
+            }
+        }
+        if ((p - prop) >= (maxcells - 3 - count * 2)) {
+            break;
+        }
+        *(p++) = cpu_to_be32(sps->page_shift);
+        *(p++) = cpu_to_be32(sps->slb_enc);
+        *(p++) = cpu_to_be32(count);
+        for (j = 0; j < count; j++) {
+            *(p++) = cpu_to_be32(sps->enc[j].page_shift);
+            *(p++) = cpu_to_be32(sps->enc[j].pte_enc);
+        }
+    }
+
+    return (p - prop) * sizeof(uint32_t);
+}

hw/ppc/pnv.c line 124

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+
+#define _FDT(exp) \
+    do { \
+        int ret = (exp);                                           \
+        if (ret < 0) {                                             \
+            fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+                    #exp, fdt_strerror(ret));                      \
+            exit(1);                                               \
+        }                                                          \
+    } while (0)

We should probably make a file where helper routines used by both spapr.c and pnv.c can live.

Quoted 101 lines
hw/ppc/pnv.c new file +101 -0
+
+static void powernv_populate_memory_node(void *fdt, int nodeid, hwaddr start,
+                                         hwaddr size)
+{
+    /* Probablly bogus, need to match with what's going on in CPU nodes */
+    uint32_t chip_id[] = {
+        cpu_to_be32(0x0), cpu_to_be32(nodeid)
+    };
+    char *mem_name;
+    uint64_t mem_reg_property[2];
+
+    mem_reg_property[0] = cpu_to_be64(start);
+    mem_reg_property[1] = cpu_to_be64(size);
+
+    mem_name = g_strdup_printf("memory@"TARGET_FMT_lx, start);
+    _FDT((fdt_begin_node(fdt, mem_name)));
+    g_free(mem_name);
+    _FDT((fdt_property_string(fdt, "device_type", "memory")));
+    _FDT((fdt_property(fdt, "reg", mem_reg_property,
+                       sizeof(mem_reg_property))));
+    _FDT((fdt_property(fdt, "ibm,chip-id", chip_id, sizeof(chip_id))));
+    _FDT((fdt_end_node(fdt)));
+}
+
+static int powernv_populate_memory(void *fdt)
+{
+    hwaddr mem_start, node_size;
+    int i, nb_nodes = nb_numa_nodes;
+    NodeInfo *nodes = numa_info;
+    NodeInfo ramnode;
+
+    /* No NUMA nodes, assume there is just one node with whole RAM */
+    if (!nb_numa_nodes) {
+        nb_nodes = 1;
+        ramnode.node_mem = ram_size;
+        nodes = &ramnode;
+    }
+
+    for (i = 0, mem_start = 0; i < nb_nodes; ++i) {
+        if (!nodes[i].node_mem) {
+            continue;
+        }
+        if (mem_start >= ram_size) {
+            node_size = 0;
+        } else {
+            node_size = nodes[i].node_mem;
+            if (node_size > ram_size - mem_start) {
+                node_size = ram_size - mem_start;
+            }
+        }
+        for ( ; node_size; ) {
+            hwaddr sizetmp = pow2floor(node_size);
+
+            /* mem_start != 0 here */
+            if (ctzl(mem_start) < ctzl(sizetmp)) {
+                sizetmp = 1ULL << ctzl(mem_start);
+            }
+
+            powernv_populate_memory_node(fdt, i, mem_start, sizetmp);
+            node_size -= sizetmp;
+            mem_start += sizetmp;
+        }
+    }
+
+    return 0;
+}
+
+static void powernv_create_cpu_node(void *fdt, CPUState *cs, int smt_threads)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+    DeviceClass *dc = DEVICE_GET_CLASS(cs);
+    PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+    uint32_t servers_prop[smt_threads];
+    uint32_t gservers_prop[smt_threads * 2];
+    int i, index = ppc_get_vcpu_dt_id(cpu);
+    uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
+                       0xffffffff, 0xffffffff};
+    uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
+    uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
+    uint32_t page_sizes_prop[64];
+    size_t page_sizes_prop_size;
+    char *nodename;
+
+    if ((index % smt_threads) != 0) {
+        return;
+    }
+
+    nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
+
+    _FDT((fdt_begin_node(fdt, nodename)));
+
+    g_free(nodename);
+
+    _FDT((fdt_property_cell(fdt, "reg", index)));
+    _FDT((fdt_property_string(fdt, "device_type", "cpu")));
+
+    _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR])));
+    _FDT((fdt_property_cell(fdt, "d-cache-block-size",
+                            env->dcache_line_size)));
+    _FDT((fdt_property_cell(fdt, "d-cache-line-size",

hw/ppc/pnv.c line 235

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+                            env->dcache_line_size)));
+    _FDT((fdt_property_cell(fdt, "i-cache-block-size",
+                            env->icache_line_size)));
+    _FDT((fdt_property_cell(fdt, "i-cache-line-size",
+                            env->icache_line_size)));
+
+    if (pcc->l1_dcache_size) {
+        _FDT((fdt_property_cell(fdt, "d-cache-size", pcc->l1_dcache_size)));
+    } else {
+        fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n");

Hmm (note to self) should probably change a bunch of these both in spapr.c and pnv.c from explicit fprintfs() to modern error_report() and similar.

Quoted 60 lines
hw/ppc/pnv.c new file +60 -0
+    }
+    if (pcc->l1_icache_size) {
+        _FDT((fdt_property_cell(fdt, "i-cache-size", pcc->l1_icache_size)));
+    } else {
+        fprintf(stderr, "Warning: Unknown L1 icache size for cpu\n");
+    }
+
+    _FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq)));
+    _FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq)));
+    _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
+    _FDT((fdt_property_string(fdt, "status", "okay")));
+    _FDT((fdt_property(fdt, "64-bit", NULL, 0)));
+
+    if (env->spr_cb[SPR_PURR].oea_read) {
+        _FDT((fdt_property(fdt, "ibm,purr", NULL, 0)));
+    }
+
+    if (env->mmu_model & POWERPC_MMU_1TSEG) {
+        _FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
+                           segs, sizeof(segs))));
+    }
+
+    /* Advertise VMX/VSX (vector extensions) if available
+     *   0 / no property == no vector extensions
+     *   1               == VMX / Altivec available
+     *   2               == VSX available */
+    if (env->insns_flags & PPC_ALTIVEC) {
+        uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
+
+        _FDT((fdt_property_cell(fdt, "ibm,vmx", vmx)));
+    }
+
+    /* Advertise DFP (Decimal Floating Point) if available
+     *   0 / no property == no DFP
+     *   1               == DFP available */
+    if (env->insns_flags2 & PPC2_DFP) {
+        _FDT((fdt_property_cell(fdt, "ibm,dfp", 1)));
+    }
+
+    page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop,
+                                                  sizeof(page_sizes_prop));
+    if (page_sizes_prop_size) {
+        _FDT((fdt_property(fdt, "ibm,segment-page-sizes",
+                           page_sizes_prop, page_sizes_prop_size)));
+    }
+
+    /* XXX Just a hack for now */
+    _FDT((fdt_property_cell(fdt, "ibm,chip-id", 0)));
+
+    if (cpu->cpu_version) {
+        _FDT((fdt_property_cell(fdt, "cpu-version", cpu->cpu_version)));
+    }
+
+    /* Build interrupt servers and gservers properties */
+    for (i = 0; i < smt_threads; i++) {
+        servers_prop[i] = cpu_to_be32(index + i);
+        /* Hack, direct the group queues back to cpu 0 */
+        gservers_prop[i*2] = cpu_to_be32(index + i);
+        gservers_prop[i*2 + 1] = 0;
+    }

hw/ppc/pnv.c line 305

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+    _FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s",
+                       servers_prop, sizeof(servers_prop))));
+    _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
+                       gservers_prop, sizeof(gservers_prop))));
+
+    _FDT((fdt_end_node(fdt)));
+}
+
+static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t initrd_size)
+{

So.. does it make sense for qemu to create a device tree for powernv, rather than leaving it to Opal?

Quoted 19 lines
hw/ppc/pnv.c new file +19 -0
+    void *fdt;
+    CPUState *cs;
+    int smt = kvmppc_smt_threads();
+    uint32_t start_prop = cpu_to_be32(initrd_base);
+    uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
+    char *buf;
+    const char plat_compat[] = "qemu,powernv\0ibm,powernv";
+
+    fdt = g_malloc0(FDT_MAX_SIZE);
+    _FDT((fdt_create(fdt, FDT_MAX_SIZE)));
+    _FDT((fdt_finish_reservemap(fdt)));
+
+    /* Root node */
+    _FDT((fdt_begin_node(fdt, "")));
+    _FDT((fdt_property_string(fdt, "model", "IBM PowerNV (emulated by qemu)")));
+    _FDT((fdt_property(fdt, "compatible", plat_compat, sizeof(plat_compat))));
+
+    /*
+     * Add info to guest to indentify which host is it being run on

hw/ppc/pnv.c line 334

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+     * and what is the uuid of the guest
+     */
+    if (kvmppc_get_host_model(&buf)) {
+        _FDT((fdt_property_string(fdt, "host-model", buf)));
+        g_free(buf);
+    }
+    if (kvmppc_get_host_serial(&buf)) {
+        _FDT((fdt_property_string(fdt, "host-serial", buf)));
+        g_free(buf);
+    }

Since you're emulating a "bare metal" machine, surely the host properties aren't relevant here.

Quoted 44 lines
hw/ppc/pnv.c new file +44 -0
+
+    buf = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1],
+                          qemu_uuid[2], qemu_uuid[3], qemu_uuid[4],
+                          qemu_uuid[5], qemu_uuid[6], qemu_uuid[7],
+                          qemu_uuid[8], qemu_uuid[9], qemu_uuid[10],
+                          qemu_uuid[11], qemu_uuid[12], qemu_uuid[13],
+                          qemu_uuid[14], qemu_uuid[15]);
+
+    _FDT((fdt_property_string(fdt, "vm,uuid", buf)));
+    g_free(buf);
+
+    _FDT((fdt_begin_node(fdt, "chosen")));
+    _FDT((fdt_property(fdt, "linux,initrd-start",
+                       &start_prop, sizeof(start_prop))));
+    _FDT((fdt_property(fdt, "linux,initrd-end",
+                       &end_prop, sizeof(end_prop))));
+    _FDT((fdt_end_node(fdt)));
+
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
+
+    /* cpus */
+    _FDT((fdt_begin_node(fdt, "cpus")));
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
+
+    CPU_FOREACH(cs) {
+        powernv_create_cpu_node(fdt, cs, smt);
+    }
+
+    _FDT((fdt_end_node(fdt)));
+
+    /* Memory */
+    _FDT((powernv_populate_memory(fdt)));
+
+    /* /hypervisor node */
+    if (kvm_enabled()) {
+        uint8_t hypercall[16];
+
+        /* indicate KVM hypercall interface */
+        _FDT((fdt_begin_node(fdt, "hypervisor")));
+        _FDT((fdt_property_string(fdt, "compatible", "linux,kvm")));
+        if (kvmppc_has_cap_fixup_hcalls()) {
+            /*

hw/ppc/pnv.c line 388

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+             * Older KVM versions with older guest kernels were broken with the
+             * magic page, don't allow the guest to map it.
+             */
+            kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
+                                 sizeof(hypercall));
+            _FDT((fdt_property(fdt, "hcall-instructions", hypercall,
+                              sizeof(hypercall))));
+        }
+        _FDT((fdt_end_node(fdt)));
+    }

And a hypercall interface surely doesn't make sense for powernv.

Quoted 11 lines
hw/ppc/pnv.c new file +11 -0
+
+    _FDT((fdt_end_node(fdt))); /* close root node */
+    _FDT((fdt_finish(fdt)));
+
+    return fdt;
+}
+
+static void powernv_cpu_reset(void *opaque)
+{
+    PowerPCCPU *cpu = opaque;
+    CPUState *cs = CPU(cpu);

hw/ppc/pnv.c line 409

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+    CPUPPCState *env = &cpu->env;
+
+    cpu_reset(cs);
+
+    env->spr[SPR_PIR] = ppc_get_vcpu_dt_id(cpu);
+    env->spr[SPR_HIOR] = 0;
+    env->gpr[3] = FDT_ADDR;
+    env->nip = 0x10;
+    env->msr |= MSR_HVB;
+}

So, I believe the qemu-ishly correct way of doing this is to have the cpu initialization in the cpu code, rather than the platform code, as much as possible. On PAPR we kind of get away with initialization in the platform code on the grounds that it's a paravirt platform, but powernv doesn't have that excuse.

But this may well be stuff that changes in later patches, so..

hw/ppc/pnv.c line 415

Quoted 5 lines
hw/ppc/pnv.c new file +5 -0
+static const VMStateDescription vmstate_powernv = {
+    .name = "powernv",
+    .version_id = 1,
+    .minimum_version_id = 1,
+};

It might be best to leave out the vmstate entirely until you're ready to implement migration, rather than having a partial, probably not working migration implementation.

hw/ppc/pnv.c line 426

Quoted 11 lines
hw/ppc/pnv.c new file +11 -0
+
+static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no)
+{
+    PnvChip *chip = &sys->chips[chip_no];
+
+    if (chip_no >= PNV_MAX_CHIPS) {
+            return;
+    }
+
+    /* XXX Improve chip numbering to better match HW */
+    chip->chip_id = chip_no;

I think modern qemu conventions would suggest creating the chips as QOM objects rather than having a fixed array.

Quoted 74 lines
hw/ppc/pnv.c new file +74 -0
+}
+
+static void ppc_powernv_init(MachineState *machine)
+{
+    ram_addr_t ram_size = machine->ram_size;
+    const char *cpu_model = machine->cpu_model;
+    const char *kernel_filename = machine->kernel_filename;
+    const char *initrd_filename = machine->initrd_filename;
+    uint32_t initrd_base = 0;
+    long initrd_size = 0;
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine);
+    PnvSystem *sys = &pnv_machine->sys;
+    long fw_size;
+    char *filename;
+    void *fdt;
+    int i;
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+        cpu_model = kvm_enabled() ? "host" : "POWER8";
+    }
+
+    for (i = 0; i < smp_cpus; i++) {
+        cpu = cpu_ppc_init(cpu_model);
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+            exit(1);
+        }
+        env = &cpu->env;
+
+        /* Set time-base frequency to 512 MHz */
+        cpu_ppc_tb_init(env, TIMEBASE_FREQ);
+
+        /* MSR[IP] doesn't exist nowadays */
+        env->msr_mask &= ~(1 << 6);
+
+        qemu_register_reset(powernv_cpu_reset, cpu);
+    }
+
+    /* allocate RAM */
+    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", ram_size);
+    memory_region_add_subregion(sysmem, 0, ram);
+
+    /* XXX We should decide how many chips to create based on #cores and
+     * Venice vs. Murano vs. Naples chip type etc..., for now, just create
+     * one chip. Also creation of the CPUs should be done per-chip
+     */
+    sys->num_chips = 1;
+
+    /* Create only one PHB for now until I figure out what's wrong
+     * when I create more (resource assignment failures in Linux)
+     */
+    pnv_create_chip(sys, 0);
+
+    if (bios_name == NULL) {
+        bios_name = FW_FILE_NAME;
+    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+    fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
+    if (fw_size < 0) {
+        hw_error("qemu: could not load OPAL '%s'\n", filename);
+        exit(1);
+    }
+    g_free(filename);
+
+
+    if (kernel_filename == NULL) {
+        kernel_filename = KERNEL_FILE_NAME;
+    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
Quoted 1 line

kernel_filename);

The commit withe Opal image should go in before this, no?

Quoted 142 lines
hw/ppc/pnv.c new file +100 -0
+    fw_size = load_image_targphys(filename, 0x20000000, 0x2000000);
+    if (fw_size < 0) {
+        hw_error("qemu: could not load kernel'%s'\n", filename);
+        exit(1);
+    }
+    g_free(filename);
+
+    /* load initrd */
+    if (initrd_filename) {
+            /* Try to locate the initrd in the gap between the kernel
+             * and the firmware. Add a bit of space just in case
+             */
+            initrd_base = 0x40000000;
+            initrd_size = load_image_targphys(initrd_filename, initrd_base,
+                                              0x10000000); // 128MB max
+            if (initrd_size < 0) {
+                    fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+                            initrd_filename);
+                    exit(1);
+            }
+    } else {
+            initrd_base = 0;
+            initrd_size = 0;
+    }
+    fdt = powernv_create_fdt(sys, initrd_base, initrd_size);
+    cpu_physical_memory_write(FDT_ADDR, fdt, fdt_totalsize(fdt));
+}
+
+static int powernv_kvm_type(const char *vm_type)
+{
+    /* Always force PR KVM */
+    return 2;
+}
+
+static void ppc_cpu_do_nmi_on_cpu(void *arg)
+{
+    CPUState *cs = arg;
+
+    cpu_synchronize_state(cs);
+    ppc_cpu_do_system_reset(cs);
+}
+
+static void powernv_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+    CPUState *cs;
+
+    CPU_FOREACH(cs) {
+        async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, cs);
+    }
+}
+
+static void powernv_machine_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+    NMIClass *nc = NMI_CLASS(oc);
+
+    mc->init = ppc_powernv_init;
+    mc->block_default_type = IF_SCSI;
+    mc->max_cpus = MAX_CPUS;
+    mc->no_parallel = 1;
+    mc->default_boot_order = NULL;
+    mc->kvm_type = powernv_kvm_type;
+
+    nc->nmi_monitor_handler = powernv_nmi;
+}
+
+static const TypeInfo powernv_machine_info = {
+    .name          = TYPE_POWERNV_MACHINE,
+    .parent        = TYPE_MACHINE,
+    .abstract      = true,
+    .instance_size = sizeof(sPowerNVMachineState),
+    .class_init    = powernv_machine_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_NMI },
+        { }
+    },
+};
+
+static void powernv_machine_2_5_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->name = "powernv-2.5";
+    mc->desc = "PowerNV v2.5";
+    mc->alias = "powernv";
+}
+
+static const TypeInfo powernv_machine_2_5_info = {
+    .name          = MACHINE_TYPE_NAME("powernv-2.5"),
+    .parent        = TYPE_POWERNV_MACHINE,
+    .class_init    = powernv_machine_2_5_class_init,
+};
+
+static void powernv_machine_register_types(void)
+{
+    type_register_static(&powernv_machine_info);
+    type_register_static(&powernv_machine_2_5_info);
+}
+
+type_init(powernv_machine_register_types)
include/hw/ppc/pnv.h new file +36 -0
new file mode 100644
index 0000000..9a48c16
@@ -0,0 +1,36 @@
+#ifndef _HW_LPC_H
+#define _HW_LPC_H
+/*
+ * QEMU PowerNV various definitions
+ *
+ * Copyright (c) 2014 BenH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+
+/* Should we turn that into a QOjb of some sort ? */
+typedef struct PnvChip {
+    uint32_t         chip_id;
+} PnvChip;
+
+typedef struct PnvSystem {
+    uint32_t  num_chips;
+#define PNV_MAX_CHIPS		1
+    PnvChip   chips[PNV_MAX_CHIPS];
+} PnvSystem;
+
+#endif /* _HW_PNV_LPC_H */
+
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#77 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 26/77] ppc/pnv: Add skeletton PowerNV platform

Quoted 16 lines · David Gibson
Quoted 10 lines · Benjamin Herrenschmidt

No devices yet, not even an interrupt controller, just to get started.

---
 default-configs/ppc64-softmmu.mak |   1 +
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 600
++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  36 +++

Quoted 3 lines
 4 files changed, 639 insertions(+)

 create mode 100644 hw/ppc/pnv.c
 create mode 100644 include/hw/ppc/pnv.h

Quoted 2 lines

Many of my comments below may be made irrelevant by later patches in the series.

Heh, well there is where the "meat" of the new platform starts showing up :-)

 .../...

Quoted 10 lines
Quoted 9 lines

+#define _FDT(exp) \
+    do { \
+        int ret = (exp);                                           \
+        if (ret < 0) {                                             \
+            fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+                    #exp, fdt_strerror(ret));                      \
+            exit(1);                                               \
+        }                                                          \
+    } while (0)

Quoted 2 lines

We should probably make a file where helper routines used by both spapr.c and pnv.c can live.

Probably but I'd see that as a later cleanup rather than doing it in this series...

.../...

Quoted 8 lines
Quoted 4 lines

+    if (pcc->l1_dcache_size) {
+        _FDT((fdt_property_cell(fdt, "d-cache-size", pcc->l1_dcache_size))); +    } else {
+        fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n");

Hmm (note to self) should probably change a bunch of these both in spapr.c and pnv.c from explicit fprintfs() to modern error_report() and similar.

That's a train I completely missed, but yes.

  .../...

Quoted 6 lines
Quoted 6 lines

+    }
+    _FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s", +                       servers_prop, sizeof(servers_prop)))); +    _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", +                       gservers_prop, sizeof(gservers_prop)))); +

Quoted 8 lines

hw/ppc/pnv.c line 305

Quoted 5 lines

+    _FDT((fdt_end_node(fdt)));

hw/ppc/pnv.c new file +4 -0
+}
+
+static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t initrd_size)
+{

So.. does it make sense for qemu to create a device tree for powernv, rather than leaving it to Opal?

Well, OPAL only creates a device-tree if you are on an FSP machine in which case it expects a complex data structure (HDAT) coming from the FSP to use as a source of info.

On OpenPower machines, which is closer to what we simulate here, we do get a device-tree as an input in OPAL, it's generated by HostBoot.

Now, I am not running HostBoot in qemu because most of what it does is completely irrelevant to an emulated system (training the various links, initializing the memory buffer chips, etc...).

However we do need to pass a number of platform information to OPAL which HB does via the device-tree, such as which cores are enabled, the memory map configured for PCI, which PHBs are enabled, etc...  so creating a DT in qemu makes sense, it mimmics HB in essence.

OPAL will enrich that device-tree before starting Linux.

Quoted 13 lines
Quoted 4 lines

+    /*
+     * Add info to guest to indentify which host is it being run on +     * and what is the uuid of the guest
+     */

Quoted 8 lines

+    if (kvmppc_get_host_model(&buf)) {
+        _FDT((fdt_property_string(fdt, "host-model", buf))); +        g_free(buf);
+    }
+    if (kvmppc_get_host_serial(&buf)) {
+        _FDT((fdt_property_string(fdt, "host-serial", buf))); +        g_free(buf);
+    }

Quoted 2 lines

Since you're emulating a "bare metal" machine, surely the host properties aren't relevant here.

They may or may not. But yes, I can take that out.

Quoted 47 lines
Quoted 47 lines

+    buf = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1], +                          qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], +                          qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], +                          qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], +                          qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], +                          qemu_uuid[14], qemu_uuid[15]);

hw/ppc/pnv.c new file +41 -0
+
+    _FDT((fdt_property_string(fdt, "vm,uuid", buf)));
+    g_free(buf);
+
+    _FDT((fdt_begin_node(fdt, "chosen")));
+    _FDT((fdt_property(fdt, "linux,initrd-start",
+                       &start_prop, sizeof(start_prop))));
+    _FDT((fdt_property(fdt, "linux,initrd-end",
+                       &end_prop, sizeof(end_prop))));
+    _FDT((fdt_end_node(fdt)));
+
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
+
+    /* cpus */
+    _FDT((fdt_begin_node(fdt, "cpus")));
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
+
+    CPU_FOREACH(cs) {
+        powernv_create_cpu_node(fdt, cs, smt);
+    }
+
+    _FDT((fdt_end_node(fdt)));
+
+    /* Memory */
+    _FDT((powernv_populate_memory(fdt)));
+
+    /* /hypervisor node */
+    if (kvm_enabled()) {
+        uint8_t hypercall[16];
+
+        /* indicate KVM hypercall interface */
+        _FDT((fdt_begin_node(fdt, "hypervisor")));
+        _FDT((fdt_property_string(fdt, "compatible", "linux,kvm")));
+        if (kvmppc_has_cap_fixup_hcalls()) {
+            /*
+             * Older KVM versions with older guest kernels were broken with the
+             * magic page, don't allow the guest to map it.
+             */
+            kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
Quoted 8 lines
Quoted 6 lines

+                                 sizeof(hypercall));
+            _FDT((fdt_property(fdt, "hcall-instructions", hypercall, +                              sizeof(hypercall))));
+        }
+        _FDT((fdt_end_node(fdt)));
+    }

And a hypercall interface surely doesn't make sense for powernv.

It's qemu paravirt, it exist on G5 too :-) It's for PR KVM, it allows to speed up some bits and pieces. But yeah we don't yet really "support" it at this point. However we might.

Quoted 28 lines
Quoted 11 lines
hw/ppc/pnv.c new file +11 -0
+
+    _FDT((fdt_end_node(fdt))); /* close root node */
+    _FDT((fdt_finish(fdt)));
+
+    return fdt;
+}
+
+static void powernv_cpu_reset(void *opaque)
+{
+    PowerPCCPU *cpu = opaque;
+    CPUState *cs = CPU(cpu);

hw/ppc/pnv.c line 409

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+    CPUPPCState *env = &cpu->env;
+
+    cpu_reset(cs);
+
+    env->spr[SPR_PIR] = ppc_get_vcpu_dt_id(cpu);
+    env->spr[SPR_HIOR] = 0;
+    env->gpr[3] = FDT_ADDR;
+    env->nip = 0x10;
+    env->msr |= MSR_HVB;
+}

So, I believe the qemu-ishly correct way of doing this is to have the cpu initialization in the cpu code, rather than the platform code, as much as possible.  On PAPR we kind of get away with initialization in the platform code on the grounds that it's a paravirt platform, but powernv doesn't have that excuse.

Quoted 1 line

But this may well be stuff that changes in later patches, so..

Well no, not really. But here too, we mimmic the state as coming out of HostBoot, which isn't quite the same thing. We need to provide the FDT entry, etc...

The "real" reset state of a P8 isn't something we can easily simulate...

It runs some microcode from a SEEPROM with a small microcontroller which initializes a core, which then runs some HB code off it's L3 cache etc... really not something we want to do in qemu at least for now.

So the initial state here is somewhat in between full virt and paravirt, we simulate a platform that has been partially initialized by HostBoot, to the state it has when it enters OPAL.

Quoted 9 lines

hw/ppc/pnv.c line 415

Quoted 5 lines
hw/ppc/pnv.c new file +5 -0
+static const VMStateDescription vmstate_powernv = {
+    .name = "powernv",
+    .version_id = 1,
+    .minimum_version_id = 1,
+};

It might be best to leave out the vmstate entirely until you're ready to implement migration, rather than having a partial, probably not working migration implementation.

Ok.

Quoted 6 lines
Quoted 6 lines
hw/ppc/pnv.c new file +6 -0
+
+static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no)
+{
+    PnvChip *chip = &sys->chips[chip_no];
+
+    if (chip_no >= PNV_MAX_CHIPS) {
Quoted 8 lines
Quoted 5 lines

+            return;
+    }
+
+    /* XXX Improve chip numbering to better match HW */ +    chip->chip_id = chip_no;

I think modern qemu conventions would suggest creating the chips as QOM objects rather than having a fixed array.

Yeah, more code & much larger memory footprint for the same result :-)

I can look into it but it's low priority. I still want to rework some of that chip stuff in future patches anyway.

Quoted 69 lines
Quoted 69 lines
hw/ppc/pnv.c new file +69 -0
+}
+
+static void ppc_powernv_init(MachineState *machine)
+{
+    ram_addr_t ram_size = machine->ram_size;
+    const char *cpu_model = machine->cpu_model;
+    const char *kernel_filename = machine->kernel_filename;
+    const char *initrd_filename = machine->initrd_filename;
+    uint32_t initrd_base = 0;
+    long initrd_size = 0;
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine);
+    PnvSystem *sys = &pnv_machine->sys;
+    long fw_size;
+    char *filename;
+    void *fdt;
+    int i;
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+        cpu_model = kvm_enabled() ? "host" : "POWER8";
+    }
+
+    for (i = 0; i < smp_cpus; i++) {
+        cpu = cpu_ppc_init(cpu_model);
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+            exit(1);
+        }
+        env = &cpu->env;
+
+        /* Set time-base frequency to 512 MHz */
+        cpu_ppc_tb_init(env, TIMEBASE_FREQ);
+
+        /* MSR[IP] doesn't exist nowadays */
+        env->msr_mask &= ~(1 << 6);
+
+        qemu_register_reset(powernv_cpu_reset, cpu);
+    }
+
+    /* allocate RAM */
+    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", ram_size);
+    memory_region_add_subregion(sysmem, 0, ram);
+
+    /* XXX We should decide how many chips to create based on #cores and
+     * Venice vs. Murano vs. Naples chip type etc..., for now, just create
+     * one chip. Also creation of the CPUs should be done per-chip
+     */
+    sys->num_chips = 1;
+
+    /* Create only one PHB for now until I figure out what's wrong
+     * when I create more (resource assignment failures in Linux)
+     */
+    pnv_create_chip(sys, 0);
+
+    if (bios_name == NULL) {
+        bios_name = FW_FILE_NAME;
+    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+    fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
+    if (fw_size < 0) {
+        hw_error("qemu: could not load OPAL '%s'\n", filename);
+        exit(1);
+    }
+    g_free(filename);
+
Quoted 8 lines
Quoted 6 lines

+
+    if (kernel_filename == NULL) {
+        kernel_filename = KERNEL_FILE_NAME; +    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, kernel_filename);

The commit withe Opal image should go in before this, no?

Now this is a bit of an open discussion at the moment :-)

The way OPAL is built on OPP machines today is by essentially building a complete flash image which contains HostBoot, OPAL and the petitboot- based bootloader which contains a Linux kernel etc...

We could create a target without HB and with a slimmed down Linux but it would still probably be about 12MB I reckon, if not more. It feels a bit "big" to ship as a binary as part of qemu...

We would also have to add code to qemu to "find" OPAL in that image, and then add a model for the flash controller.

The other option is to bundle just OPAL itself. However that means you can't go anywhere without a -kernel argument, which would then be either a petitboot-based bootloader or your actual target kernel.

Cheers,
Ben.

#78 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 26/77] ppc/pnv: Add skeletton PowerNV platform

Quoted 35 lines · Benjamin Herrenschmidt
Quoted 16 lines · David Gibson
Quoted 10 lines · Benjamin Herrenschmidt

No devices yet, not even an interrupt controller, just to get started.

---
 default-configs/ppc64-softmmu.mak |   1 +
 hw/ppc/Makefile.objs              |   2 +
 hw/ppc/pnv.c                      | 600
++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h              |  36 +++

Quoted 3 lines
 4 files changed, 639 insertions(+)

 create mode 100644 hw/ppc/pnv.c
 create mode 100644 include/hw/ppc/pnv.h

Quoted 2 lines

Many of my comments below may be made irrelevant by later patches in the series.

Heh, well there is where the "meat" of the new platform starts showing up :-)

 .../...

Quoted 10 lines
Quoted 9 lines

+#define _FDT(exp) \
+    do { \
+        int ret = (exp);                                           \
+        if (ret < 0) {                                             \
+            fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+                    #exp, fdt_strerror(ret));                      \
+            exit(1);                                               \
+        }                                                          \
+    } while (0)

Quoted 5 lines
Quoted 2 lines

We should probably make a file where helper routines used by both spapr.c and pnv.c can live.

Probably but I'd see that as a later cleanup rather than doing it in this series...

Ok.

Quoted 42 lines

.../...

Quoted 8 lines
Quoted 4 lines

+    if (pcc->l1_dcache_size) {
+        _FDT((fdt_property_cell(fdt, "d-cache-size", pcc->l1_dcache_size))); +    } else {
+        fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n");

Hmm (note to self) should probably change a bunch of these both in spapr.c and pnv.c from explicit fprintfs() to modern error_report() and similar.

That's a train I completely missed, but yes.

  .../...

Quoted 6 lines
Quoted 6 lines

+    }
+    _FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s", +                       servers_prop, sizeof(servers_prop)))); +    _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", +                       gservers_prop, sizeof(gservers_prop)))); +

Quoted 8 lines

hw/ppc/pnv.c line 305

Quoted 5 lines

+    _FDT((fdt_end_node(fdt)));

hw/ppc/pnv.c new file +4 -0
+}
+
+static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t initrd_size)
+{

So.. does it make sense for qemu to create a device tree for powernv, rather than leaving it to Opal?

Well, OPAL only creates a device-tree if you are on an FSP machine in which case it expects a complex data structure (HDAT) coming from the FSP to use as a source of info.

On OpenPower machines, which is closer to what we simulate here, we do get a device-tree as an input in OPAL, it's generated by HostBoot.

Now, I am not running HostBoot in qemu because most of what it does is completely irrelevant to an emulated system (training the various links, initializing the memory buffer chips, etc...).

Quoted 6 lines

However we do need to pass a number of platform information to OPAL which HB does via the device-tree, such as which cores are enabled, the memory map configured for PCI, which PHBs are enabled, etc...  so creating a DT in qemu makes sense, it mimmics HB in essence.

OPAL will enrich that device-tree before starting Linux.

Ok. Some comments mentioning that you're simulating the exit state from HostBoot would be good then.

Quoted 74 lines
Quoted 13 lines
Quoted 4 lines

+    /*
+     * Add info to guest to indentify which host is it being run on +     * and what is the uuid of the guest
+     */

Quoted 8 lines

+    if (kvmppc_get_host_model(&buf)) {
+        _FDT((fdt_property_string(fdt, "host-model", buf))); +        g_free(buf);
+    }
+    if (kvmppc_get_host_serial(&buf)) {
+        _FDT((fdt_property_string(fdt, "host-serial", buf))); +        g_free(buf);
+    }

Quoted 2 lines

Since you're emulating a "bare metal" machine, surely the host properties aren't relevant here.

They may or may not. But yes, I can take that out.

Quoted 47 lines
Quoted 47 lines

+    buf = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1], +                          qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], +                          qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], +                          qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], +                          qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], +                          qemu_uuid[14], qemu_uuid[15]);

hw/ppc/pnv.c new file +41 -0
+
+    _FDT((fdt_property_string(fdt, "vm,uuid", buf)));
+    g_free(buf);
+
+    _FDT((fdt_begin_node(fdt, "chosen")));
+    _FDT((fdt_property(fdt, "linux,initrd-start",
+                       &start_prop, sizeof(start_prop))));
+    _FDT((fdt_property(fdt, "linux,initrd-end",
+                       &end_prop, sizeof(end_prop))));
+    _FDT((fdt_end_node(fdt)));
+
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
+
+    /* cpus */
+    _FDT((fdt_begin_node(fdt, "cpus")));
+    _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
+
+    CPU_FOREACH(cs) {
+        powernv_create_cpu_node(fdt, cs, smt);
+    }
+
+    _FDT((fdt_end_node(fdt)));
+
+    /* Memory */
+    _FDT((powernv_populate_memory(fdt)));
+
+    /* /hypervisor node */
+    if (kvm_enabled()) {
+        uint8_t hypercall[16];
+
+        /* indicate KVM hypercall interface */
+        _FDT((fdt_begin_node(fdt, "hypervisor")));
+        _FDT((fdt_property_string(fdt, "compatible", "linux,kvm")));
+        if (kvmppc_has_cap_fixup_hcalls()) {
+            /*
+             * Older KVM versions with older guest kernels were broken with the
+             * magic page, don't allow the guest to map it.
+             */
+            kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
Quoted 8 lines
Quoted 6 lines

+                                 sizeof(hypercall));
+            _FDT((fdt_property(fdt, "hcall-instructions", hypercall, +                              sizeof(hypercall))));
+        }
+        _FDT((fdt_end_node(fdt)));
+    }

And a hypercall interface surely doesn't make sense for powernv.

Quoted 3 lines

It's qemu paravirt, it exist on G5 too :-) It's for PR KVM, it allows to speed up some bits and pieces. But yeah we don't yet really "support" it at this point. However we might.

Ah, yes, I forgot about that.

Quoted 37 lines
Quoted 28 lines
Quoted 11 lines
hw/ppc/pnv.c new file +11 -0
+
+    _FDT((fdt_end_node(fdt))); /* close root node */
+    _FDT((fdt_finish(fdt)));
+
+    return fdt;
+}
+
+static void powernv_cpu_reset(void *opaque)
+{
+    PowerPCCPU *cpu = opaque;
+    CPUState *cs = CPU(cpu);

hw/ppc/pnv.c line 409

Quoted 10 lines
hw/ppc/pnv.c new file +10 -0
+    CPUPPCState *env = &cpu->env;
+
+    cpu_reset(cs);
+
+    env->spr[SPR_PIR] = ppc_get_vcpu_dt_id(cpu);
+    env->spr[SPR_HIOR] = 0;
+    env->gpr[3] = FDT_ADDR;
+    env->nip = 0x10;
+    env->msr |= MSR_HVB;
+}

So, I believe the qemu-ishly correct way of doing this is to have the cpu initialization in the cpu code, rather than the platform code, as much as possible.  On PAPR we kind of get away with initialization in the platform code on the grounds that it's a paravirt platform, but powernv doesn't have that excuse.

Quoted 1 line

But this may well be stuff that changes in later patches, so..

Well no, not really. But here too, we mimmic the state as coming out of HostBoot, which isn't quite the same thing. We need to provide the FDT entry, etc...

The "real" reset state of a P8 isn't something we can easily simulate...

Quoted 8 lines

It runs some microcode from a SEEPROM with a small microcontroller which initializes a core, which then runs some HB code off it's L3 cache etc... really not something we want to do in qemu at least for now.

So the initial state here is somewhat in between full virt and paravirt, we simulate a platform that has been partially initialized by HostBoot, to the state it has when it enters OPAL.

Ok, that makes sense, but I think it needs a bit more explanation in the code to that effect.

Quoted 120 lines
Quoted 9 lines

hw/ppc/pnv.c line 415

Quoted 5 lines
hw/ppc/pnv.c new file +5 -0
+static const VMStateDescription vmstate_powernv = {
+    .name = "powernv",
+    .version_id = 1,
+    .minimum_version_id = 1,
+};

It might be best to leave out the vmstate entirely until you're ready to implement migration, rather than having a partial, probably not working migration implementation.

Ok.

Quoted 6 lines
Quoted 6 lines
hw/ppc/pnv.c new file +6 -0
+
+static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no)
+{
+    PnvChip *chip = &sys->chips[chip_no];
+
+    if (chip_no >= PNV_MAX_CHIPS) {
Quoted 8 lines
Quoted 5 lines

+            return;
+    }
+
+    /* XXX Improve chip numbering to better match HW */ +    chip->chip_id = chip_no;

I think modern qemu conventions would suggest creating the chips as QOM objects rather than having a fixed array.

Yeah, more code & much larger memory footprint for the same result :-)

I can look into it but it's low priority. I still want to rework some of that chip stuff in future patches anyway.

Quoted 69 lines
Quoted 69 lines
hw/ppc/pnv.c new file +69 -0
+}
+
+static void ppc_powernv_init(MachineState *machine)
+{
+    ram_addr_t ram_size = machine->ram_size;
+    const char *cpu_model = machine->cpu_model;
+    const char *kernel_filename = machine->kernel_filename;
+    const char *initrd_filename = machine->initrd_filename;
+    uint32_t initrd_base = 0;
+    long initrd_size = 0;
+    PowerPCCPU *cpu;
+    CPUPPCState *env;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine);
+    PnvSystem *sys = &pnv_machine->sys;
+    long fw_size;
+    char *filename;
+    void *fdt;
+    int i;
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+        cpu_model = kvm_enabled() ? "host" : "POWER8";
+    }
+
+    for (i = 0; i < smp_cpus; i++) {
+        cpu = cpu_ppc_init(cpu_model);
+        if (cpu == NULL) {
+            fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+            exit(1);
+        }
+        env = &cpu->env;
+
+        /* Set time-base frequency to 512 MHz */
+        cpu_ppc_tb_init(env, TIMEBASE_FREQ);
+
+        /* MSR[IP] doesn't exist nowadays */
+        env->msr_mask &= ~(1 << 6);
+
+        qemu_register_reset(powernv_cpu_reset, cpu);
+    }
+
+    /* allocate RAM */
+    memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", ram_size);
+    memory_region_add_subregion(sysmem, 0, ram);
+
+    /* XXX We should decide how many chips to create based on #cores and
+     * Venice vs. Murano vs. Naples chip type etc..., for now, just create
+     * one chip. Also creation of the CPUs should be done per-chip
+     */
+    sys->num_chips = 1;
+
+    /* Create only one PHB for now until I figure out what's wrong
+     * when I create more (resource assignment failures in Linux)
+     */
+    pnv_create_chip(sys, 0);
+
+    if (bios_name == NULL) {
+        bios_name = FW_FILE_NAME;
+    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+    fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
+    if (fw_size < 0) {
+        hw_error("qemu: could not load OPAL '%s'\n", filename);
+        exit(1);
+    }
+    g_free(filename);
+
Quoted 8 lines
Quoted 6 lines

+
+    if (kernel_filename == NULL) {
+        kernel_filename = KERNEL_FILE_NAME; +    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, kernel_filename);

The commit withe Opal image should go in before this, no?

Now this is a bit of an open discussion at the moment :-)

The way OPAL is built on OPP machines today is by essentially building a complete flash image which contains HostBoot, OPAL and the petitboot- based bootloader which contains a Linux kernel etc...

We could create a target without HB and with a slimmed down Linux but it would still probably be about 12MB I reckon, if not more. It feels a bit "big" to ship as a binary as part of qemu...

Quoted 6 lines

We would also have to add code to qemu to "find" OPAL in that image, and then add a model for the flash controller.

The other option is to bundle just OPAL itself. However that means you can't go anywhere without a -kernel argument, which would then be either a petitboot-based bootloader or your actual target kernel.

Hm, ok. But in order for this to be usable, we need some way to get a suitable image. So medium term, I think it makes sense to include both opal and PetitBoot, so you can boot the guest like a real machine.

However, including only Opal and requiring -kernel would be a reasonable interim step.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#79 lore raw

PATCH 27/77 ppc/pnv: Add XSCOM infrastructure

In reply to Benjamin Herrenschmidt

XSCOM is an interface to a sideband bus provided by the POWER8 chip pervasive unit, which gives access to a number of facilities in the chip that are needed by the OPAL firmware and to a lesser extent, Linux. This is among others how the PCI Host bridges get configured at boot or how the LPC bus is accessed.

This provides a simple bus and device type for devices sitting on XSCOM along with some facilities to optionally generate corresponding device-tree nodes

---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  11 ++
 hw/ppc/pnv_xscom.c         | 415 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   2 +
 include/hw/ppc/pnv_xscom.h |  73 ++++++++
 5 files changed, 502 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_xscom.c
 create mode 100644 include/hw/ppc/pnv_xscom.h
hw/ppc/Makefile.objs +1 -1
index cd74c96..2a7dd42 100644
@@ -5,7 +5,7 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
hw/ppc/pnv.c +11 -0
index e68c9b1..2eac877 100644
@@ -41,6 +41,7 @@
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/pnv.h"
 #include "hw/loader.h"
+#include "hw/ppc/pnv_xscom.h"
 
 #include "exec/address-spaces.h"
 #include "qemu/config-file.h"
@@ -310,6 +311,7 @@ static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t i
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     char *buf;
     const char plat_compat[] = "qemu,powernv\0ibm,powernv";
+    unsigned int i;
 
     fdt = g_malloc0(FDT_MAX_SIZE);
     _FDT((fdt_create(fdt, FDT_MAX_SIZE)));
@@ -367,6 +369,12 @@ static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t i
     /* Memory */
     _FDT((powernv_populate_memory(fdt)));
 
+    /* For each chip */
+    for (i = 0; i < sys->num_chips; i++) {
+        /* Populate XSCOM */
+        _FDT((xscom_populate_fdt(sys->chips[i].xscom, fdt)));
+    }
+
     /* /hypervisor node */
     if (kvm_enabled()) {
         uint8_t hypercall[16];
@@ -424,6 +432,9 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no)
 
     /* XXX Improve chip numbering to better match HW */
     chip->chip_id = chip_no;
+
+    /* Set up XSCOM bus */
+    xscom_create(chip);
 }
 
 static void ppc_powernv_init(MachineState *machine)
hw/ppc/pnv_xscom.c new file +415 -0

415 lines not on this page · raw message

include/hw/ppc/pnv.h +2 -0
index 9a48c16..cb157eb 100644
@@ -20,10 +20,12 @@
  */
 
 #include "hw/hw.h"
+typedef struct XScomBus XScomBus;
 
 /* Should we turn that into a QOjb of some sort ? */
 typedef struct PnvChip {
     uint32_t         chip_id;
+    XScomBus         *xscom;
 } PnvChip;
 
 typedef struct PnvSystem {
include/hw/ppc/pnv_xscom.h new file +73 -0

73 lines not on this page · raw message

Signature · 1 line
2.5.0
#80 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 27/77] ppc/pnv: Add XSCOM infrastructure

Quoted 68 lines · Benjamin Herrenschmidt

XSCOM is an interface to a sideband bus provided by the POWER8 chip pervasive unit, which gives access to a number of facilities in the chip that are needed by the OPAL firmware and to a lesser extent, Linux. This is among others how the PCI Host bridges get configured at boot or how the LPC bus is accessed.

This provides a simple bus and device type for devices sitting on XSCOM along with some facilities to optionally generate corresponding device-tree nodes

---
 hw/ppc/Makefile.objs       |   2 +-
 hw/ppc/pnv.c               |  11 ++
 hw/ppc/pnv_xscom.c         | 415 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/pnv.h       |   2 +
 include/hw/ppc/pnv_xscom.h |  73 ++++++++
 5 files changed, 502 insertions(+), 1 deletion(-)
 create mode 100644 hw/ppc/pnv_xscom.c
 create mode 100644 include/hw/ppc/pnv_xscom.h
hw/ppc/Makefile.objs +1 -1
index cd74c96..2a7dd42 100644
@@ -5,7 +5,7 @@ obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
 # IBM PowerNV
-obj-$(CONFIG_POWERNV) += pnv.o
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o
 ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
 obj-y += spapr_pci_vfio.o
 endif
hw/ppc/pnv.c +8 -0
index e68c9b1..2eac877 100644
@@ -41,6 +41,7 @@
 #include "hw/ppc/ppc.h"
 #include "hw/ppc/pnv.h"
 #include "hw/loader.h"
+#include "hw/ppc/pnv_xscom.h"
 
 #include "exec/address-spaces.h"
 #include "qemu/config-file.h"
@@ -310,6 +311,7 @@ static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t i
     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
     char *buf;
     const char plat_compat[] = "qemu,powernv\0ibm,powernv";
+    unsigned int i;
 
     fdt = g_malloc0(FDT_MAX_SIZE);
     _FDT((fdt_create(fdt, FDT_MAX_SIZE)));
@@ -367,6 +369,12 @@ static void *powernv_create_fdt(PnvSystem *sys, uint32_t initrd_base, uint32_t i
     /* Memory */
     _FDT((powernv_populate_memory(fdt)));
 
+    /* For each chip */
+    for (i = 0; i < sys->num_chips; i++) {
+        /* Populate XSCOM */
+        _FDT((xscom_populate_fdt(sys->chips[i].xscom, fdt)));
+    }
+
     /* /hypervisor node */
     if (kvm_enabled()) {
         uint8_t hypercall[16];

hw/ppc/pnv.c static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no) · line 437

Quoted 6 lines
hw/ppc/pnv.c +3 -0
@@ -424,6 +432,9 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no)
 
     /* XXX Improve chip numbering to better match HW */
     chip->chip_id = chip_no;
+
+    /* Set up XSCOM bus */
+    xscom_create(chip);

Hmm.. I'm thinking it probably makes sense to unify the representation of "chip" and "xscom" since there's a 1:1 correspondance. To be QOMishly correct, I think the right way would be a SysBusDevice for each chip, implementing the xscom MMIOs, then an array of link properties under the Machine object to find the right scoms by chip id.

Quoted 64 lines
hw/ppc/pnv.c +0 -0
 }
 
 static void ppc_powernv_init(MachineState *machine)
hw/ppc/pnv_xscom.c new file +55 -0
new file mode 100644
index 0000000..bb35422
@@ -0,0 +1,415 @@
+
+/*
+ * QEMU PowerNV XSCOM bus definitions
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <[email protected]>
+ * Based on the s390 virtio bus code:
+ * Copyright (c) 2009 Alexander Graf <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* TODO: Add some infrastructure for "random stuff" and FIRs that
+ * various units might want to deal with without creating actual
+ * XSCOM devices.
+ *
+ * For example, HB LPC XSCOM in the PIBAM
+ */
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/boards.h"
+#include "monitor/monitor.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "sysemu/device_tree.h"
+#include "kvm_ppc.h"
+
+#include "hw/ppc/pnv_xscom.h"
+
+#include <libfdt.h>
+
+#define TYPE_XSCOM "xscom"
+#define XSCOM(obj) OBJECT_CHECK(XScomState, (obj), TYPE_XSCOM)
+
+#define XSCOM_SIZE        0x800000000ull
+#define XSCOM_BASE(chip)  (0x3fc0000000000ull + ((uint64_t)(chip)) * XSCOM_SIZE)
+
+//#define TRACE_SCOMS
+
+typedef struct XScomState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/

hw/ppc/pnv_xscom.c line 65

Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+
+    MemoryRegion mem;
+    int32_t chip_id;
+    XScomBus *bus;
+} XScomState;
+
+static uint32_t xscom_to_pcb_addr(uint64_t addr)
+{
+        addr &= (XSCOM_SIZE - 1);
+        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);

Wow, that's a pretty weird address transform.

hw/ppc/pnv_xscom.c line 78

Quoted 13 lines
hw/ppc/pnv_xscom.c new file +13 -0
+}
+
+static void xscom_complete(uint64_t hmer_bits)
+{
+    CPUState *cs = current_cpu;
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+
+    cpu_synchronize_state(cs);
+    env->spr[SPR_HMER] |= hmer_bits;
+
+    /* XXX Need a CPU helper to set HMER, also handle gneeration
+     * of HMIs

Not sure what you're referring to here. Nothing more should be needed to set the HMER - because you've called cpu_synchronize_state() it will be marked dirty and flushed back to KVM before re-entry.

Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+     */
+}
+
+static XScomDevice *xscom_find_target(XScomState *s, uint32_t pcb_addr, uint32_t *range)
+{
+    BusChild *bc;
+
+    QTAILQ_FOREACH(bc, &s->bus->bus.children, sibling) {
+        DeviceState *qd = bc->child;
+        XScomDevice *xd = XSCOM_DEVICE(qd);

hw/ppc/pnv_xscom.c line 98

Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+        unsigned int i;
+
+        for (i = 0; i < MAX_XSCOM_RANGES; i++) {
+            if (xd->ranges[i].addr <= pcb_addr &&
+                (xd->ranges[i].addr + xd->ranges[i].size) > pcb_addr) {
+                *range = i;
+                return xd;
+            }
+        }
+    }

I'm wondering if it makes sense to construct a custom AddressSpace and use the existing address space lookup logic from exec.c and memory.c rather than implementing your own.

Quoted 38 lines
hw/ppc/pnv_xscom.c new file +38 -0
+    return NULL;
+}
+
+static bool xscom_dispatch_read(XScomState *s, uint32_t pcb_addr, uint64_t *out_val)
+{
+    uint32_t range, offset;
+    struct XScomDevice *xd = xscom_find_target(s, pcb_addr, &range);
+    XScomDeviceClass *xc;
+
+    if (!xd) {
+        return false;
+    }
+    xc = XSCOM_DEVICE_GET_CLASS(xd);
+    if (!xc->read) {
+        return false;
+    }
+    offset = pcb_addr - xd->ranges[range].addr;
+    return xc->read(xd, range, offset, out_val);
+}
+
+static bool xscom_dispatch_write(XScomState *s, uint32_t pcb_addr, uint64_t val)
+{
+    uint32_t range, offset;
+    struct XScomDevice *xd = xscom_find_target(s, pcb_addr, &range);
+    XScomDeviceClass *xc;
+
+    if (!xd) {
+        return false;
+    }
+    xc = XSCOM_DEVICE_GET_CLASS(xd);
+    if (!xc->write) {
+        return false;
+    }
+    offset = pcb_addr - xd->ranges[range].addr;
+    return xc->write(xd, range, offset, val);
+}
+
+static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)

hw/ppc/pnv_xscom.c line 146

Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+{
+    XScomState *s = opaque;
+    uint32_t pcba = xscom_to_pcb_addr(addr);
+    uint64_t val;
+
+    assert(width == 8);
+
+#ifdef TRACE_SCOMS
+    printf("XSCOM_READ(0x%x:0x%x)\n", s->chip_id, pcba);
+#endif

You should be using the built in trace infrastructure here - it's really not that much of a pain. Put

	trace_xscom_read(s->chip_id, pcba)

here, put a suitable format in trace-events, and ./configure --enable-trace-backends=stderr

Quoted 85 lines
hw/ppc/pnv_xscom.c new file +85 -0
+
+    /* Handle some SCOMs here before dispatch */
+    switch(pcba) {
+    case 0xf000f:
+        val = 0x221EF04980000000;
+        break;
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        val = 0;
+        break;
+    case 0x2013f00:     /* PBA stuff */
+    case 0x2013f01:     /* PBA stuff */
+    case 0x2013f02:     /* PBA stuff */
+    case 0x2013f03:     /* PBA stuff */
+    case 0x2013f04:     /* PBA stuff */
+    case 0x2013f05:     /* PBA stuff */
+    case 0x2013f06:     /* PBA stuff */
+    case 0x2013f07:     /* PBA stuff */
+        val = 0;
+        break;
+    default:
+        if (!xscom_dispatch_read(s, pcba, &val)) {
+            xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+            return 0;
+        }
+    }
+
+    xscom_complete(HMER_XSCOM_DONE);
+    return val;
+}
+
+static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
+                        unsigned width)
+{
+    XScomState *s = opaque;
+    uint32_t pcba = xscom_to_pcb_addr(addr);
+
+    assert(width == 8);
+
+#ifdef TRACE_SCOMS
+    printf("XSCOM_WRITE(0x%x:0x%x, 0x%016llx)\n",
+           s->chip_id, pcba, (unsigned long long)val);
+#endif
+    /* Handle some SCOMs here before dispatch */
+    switch(pcba) {
+        /* We ignore writes to these */
+    case 0xf000f:       /* chip id is RO */
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c01:     /* PIBAM FIR */
+    case 0x1010c02:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x1010c04:     /* PIBAM FIR MASK */
+    case 0x1010c05:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        break;
+    default:
+        if (!xscom_dispatch_write(s, pcba, val)) {
+            xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+            return;
+        }
+    }
+
+    xscom_complete(HMER_XSCOM_DONE);
+}
+
+static const MemoryRegionOps xscom_ops = {
+    .read = xscom_read,
+    .write = xscom_write,
+    .valid.min_access_size = 8,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 8,
+    .impl.max_access_size = 8,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int xscom_init(SysBusDevice *dev)
+{
+    XScomState *s = XSCOM(dev);
+
+    s->chip_id = -1;

hw/ppc/pnv_xscom.c line 241

Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+    return 0;
+}
+
+static void xscom_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    XScomState *s = XSCOM(dev);
+    char *name;
+
+    assert(s->chip_id >= 0);

So, this assert could be tripped if the user explicitly instantiated an xscom device which they probably shouldn't do, but could. So, it probably makes sense to use error_setg() here instead of assert().

Quoted 47 lines
hw/ppc/pnv_xscom.c new file +47 -0
+    name = g_strdup_printf("xscom-%x", s->chip_id);
+    memory_region_init_io(&s->mem, OBJECT(s), &xscom_ops, s, name, XSCOM_SIZE);
+    sysbus_init_mmio(sbd, &s->mem);
+    sysbus_mmio_map(sbd, 0, XSCOM_BASE(s->chip_id));
+}
+
+static Property xscom_properties[] = {
+        DEFINE_PROP_INT32("chip_id", XScomState, chip_id, 0),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xscom_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = xscom_properties;
+    dc->realize = xscom_realize;
+    k->init = xscom_init;
+}
+
+static const TypeInfo xscom_info = {
+    .name          = TYPE_XSCOM,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XScomState),
+    .class_init    = xscom_class_init,
+};
+
+static void xscom_bus_class_init(ObjectClass *klass, void *data)
+{
+}
+
+static const TypeInfo xscom_bus_info = {
+    .name = TYPE_XSCOM_BUS,
+    .parent = TYPE_BUS,
+    .class_init = xscom_bus_class_init,
+    .instance_size = sizeof(XScomBus),
+};
+
+void xscom_create(PnvChip *chip)
+{
+    DeviceState *dev;
+    XScomState *xdev;
+    BusState *qbus;
+    XScomBus *xb;
+
+    dev = qdev_create(NULL, TYPE_XSCOM);

hw/ppc/pnv_xscom.c line 298

Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+    qdev_prop_set_uint32(dev, "chip_id", chip->chip_id);
+    qdev_init_nofail(dev);
+
+    /* Create bus on bridge device */
+    qbus = qbus_create(TYPE_XSCOM_BUS, dev, "xscom");
+    xb = DO_UPCAST(XScomBus, bus, qbus);
+    xb->chip_id = chip->chip_id;
+    xdev = XSCOM(dev);
+    xdev->bus = xb;
+    chip->xscom = xb;

I believe the qbus_create() is usually invoked by the bridge's init function, rather than externally.

Quoted 213 lines
hw/ppc/pnv_xscom.c new file +117 -0
+}
+
+#define _FDT(exp) \
+    do { \
+        int ret = (exp);                                           \
+        if (ret < 0) {                                             \
+            fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+                    #exp, fdt_strerror(ret));                      \
+            exit(1);                                               \
+        }                                                          \
+    } while (0)
+
+
+int xscom_populate_fdt(XScomBus *xb, void *fdt)
+{
+    BusChild *bc;
+    char *name;
+    const char compat[] = "ibm,power8-xscom\0ibm,xscom";
+    uint64_t reg[] = { cpu_to_be64(XSCOM_BASE(xb->chip_id)),
+                       cpu_to_be64(XSCOM_SIZE) };
+
+    name = g_strdup_printf("xscom@%llx", (unsigned long long)be64_to_cpu(reg[0]));
+    _FDT((fdt_begin_node(fdt, name)));
+    g_free(name);
+    _FDT((fdt_property_cell(fdt, "ibm,chip-id", xb->chip_id)));
+    _FDT((fdt_property_cell(fdt, "#address-cells", 1)));
+    _FDT((fdt_property_cell(fdt, "#size-cells", 1)));
+    _FDT((fdt_property(fdt, "reg", reg, sizeof(reg))));
+    _FDT((fdt_property(fdt, "compatible", compat, sizeof(compat)))); 
+    _FDT((fdt_property(fdt, "scom-controller", NULL, 0))); 
+
+    QTAILQ_FOREACH(bc, &xb->bus.children, sibling) {
+        DeviceState *qd = bc->child;
+        XScomDevice *xd = XSCOM_DEVICE(qd);
+        XScomDeviceClass *xc = XSCOM_DEVICE_GET_CLASS(xd);
+        uint32_t reg[MAX_XSCOM_RANGES * 2];
+        unsigned int i, sz = 0;
+        void *cp, *p;
+
+        /* Some XSCOM slaves may not be represented in the DT */
+        if (!xc->dt_name) {
+            continue;
+        }
+        name = g_strdup_printf("%s@%x", xc->dt_name, xd->ranges[0].addr);
+        _FDT((fdt_begin_node(fdt, name)));
+        g_free(name);
+        for (i = 0; i < MAX_XSCOM_RANGES; i++) {
+            if (xd->ranges[i].size == 0) {
+                break;
+            }
+            reg[sz++] = cpu_to_be32(xd->ranges[i].addr);
+            reg[sz++] = cpu_to_be32(xd->ranges[i].size);
+        }
+        _FDT((fdt_property(fdt, "reg", reg, sz * 4)));
+        if (xc->devnode) {
+            _FDT((xc->devnode(xd, fdt)));
+        }
+#define MAX_COMPATIBLE_PROP     1024
+        cp = p = g_malloc0(MAX_COMPATIBLE_PROP);
+        i = 0;
+        while((p - cp) < MAX_COMPATIBLE_PROP) {
+            int l;
+            if (xc->dt_compatible[i] == NULL) {
+                break;
+            }
+            l = strlen(xc->dt_compatible[i]);
+            if (l >= (MAX_COMPATIBLE_PROP - i)) {
+                break;
+            }
+            strcpy(p, xc->dt_compatible[i++]);
+            p += l + 1;
+        }
+        _FDT((fdt_property(fdt, "compatible", cp, p - cp)));
+        _FDT((fdt_end_node(fdt)));
+    }
+
+    _FDT((fdt_end_node(fdt)));
+
+    return 0;
+}
+
+static int xscom_qdev_init(DeviceState *qdev)
+{
+    XScomDevice *xdev = (XScomDevice *)qdev;
+    XScomDeviceClass *xc = XSCOM_DEVICE_GET_CLASS(xdev);
+
+    if (xc->init) {
+        return xc->init(xdev);
+    }
+    return 0;
+}
+
+static void xscom_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->init = xscom_qdev_init;
+    k->bus_type = TYPE_XSCOM_BUS;
+}
+
+static const TypeInfo xscom_dev_info = {
+    .name = TYPE_XSCOM_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(XScomDevice),
+    .abstract = true,
+    .class_size = sizeof(XScomDeviceClass),
+    .class_init = xscom_device_class_init,
+};
+
+static void xscom_register_types(void)
+{
+    type_register_static(&xscom_info);
+    type_register_static(&xscom_bus_info);
+    type_register_static(&xscom_dev_info);
+}
+
+type_init(xscom_register_types)
+
include/hw/ppc/pnv.h +2 -0
index 9a48c16..cb157eb 100644
@@ -20,10 +20,12 @@
  */
 
 #include "hw/hw.h"
+typedef struct XScomBus XScomBus;
 
 /* Should we turn that into a QOjb of some sort ? */
 typedef struct PnvChip {
     uint32_t         chip_id;
+    XScomBus         *xscom;
 } PnvChip;
 
 typedef struct PnvSystem {
include/hw/ppc/pnv_xscom.h new file +73 -0
new file mode 100644
index 0000000..99de078
@@ -0,0 +1,73 @@
+#ifndef _HW_XSCOM_H
+#define _HW_XSCOM_H
+/*
+ * QEMU PowerNV XSCOM bus definitions
+ *
+ * Copyright (c) 2010 David Gibson, IBM Corporation <[email protected]>
+ * Based on the s390 virtio bus definitions:
+ * Copyright (c) 2009 Alexander Graf <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <hw/ppc/pnv.h>
+
+#define TYPE_XSCOM_DEVICE "xscom-device"
+#define XSCOM_DEVICE(obj) \
+     OBJECT_CHECK(XScomDevice, (obj), TYPE_XSCOM_DEVICE)
+#define XSCOM_DEVICE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(XScomDeviceClass, (klass), TYPE_XSCOM_DEVICE)
+#define XSCOM_DEVICE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(XScomDeviceClass, (obj), TYPE_XSCOM_DEVICE)
+
+#define TYPE_XSCOM_BUS "xscom-bus"
+#define XSCOM_BUS(obj) OBJECT_CHECK(XScomBus, (obj), TYPE_XSCOM_BUS)
+
+typedef struct XScomDevice XScomDevice;
+typedef struct XScomBus XScomBus;
+
+typedef struct XScomDeviceClass {
+    DeviceClass parent_class;
+
+    const char *dt_name;
+    const char **dt_compatible;
+    int (*init)(XScomDevice *dev);
+    int (*devnode)(XScomDevice *dev, void *fdt);
+
+    /* Actual XScom accesses */
+    bool (*read)(XScomDevice *dev, uint32_t range, uint32_t offset, uint64_t *out_val);
+    bool (*write)(XScomDevice *dev, uint32_t range, uint32_t offset, uint64_t val);
+} XScomDeviceClass;
+
+typedef struct XScomRange {
+    uint32_t addr;
+    uint32_t size;
+} XScomRange;
+
+struct XScomDevice {
+    DeviceState qdev;
+#define MAX_XSCOM_RANGES	4
+    struct XScomRange ranges[MAX_XSCOM_RANGES];
+};
+
+struct XScomBus {
+    BusState bus;
+    uint32_t chip_id;
+};
+
+extern void xscom_create(PnvChip *chip);
+extern int xscom_populate_fdt(XScomBus *xscom, void *fdt);
+
+
+#endif /* _HW_XSCOM_H */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#81 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 27/77] ppc/pnv: Add XSCOM infrastructure

Quoted 5 lines · David Gibson

Hmm.. I'm thinking it probably makes sense to unify the representation of "chip" and "xscom" since there's a 1:1 correspondance.  To be QOMishly correct, I think the right way would be a SysBusDevice for each chip, implementing the xscom MMIOs, then an array of link properties under the Machine object to find the right scoms by chip id.

But there is more than xscom's under each chip ... there's PHBs, LPC controllers (though those are currently under XSCOM) and possibly other completely unrelated things...

To be honest I've never been a big fan of that whole QOM business and am a bit confused as to how that would all work together.

Cheers,
Ben.

#82 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 27/77] ppc/pnv: Add XSCOM infrastructure

In reply to David Gibson
Quoted 7 lines · David Gibson

hw/ppc/pnv_xscom.c line 65

Quoted 5 lines
hw/ppc/pnv_xscom.c new file +4 -0
+static uint32_t xscom_to_pcb_addr(uint64_t addr)
+{
+        addr &= (XSCOM_SIZE - 1);
+        return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);

Wow, that's a pretty weird address transform.

Indeed :-) That's how it is in HW. It's also a bit different between chip generations.

Quoted 10 lines
Quoted 10 lines
hw/ppc/pnv_xscom.c new file +10 -0
+}
+
+static void xscom_complete(uint64_t hmer_bits)
+{
+    CPUState *cs = current_cpu;
+    PowerPCCPU *cpu = POWERPC_CPU(cs);
+    CPUPPCState *env = &cpu->env;
+
+    cpu_synchronize_state(cs);
+    env->spr[SPR_HMER] |= hmer_bits;
Quoted 8 lines
Quoted 3 lines

+
+    /* XXX Need a CPU helper to set HMER, also handle gneeration +     * of HMIs

Not sure what you're referring to here.  Nothing more should be needed to set the HMER - because you've called cpu_synchronize_state() it will be marked dirty and flushed back to KVM before re-entry.

No it's not that. Setting HMER can potentially generate an HMI interrupt (if enabled in HMEER). We never use the interrupt corresponding to XSCOMs in FW though, so that's why I haven't bothered yet.

Quoted 17 lines
Quoted 17 lines

+     */

hw/ppc/pnv_xscom.c new file +16 -0
+}
+
+static XScomDevice *xscom_find_target(XScomState *s, uint32_t pcb_addr, uint32_t *range)
+{
+    BusChild *bc;
+
+    QTAILQ_FOREACH(bc, &s->bus->bus.children, sibling) {
+        DeviceState *qd = bc->child;
+        XScomDevice *xd = XSCOM_DEVICE(qd);
+        unsigned int i;
+
+        for (i = 0; i < MAX_XSCOM_RANGES; i++) {
+            if (xd->ranges[i].addr <= pcb_addr &&
+                (xd->ranges[i].addr + xd->ranges[i].size) > pcb_addr) {
+                *range = i;
+                return xd;
Quoted 8 lines
Quoted 3 lines

+            }
+        }
+    }

I'm wondering if it makes sense to construct a custom AddressSpace and
use the existing address space lookup logic from exec.c and memory.c rather than implementing your own.

Maybe but we'd then have to shift everything by 3 bits, which means the "XSCOM addresses" would no longer match the doc unless we use some kind of macro to do the shifting.

Quoted 46 lines
Quoted 46 lines

+    return NULL;

hw/ppc/pnv_xscom.c new file +45 -0
+}
+
+static bool xscom_dispatch_read(XScomState *s, uint32_t pcb_addr, uint64_t *out_val)
+{
+    uint32_t range, offset;
+    struct XScomDevice *xd = xscom_find_target(s, pcb_addr, &range);
+    XScomDeviceClass *xc;
+
+    if (!xd) {
+        return false;
+    }
+    xc = XSCOM_DEVICE_GET_CLASS(xd);
+    if (!xc->read) {
+        return false;
+    }
+    offset = pcb_addr - xd->ranges[range].addr;
+    return xc->read(xd, range, offset, out_val);
+}
+
+static bool xscom_dispatch_write(XScomState *s, uint32_t pcb_addr, uint64_t val)
+{
+    uint32_t range, offset;
+    struct XScomDevice *xd = xscom_find_target(s, pcb_addr, &range);
+    XScomDeviceClass *xc;
+
+    if (!xd) {
+        return false;
+    }
+    xc = XSCOM_DEVICE_GET_CLASS(xd);
+    if (!xc->write) {
+        return false;
+    }
+    offset = pcb_addr - xd->ranges[range].addr;
+    return xc->write(xd, range, offset, val);
+}
+
+static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
+{
+    XScomState *s = opaque;
+    uint32_t pcba = xscom_to_pcb_addr(addr);
+    uint64_t val;
+
+    assert(width == 8);
+
+#ifdef TRACE_SCOMS
Quoted 8 lines
Quoted 2 lines

+    printf("XSCOM_READ(0x%x:0x%x)\n", s->chip_id, pcba); +#endif

You should be using the built in trace infrastructure here - it's really not that much of a pain.  Put trace_xscom_read(s->chip_id, pcba)
here, put a suitable format in trace-events, and ./configure --enable-trace-backends=stderr

I'll investigate this.

Quoted 91 lines
Quoted 91 lines
hw/ppc/pnv_xscom.c new file +91 -0
+
+    /* Handle some SCOMs here before dispatch */
+    switch(pcba) {
+    case 0xf000f:
+        val = 0x221EF04980000000;
+        break;
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        val = 0;
+        break;
+    case 0x2013f00:     /* PBA stuff */
+    case 0x2013f01:     /* PBA stuff */
+    case 0x2013f02:     /* PBA stuff */
+    case 0x2013f03:     /* PBA stuff */
+    case 0x2013f04:     /* PBA stuff */
+    case 0x2013f05:     /* PBA stuff */
+    case 0x2013f06:     /* PBA stuff */
+    case 0x2013f07:     /* PBA stuff */
+        val = 0;
+        break;
+    default:
+        if (!xscom_dispatch_read(s, pcba, &val)) {
+            xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+            return 0;
+        }
+    }
+
+    xscom_complete(HMER_XSCOM_DONE);
+    return val;
+}
+
+static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
+                        unsigned width)
+{
+    XScomState *s = opaque;
+    uint32_t pcba = xscom_to_pcb_addr(addr);
+
+    assert(width == 8);
+
+#ifdef TRACE_SCOMS
+    printf("XSCOM_WRITE(0x%x:0x%x, 0x%016llx)\n",
+           s->chip_id, pcba, (unsigned long long)val);
+#endif
+    /* Handle some SCOMs here before dispatch */
+    switch(pcba) {
+        /* We ignore writes to these */
+    case 0xf000f:       /* chip id is RO */
+    case 0x1010c00:     /* PIBAM FIR */
+    case 0x1010c01:     /* PIBAM FIR */
+    case 0x1010c02:     /* PIBAM FIR */
+    case 0x1010c03:     /* PIBAM FIR MASK */
+    case 0x1010c04:     /* PIBAM FIR MASK */
+    case 0x1010c05:     /* PIBAM FIR MASK */
+    case 0x2020007:     /* ADU stuff */
+    case 0x2020009:     /* ADU stuff */
+    case 0x202000f:     /* ADU stuff */
+        break;
+    default:
+        if (!xscom_dispatch_write(s, pcba, val)) {
+            xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+            return;
+        }
+    }
+
+    xscom_complete(HMER_XSCOM_DONE);
+}
+
+static const MemoryRegionOps xscom_ops = {
+    .read = xscom_read,
+    .write = xscom_write,
+    .valid.min_access_size = 8,
+    .valid.max_access_size = 8,
+    .impl.min_access_size = 8,
+    .impl.max_access_size = 8,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int xscom_init(SysBusDevice *dev)
+{
+    XScomState *s = XSCOM(dev);
+
+    s->chip_id = -1;
+    return 0;
+}
+
+static void xscom_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
Quoted 8 lines
Quoted 4 lines

+    XScomState *s = XSCOM(dev);
+    char *name;
+
+    assert(s->chip_id >= 0);

So, this assert could be tripped if the user explicitly instantiated an xscom device which they probably shouldn't do, but could.  So, it probably makes sense to use error_setg() here instead of assert().

No idea what error_setg() is, I'll look into it :)

Quoted 53 lines
Quoted 2 lines

+    name = g_strdup_printf("xscom-%x", s->chip_id);
+    memory_region_init_io(&s->mem, OBJECT(s), &xscom_ops, s, name,

XSCOM_SIZE);

Quoted 50 lines

+    sysbus_init_mmio(sbd, &s->mem);
+    sysbus_mmio_map(sbd, 0, XSCOM_BASE(s->chip_id));

hw/ppc/pnv_xscom.c new file +48 -0
+}
+
+static Property xscom_properties[] = {
+        DEFINE_PROP_INT32("chip_id", XScomState, chip_id, 0),
+        DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xscom_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = xscom_properties;
+    dc->realize = xscom_realize;
+    k->init = xscom_init;
+}
+
+static const TypeInfo xscom_info = {
+    .name          = TYPE_XSCOM,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XScomState),
+    .class_init    = xscom_class_init,
+};
+
+static void xscom_bus_class_init(ObjectClass *klass, void *data)
+{
+}
+
+static const TypeInfo xscom_bus_info = {
+    .name = TYPE_XSCOM_BUS,
+    .parent = TYPE_BUS,
+    .class_init = xscom_bus_class_init,
+    .instance_size = sizeof(XScomBus),
+};
+
+void xscom_create(PnvChip *chip)
+{
+    DeviceState *dev;
+    XScomState *xdev;
+    BusState *qbus;
+    XScomBus *xb;
+
+    dev = qdev_create(NULL, TYPE_XSCOM);
+    qdev_prop_set_uint32(dev, "chip_id", chip->chip_id);
+    qdev_init_nofail(dev);
+
+    /* Create bus on bridge device */
+    qbus = qbus_create(TYPE_XSCOM_BUS, dev, "xscom");
Quoted 8 lines
Quoted 5 lines

+    xb = DO_UPCAST(XScomBus, bus, qbus);
+    xb->chip_id = chip->chip_id;
+    xdev = XSCOM(dev);
+    xdev->bus = xb;
+    chip->xscom = xb;

I believe the qbus_create() is usually invoked by the bridge's init function, rather than externally.

Init or realize ?

Cheers,
Ben.

#83 lore raw

PATCH 28/77 ppc/xics: Rename existing XICS classe to XICS_SPAPR

In reply to Benjamin Herrenschmidt

The common class doesn't change, the KVM one is SPAPR specific, this is a preliminary change to make it easier to support "native" XICS.

---
 hw/intc/xics.c        | 26 +++++++++++++-------------
 hw/intc/xics_kvm.c    |  6 +++---
 hw/ppc/spapr.c        |  6 +++---
 hw/ppc/spapr_events.c |  2 +-
 hw/ppc/spapr_pci.c    |  8 ++++----
 hw/ppc/spapr_vio.c    |  2 +-
 include/hw/ppc/xics.h | 25 +++++++++++++------------
 7 files changed, 38 insertions(+), 37 deletions(-)
hw/intc/xics.c +13 -13
index 9ff5796..bcea1f0 100644
@@ -711,7 +711,7 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
     return -1;
 }
 
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
+int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
 {
     ICSState *ics = &icp->ics[src];
     int irq;
@@ -742,7 +742,7 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
  * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
  * If align==true, aligns the first IRQ number to num.
  */
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
+int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
 {
     int i, first = -1;
     ICSState *ics = &icp->ics[src];
@@ -787,7 +787,7 @@ static void ics_free(ICSState *ics, int srcno, int num)
     }
 }
 
-void xics_free(XICSState *icp, int irq, int num)
+void xics_spapr_free(XICSState *icp, int irq, int num)
 {
     int src = xics_find_source(icp, irq);
 
@@ -1006,7 +1006,7 @@ static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
     }
 }
 
-static void xics_realize(DeviceState *dev, Error **errp)
+static void xics_spapr_realize(DeviceState *dev, Error **errp)
 {
     XICSState *icp = XICS(dev);
     Error *error = NULL;
@@ -1045,7 +1045,7 @@ static void xics_realize(DeviceState *dev, Error **errp)
     }
 }
 
-static void xics_initfn(Object *obj)
+static void xics_spapr_initfn(Object *obj)
 {
     XICSState *xics = XICS(obj);
 
@@ -1054,29 +1054,29 @@ static void xics_initfn(Object *obj)
     xics->ics->icp = xics;
 }
 
-static void xics_class_init(ObjectClass *oc, void *data)
+static void xics_spapr_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
-    XICSStateClass *xsc = XICS_CLASS(oc);
+    XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
 
-    dc->realize = xics_realize;
+    dc->realize = xics_spapr_realize;
     xsc->set_nr_irqs = xics_set_nr_irqs;
     xsc->set_nr_servers = xics_set_nr_servers;
 }
 
-static const TypeInfo xics_info = {
-    .name          = TYPE_XICS,
+static const TypeInfo xics_spapr_info = {
+    .name          = TYPE_XICS_SPAPR,
     .parent        = TYPE_XICS_COMMON,
     .instance_size = sizeof(XICSState),
     .class_size = sizeof(XICSStateClass),
-    .class_init    = xics_class_init,
-    .instance_init = xics_initfn,
+    .class_init    = xics_spapr_class_init,
+    .instance_init = xics_spapr_initfn,
 };
 
 static void xics_register_types(void)
 {
     type_register_static(&xics_common_info);
-    type_register_static(&xics_info);
+    type_register_static(&xics_spapr_info);
     type_register_static(&ics_info);
     type_register_static(&icp_info);
 }
hw/intc/xics_kvm.c +3 -3
index d58729c..03ae801 100644
@@ -490,8 +490,8 @@ static void xics_kvm_class_init(ObjectClass *oc, void *data)
     xsc->set_nr_servers = xics_kvm_set_nr_servers;
 }
 
-static const TypeInfo xics_kvm_info = {
-    .name          = TYPE_KVM_XICS,
+static const TypeInfo xics_spapr_kvm_info = {
+    .name          = TYPE_XICS_SPAPR_KVM,
     .parent        = TYPE_XICS_COMMON,
     .instance_size = sizeof(KVMXICSState),
     .class_init    = xics_kvm_class_init,
@@ -500,7 +500,7 @@ static const TypeInfo xics_kvm_info = {
 
 static void xics_kvm_register_types(void)
 {
-    type_register_static(&xics_kvm_info);
+    type_register_static(&xics_spapr_kvm_info);
     type_register_static(&ics_kvm_info);
     type_register_static(&icp_kvm_info);
 }
hw/ppc/spapr.c +3 -3
index 610629e..bf94426 100644
@@ -119,7 +119,7 @@ static XICSState *xics_system_init(MachineState *machine,
         Error *err = NULL;
 
         if (machine_kernel_irqchip_allowed(machine)) {
-            icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
+            icp = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs, &err);
         }
         if (machine_kernel_irqchip_required(machine) && !icp) {
             error_report("kernel_irqchip requested but unavailable: %s",
@@ -128,7 +128,7 @@ static XICSState *xics_system_init(MachineState *machine,
     }
 
     if (!icp) {
-        icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort);
+        icp = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, &error_abort);
     }
 
     return icp;
@@ -1768,7 +1768,7 @@ static void ppc_spapr_init(MachineState *machine)
     spapr->icp = xics_system_init(machine,
                                   DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(),
                                                smp_threads),
-                                  XICS_IRQS);
+                                  XICS_IRQS_SPAPR);
 
     if (smc->dr_lmb_enabled) {
         spapr_validate_node_memory(machine);
hw/ppc/spapr_events.c +1 -1
index 744ea62..3b3663e 100644
@@ -587,7 +587,7 @@ out_no_events:
 void spapr_events_init(sPAPRMachineState *spapr)
 {
     QTAILQ_INIT(&spapr->pending_events);
-    spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
+    spapr->check_exception_irq = xics_spapr_alloc(spapr->icp, 0, 0, false);
     spapr->epow_notifier.notify = spapr_powerdown_req;
     qemu_register_powerdown_notifier(&spapr->epow_notifier);
     spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
hw/ppc/spapr_pci.c +4 -4
index 55fa8db..8b613a8 100644
@@ -313,7 +313,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
             return;
         }
 
-        xics_free(spapr->icp, msi->first_irq, msi->num);
+        xics_spapr_free(spapr->icp, msi->first_irq, msi->num);
         if (msi_present(pdev)) {
             spapr_msi_setmsg(pdev, 0, false, 0, num);
         }
@@ -351,7 +351,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     }
 
     /* Allocate MSIs */
-    irq = xics_alloc_block(spapr->icp, 0, req_num, false,
+    irq = xics_spapr_alloc_block(spapr->icp, 0, req_num, false,
                            ret_intr_type == RTAS_TYPE_MSI);
     if (!irq) {
         error_report("Cannot allocate MSIs for device %x", config_addr);
@@ -1360,7 +1360,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
     for (i = 0; i < PCI_NUM_PINS; i++) {
         uint32_t irq;
 
-        irq = xics_alloc_block(spapr->icp, 0, 1, true, false);
+        irq = xics_spapr_alloc_block(spapr->icp, 0, 1, true, false);
         if (!irq) {
             error_setg(errp, "spapr_allocate_lsi failed");
             return;
@@ -1717,7 +1717,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
     _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
     _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
     _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
-    _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS));
+    _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
 
     /* Build the interrupt-map, this must matches what is done
      * in pci_spapr_map_irq
hw/ppc/spapr_vio.c +1 -1
index c51eb8e..7b718cc 100644
@@ -462,7 +462,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
         dev->qdev.id = id;
     }
 
-    dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false);
+    dev->irq = xics_spapr_alloc(spapr->icp, 0, dev->irq, false);
     if (!dev->irq) {
         error_setg(errp, "can't allocate IRQ");
         return;
include/hw/ppc/xics.h +13 -12
index 355a966..8d33dfa 100644
@@ -32,20 +32,20 @@
 #define TYPE_XICS_COMMON "xics-common"
 #define XICS_COMMON(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_COMMON)
 
-#define TYPE_XICS "xics"
-#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS)
+#define TYPE_XICS_SPAPR "xics-spapr"
+#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_SPAPR)
 
-#define TYPE_KVM_XICS "xics-kvm"
-#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_KVM_XICS)
+#define TYPE_XICS_SPAPR_KVM "xics-spapr-kvm"
+#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_XICS_SPAPR_KVM)
 
 #define XICS_COMMON_CLASS(klass) \
      OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON)
-#define XICS_CLASS(klass) \
-     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS)
+#define XICS_SPAPR_CLASS(klass) \
+     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_SPAPR)
 #define XICS_COMMON_GET_CLASS(obj) \
      OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON)
-#define XICS_GET_CLASS(obj) \
-     OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS)
+#define XICS_SPAPR_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_SPAPR)
 
 #define XICS_IPI        0x2
 #define XICS_BUID       0x1
@@ -157,13 +157,14 @@ struct ICSIRQState {
     uint8_t flags;
 };
 
-#define XICS_IRQS               1024
+#define XICS_IRQS_SPAPR               1024
 
 qemu_irq xics_get_qirq(XICSState *icp, int irq);
 void xics_set_irq_type(XICSState *icp, int irq, bool lsi);
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
-void xics_free(XICSState *icp, int irq, int num);
+
+int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
+int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
+void xics_spapr_free(XICSState *icp, int irq, int num);
 
 void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
 
Signature · 1 line
2.5.0
#84 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 28/77] ppc/xics: Rename existing XICS classe to XICS_SPAPR

Quoted 4 lines · Benjamin Herrenschmidt

The common class doesn't change, the KVM one is SPAPR specific, this is a preliminary change to make it easier to support "native" XICS.

So changing the variable names is fine, but I believe actually changing the type name from "xics" to "xics-spapr" will break migration for existing sPAPR guests. There are some ways to wangle that, but I think it's going to be much simpler to leave the existing name as "xics" and add a new "xics-native".

You can still change the name of the TYPE_XICS macro to TYPE_XICS_SPAPR if you want.

Quoted 270 lines
---
 hw/intc/xics.c        | 26 +++++++++++++-------------
 hw/intc/xics_kvm.c    |  6 +++---
 hw/ppc/spapr.c        |  6 +++---
 hw/ppc/spapr_events.c |  2 +-
 hw/ppc/spapr_pci.c    |  8 ++++----
 hw/ppc/spapr_vio.c    |  2 +-
 include/hw/ppc/xics.h | 25 +++++++++++++------------
 7 files changed, 38 insertions(+), 37 deletions(-)
hw/intc/xics.c +13 -13
index 9ff5796..bcea1f0 100644
@@ -711,7 +711,7 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
     return -1;
 }
 
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
+int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
 {
     ICSState *ics = &icp->ics[src];
     int irq;
@@ -742,7 +742,7 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
  * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
  * If align==true, aligns the first IRQ number to num.
  */
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
+int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
 {
     int i, first = -1;
     ICSState *ics = &icp->ics[src];
@@ -787,7 +787,7 @@ static void ics_free(ICSState *ics, int srcno, int num)
     }
 }
 
-void xics_free(XICSState *icp, int irq, int num)
+void xics_spapr_free(XICSState *icp, int irq, int num)
 {
     int src = xics_find_source(icp, irq);
 
@@ -1006,7 +1006,7 @@ static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
     }
 }
 
-static void xics_realize(DeviceState *dev, Error **errp)
+static void xics_spapr_realize(DeviceState *dev, Error **errp)
 {
     XICSState *icp = XICS(dev);
     Error *error = NULL;
@@ -1045,7 +1045,7 @@ static void xics_realize(DeviceState *dev, Error **errp)
     }
 }
 
-static void xics_initfn(Object *obj)
+static void xics_spapr_initfn(Object *obj)
 {
     XICSState *xics = XICS(obj);
 
@@ -1054,29 +1054,29 @@ static void xics_initfn(Object *obj)
     xics->ics->icp = xics;
 }
 
-static void xics_class_init(ObjectClass *oc, void *data)
+static void xics_spapr_class_init(ObjectClass *oc, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(oc);
-    XICSStateClass *xsc = XICS_CLASS(oc);
+    XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
 
-    dc->realize = xics_realize;
+    dc->realize = xics_spapr_realize;
     xsc->set_nr_irqs = xics_set_nr_irqs;
     xsc->set_nr_servers = xics_set_nr_servers;
 }
 
-static const TypeInfo xics_info = {
-    .name          = TYPE_XICS,
+static const TypeInfo xics_spapr_info = {
+    .name          = TYPE_XICS_SPAPR,
     .parent        = TYPE_XICS_COMMON,
     .instance_size = sizeof(XICSState),
     .class_size = sizeof(XICSStateClass),
-    .class_init    = xics_class_init,
-    .instance_init = xics_initfn,
+    .class_init    = xics_spapr_class_init,
+    .instance_init = xics_spapr_initfn,
 };
 
 static void xics_register_types(void)
 {
     type_register_static(&xics_common_info);
-    type_register_static(&xics_info);
+    type_register_static(&xics_spapr_info);
     type_register_static(&ics_info);
     type_register_static(&icp_info);
 }
hw/intc/xics_kvm.c +3 -3
index d58729c..03ae801 100644
@@ -490,8 +490,8 @@ static void xics_kvm_class_init(ObjectClass *oc, void *data)
     xsc->set_nr_servers = xics_kvm_set_nr_servers;
 }
 
-static const TypeInfo xics_kvm_info = {
-    .name          = TYPE_KVM_XICS,
+static const TypeInfo xics_spapr_kvm_info = {
+    .name          = TYPE_XICS_SPAPR_KVM,
     .parent        = TYPE_XICS_COMMON,
     .instance_size = sizeof(KVMXICSState),
     .class_init    = xics_kvm_class_init,
@@ -500,7 +500,7 @@ static const TypeInfo xics_kvm_info = {
 
 static void xics_kvm_register_types(void)
 {
-    type_register_static(&xics_kvm_info);
+    type_register_static(&xics_spapr_kvm_info);
     type_register_static(&ics_kvm_info);
     type_register_static(&icp_kvm_info);
 }
hw/ppc/spapr.c +3 -3
index 610629e..bf94426 100644
@@ -119,7 +119,7 @@ static XICSState *xics_system_init(MachineState *machine,
         Error *err = NULL;
 
         if (machine_kernel_irqchip_allowed(machine)) {
-            icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
+            icp = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs, &err);
         }
         if (machine_kernel_irqchip_required(machine) && !icp) {
             error_report("kernel_irqchip requested but unavailable: %s",
@@ -128,7 +128,7 @@ static XICSState *xics_system_init(MachineState *machine,
     }
 
     if (!icp) {
-        icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort);
+        icp = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, &error_abort);
     }
 
     return icp;
@@ -1768,7 +1768,7 @@ static void ppc_spapr_init(MachineState *machine)
     spapr->icp = xics_system_init(machine,
                                   DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(),
                                                smp_threads),
-                                  XICS_IRQS);
+                                  XICS_IRQS_SPAPR);
 
     if (smc->dr_lmb_enabled) {
         spapr_validate_node_memory(machine);
hw/ppc/spapr_events.c +1 -1
index 744ea62..3b3663e 100644
@@ -587,7 +587,7 @@ out_no_events:
 void spapr_events_init(sPAPRMachineState *spapr)
 {
     QTAILQ_INIT(&spapr->pending_events);
-    spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
+    spapr->check_exception_irq = xics_spapr_alloc(spapr->icp, 0, 0, false);
     spapr->epow_notifier.notify = spapr_powerdown_req;
     qemu_register_powerdown_notifier(&spapr->epow_notifier);
     spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
hw/ppc/spapr_pci.c +4 -4
index 55fa8db..8b613a8 100644
@@ -313,7 +313,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
             return;
         }
 
-        xics_free(spapr->icp, msi->first_irq, msi->num);
+        xics_spapr_free(spapr->icp, msi->first_irq, msi->num);
         if (msi_present(pdev)) {
             spapr_msi_setmsg(pdev, 0, false, 0, num);
         }
@@ -351,7 +351,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     }
 
     /* Allocate MSIs */
-    irq = xics_alloc_block(spapr->icp, 0, req_num, false,
+    irq = xics_spapr_alloc_block(spapr->icp, 0, req_num, false,
                            ret_intr_type == RTAS_TYPE_MSI);
     if (!irq) {
         error_report("Cannot allocate MSIs for device %x", config_addr);
@@ -1360,7 +1360,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
     for (i = 0; i < PCI_NUM_PINS; i++) {
         uint32_t irq;
 
-        irq = xics_alloc_block(spapr->icp, 0, 1, true, false);
+        irq = xics_spapr_alloc_block(spapr->icp, 0, 1, true, false);
         if (!irq) {
             error_setg(errp, "spapr_allocate_lsi failed");
             return;
@@ -1717,7 +1717,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
     _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
     _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
     _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
-    _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS));
+    _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
 
     /* Build the interrupt-map, this must matches what is done
      * in pci_spapr_map_irq
hw/ppc/spapr_vio.c +1 -1
index c51eb8e..7b718cc 100644
@@ -462,7 +462,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
         dev->qdev.id = id;
     }
 
-    dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false);
+    dev->irq = xics_spapr_alloc(spapr->icp, 0, dev->irq, false);
     if (!dev->irq) {
         error_setg(errp, "can't allocate IRQ");
         return;
include/hw/ppc/xics.h +13 -12
index 355a966..8d33dfa 100644
@@ -32,20 +32,20 @@
 #define TYPE_XICS_COMMON "xics-common"
 #define XICS_COMMON(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_COMMON)
 
-#define TYPE_XICS "xics"
-#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS)
+#define TYPE_XICS_SPAPR "xics-spapr"
+#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_SPAPR)
 
-#define TYPE_KVM_XICS "xics-kvm"
-#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_KVM_XICS)
+#define TYPE_XICS_SPAPR_KVM "xics-spapr-kvm"
+#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_XICS_SPAPR_KVM)
 
 #define XICS_COMMON_CLASS(klass) \
      OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON)
-#define XICS_CLASS(klass) \
-     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS)
+#define XICS_SPAPR_CLASS(klass) \
+     OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_SPAPR)
 #define XICS_COMMON_GET_CLASS(obj) \
      OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON)
-#define XICS_GET_CLASS(obj) \
-     OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS)
+#define XICS_SPAPR_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_SPAPR)
 
 #define XICS_IPI        0x2
 #define XICS_BUID       0x1
@@ -157,13 +157,14 @@ struct ICSIRQState {
     uint8_t flags;
 };
 
-#define XICS_IRQS               1024
+#define XICS_IRQS_SPAPR               1024
 
 qemu_irq xics_get_qirq(XICSState *icp, int irq);
 void xics_set_irq_type(XICSState *icp, int irq, bool lsi);
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
-void xics_free(XICSState *icp, int irq, int num);
+
+int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
+int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
+void xics_spapr_free(XICSState *icp, int irq, int num);
 
 void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
 
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#85 lore raw

PATCH 29/77 ppc/xics: Move SPAPR specific code to a separate file

In reply to Benjamin Herrenschmidt

Leave the core ICP/ICS logic in xics.c and move the top level class wrapper, hypercall and RTAS handlers to xics_spapr.c

---
 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xics.c                    | 390 ++----------------------------------
 hw/intc/xics_spapr.c              | 401 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xics.h             |  23 +++
 5 files changed, 437 insertions(+), 379 deletions(-)
 create mode 100644 hw/intc/xics_spapr.c
default-configs/ppc64-softmmu.mak +1 -0
index 96574c8..516a6e2 100644
@@ -50,6 +50,7 @@ CONFIG_ETSEC=y
 CONFIG_LIBDECNUMBER=y
 # For pSeries
 CONFIG_XICS=$(CONFIG_PSERIES)
+CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
 # For PReP
 CONFIG_MC146818RTC=y
hw/intc/Makefile.objs +1 -0
index 004b0c2..e24cb03 100644
@@ -26,6 +26,7 @@ obj-$(CONFIG_OMAP) += omap_intc.o
 obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
 obj-$(CONFIG_SH4) += sh_intc.o
 obj-$(CONFIG_XICS) += xics.o
+obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
 obj-$(CONFIG_XICS_KVM) += xics_kvm.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xics.c +11 -379

467 lines not on this page · raw message

hw/intc/xics_spapr.c new file +401 -0

401 lines not on this page · raw message

include/hw/ppc/xics.h +23 -0
index 8d33dfa..8efff94 100644
@@ -141,6 +141,12 @@ struct ICSState {
     XICSState *icp;
 };
 
+static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
+{
+    return (nr >= ics->offset)
+        && (nr < (ics->offset + ics->nr_irqs));
+}
+
 struct ICSIRQState {
     uint32_t server;
     uint8_t priority;
@@ -168,4 +174,21 @@ void xics_spapr_free(XICSState *icp, int irq, int num);
 
 void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
 
+/* Internal XICS interfaces */
+int get_cpu_index_by_dt_id(int cpu_dt_id);
+
+void icp_set_cppr(XICSState *icp, int server, uint8_t cppr);
+void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr);
+uint32_t icp_accept(ICPState *ss);
+void icp_eoi(XICSState *icp, int server, uint32_t xirr);
+
+void ics_write_xive(ICSState *ics, int nr, int server,
+                    uint8_t priority, uint8_t saved_priority);
+
+void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
+
+void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp);
+void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp);
+int xics_find_source(XICSState *icp, int irq);
+
 #endif /* __XICS_H__ */
Signature · 1 line
2.5.0
#86 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 29/77] ppc/xics: Move SPAPR specific code to a separate file

Quoted 4 lines · Benjamin Herrenschmidt

Leave the core ICP/ICS logic in xics.c and move the top level class wrapper, hypercall and RTAS handlers to xics_spapr.c

Concept looks fine.

Quoted 961 lines
---
 default-configs/ppc64-softmmu.mak |   1 +
 hw/intc/Makefile.objs             |   1 +
 hw/intc/xics.c                    | 390 ++----------------------------------
 hw/intc/xics_spapr.c              | 401 ++++++++++++++++++++++++++++++++++++++
 include/hw/ppc/xics.h             |  23 +++
 5 files changed, 437 insertions(+), 379 deletions(-)
 create mode 100644 hw/intc/xics_spapr.c
default-configs/ppc64-softmmu.mak +1 -0
index 96574c8..516a6e2 100644
@@ -50,6 +50,7 @@ CONFIG_ETSEC=y
 CONFIG_LIBDECNUMBER=y
 # For pSeries
 CONFIG_XICS=$(CONFIG_PSERIES)
+CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
 CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
 # For PReP
 CONFIG_MC146818RTC=y
hw/intc/Makefile.objs +1 -0
index 004b0c2..e24cb03 100644
@@ -26,6 +26,7 @@ obj-$(CONFIG_OMAP) += omap_intc.o
 obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
 obj-$(CONFIG_SH4) += sh_intc.o
 obj-$(CONFIG_XICS) += xics.o
+obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
 obj-$(CONFIG_XICS_KVM) += xics_kvm.o
 obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
 obj-$(CONFIG_S390_FLIC) += s390_flic.o
hw/intc/xics.c +11 -379
index bcea1f0..38cacd8 100644
@@ -33,7 +33,7 @@
 #include "qemu/error-report.h"
 #include "qapi/visitor.h"
 
-static int get_cpu_index_by_dt_id(int cpu_dt_id)
+int get_cpu_index_by_dt_id(int cpu_dt_id)
 {
     PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id);
 
@@ -224,7 +224,7 @@ static void icp_resend(XICSState *icp, int server)
     ics_resend(icp->ics);
 }
 
-static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
+void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
 {
     ICPState *ss = icp->ss + server;
     uint8_t old_cppr;
@@ -248,7 +248,7 @@ static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
     }
 }
 
-static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr)
+void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr)
 {
     ICPState *ss = icp->ss + server;
 
@@ -258,7 +258,7 @@ static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr)
     }
 }
 
-static uint32_t icp_accept(ICPState *ss)
+uint32_t icp_accept(ICPState *ss)
 {
     uint32_t xirr = ss->xirr;
 
@@ -271,7 +271,7 @@ static uint32_t icp_accept(ICPState *ss)
     return xirr;
 }
 
-static void icp_eoi(XICSState *icp, int server, uint32_t xirr)
+void icp_eoi(XICSState *icp, int server, uint32_t xirr)
 {
     ICPState *ss = icp->ss + server;
 
@@ -372,12 +372,6 @@ static const TypeInfo icp_info = {
 /*
  * ICS: Source layer
  */
-static int ics_valid_irq(ICSState *ics, uint32_t nr)
-{
-    return (nr >= ics->offset)
-        && (nr < (ics->offset + ics->nr_irqs));
-}
-
 static void resend_msi(ICSState *ics, int srcno)
 {
     ICSIRQState *irq = ics->irqs + srcno;
@@ -462,8 +456,8 @@ static void write_xive_lsi(ICSState *ics, int srcno)
     resend_lsi(ics, srcno);
 }
 
-static void ics_write_xive(ICSState *ics, int nr, int server,
-                           uint8_t priority, uint8_t saved_priority)
+void ics_write_xive(ICSState *ics, int nr, int server,
+		    uint8_t priority, uint8_t saved_priority)
 {
     int srcno = nr - ics->offset;
     ICSIRQState *irq = ics->irqs + srcno;
@@ -640,7 +634,7 @@ static const TypeInfo ics_info = {
 /*
  * Exported functions
  */
-static int xics_find_source(XICSState *icp, int irq)
+int xics_find_source(XICSState *icp, int irq)
 {
     int sources = 1;
     int src;
@@ -668,7 +662,7 @@ qemu_irq xics_get_qirq(XICSState *icp, int irq)
     return NULL;
 }
 
-static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
+void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
 {
     assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK));
 
@@ -687,310 +681,16 @@ void xics_set_irq_type(XICSState *icp, int irq, bool lsi)
     ics_set_irq_type(ics, irq - ics->offset, lsi);
 }
 
-#define ICS_IRQ_FREE(ics, srcno)   \
-    (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
-
-static int ics_find_free_block(ICSState *ics, int num, int alignnum)
-{
-    int first, i;
-
-    for (first = 0; first < ics->nr_irqs; first += alignnum) {
-        if (num > (ics->nr_irqs - first)) {
-            return -1;
-        }
-        for (i = first; i < first + num; ++i) {
-            if (!ICS_IRQ_FREE(ics, i)) {
-                break;
-            }
-        }
-        if (i == (first + num)) {
-            return first;
-        }
-    }
-
-    return -1;
-}
-
-int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
-{
-    ICSState *ics = &icp->ics[src];
-    int irq;
-
-    if (irq_hint) {
-        assert(src == xics_find_source(icp, irq_hint));
-        if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
-            trace_xics_alloc_failed_hint(src, irq_hint);
-            return -1;
-        }
-        irq = irq_hint;
-    } else {
-        irq = ics_find_free_block(ics, 1, 1);
-        if (irq < 0) {
-            trace_xics_alloc_failed_no_left(src);
-            return -1;
-        }
-        irq += ics->offset;
-    }
-
-    ics_set_irq_type(ics, irq - ics->offset, lsi);
-    trace_xics_alloc(src, irq);
-
-    return irq;
-}
-
-/*
- * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
- * If align==true, aligns the first IRQ number to num.
- */
-int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
-{
-    int i, first = -1;
-    ICSState *ics = &icp->ics[src];
-
-    assert(src == 0);
-    /*
-     * MSIMesage::data is used for storing VIRQ so
-     * it has to be aligned to num to support multiple
-     * MSI vectors. MSI-X is not affected by this.
-     * The hint is used for the first IRQ, the rest should
-     * be allocated continuously.
-     */
-    if (align) {
-        assert((num == 1) || (num == 2) || (num == 4) ||
-               (num == 8) || (num == 16) || (num == 32));
-        first = ics_find_free_block(ics, num, num);
-    } else {
-        first = ics_find_free_block(ics, num, 1);
-    }
-
-    if (first >= 0) {
-        for (i = first; i < first + num; ++i) {
-            ics_set_irq_type(ics, i, lsi);
-        }
-    }
-    first += ics->offset;
-
-    trace_xics_alloc_block(src, first, num, lsi, align);
-
-    return first;
-}
-
-static void ics_free(ICSState *ics, int srcno, int num)
-{
-    int i;
-
-    for (i = srcno; i < srcno + num; ++i) {
-        if (ICS_IRQ_FREE(ics, i)) {
-            trace_xics_ics_free_warn(ics - ics->icp->ics, i + ics->offset);
-        }
-        memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
-    }
-}
-
-void xics_spapr_free(XICSState *icp, int irq, int num)
-{
-    int src = xics_find_source(icp, irq);
-
-    if (src >= 0) {
-        ICSState *ics = &icp->ics[src];
-
-        /* FIXME: implement multiple sources */
-        assert(src == 0);
-
-        trace_xics_ics_free(ics - icp->ics, irq, num);
-        ics_free(ics, irq - ics->offset, num);
-    }
-}
-
-/*
- * Guest interfaces
- */
-
-static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                           target_ulong opcode, target_ulong *args)
-{
-    CPUState *cs = CPU(cpu);
-    target_ulong cppr = args[0];
-
-    icp_set_cppr(spapr->icp, cs->cpu_index, cppr);
-    return H_SUCCESS;
-}
-
-static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                          target_ulong opcode, target_ulong *args)
-{
-    target_ulong server = get_cpu_index_by_dt_id(args[0]);
-    target_ulong mfrr = args[1];
-
-    if (server >= spapr->icp->nr_servers) {
-        return H_PARAMETER;
-    }
-
-    icp_set_mfrr(spapr->icp, server, mfrr);
-    return H_SUCCESS;
-}
-
-static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                           target_ulong opcode, target_ulong *args)
-{
-    CPUState *cs = CPU(cpu);
-    uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index);
-
-    args[0] = xirr;
-    return H_SUCCESS;
-}
-
-static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                             target_ulong opcode, target_ulong *args)
-{
-    CPUState *cs = CPU(cpu);
-    ICPState *ss = &spapr->icp->ss[cs->cpu_index];
-    uint32_t xirr = icp_accept(ss);
-
-    args[0] = xirr;
-    args[1] = cpu_get_host_ticks();
-    return H_SUCCESS;
-}
-
-static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                          target_ulong opcode, target_ulong *args)
-{
-    CPUState *cs = CPU(cpu);
-    target_ulong xirr = args[0];
-
-    icp_eoi(spapr->icp, cs->cpu_index, xirr);
-    return H_SUCCESS;
-}
-
-static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                            target_ulong opcode, target_ulong *args)
-{
-    CPUState *cs = CPU(cpu);
-    ICPState *ss = &spapr->icp->ss[cs->cpu_index];
-
-    args[0] = ss->xirr;
-    args[1] = ss->mfrr;
-
-    return H_SUCCESS;
-}
-
-static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                          uint32_t token,
-                          uint32_t nargs, target_ulong args,
-                          uint32_t nret, target_ulong rets)
-{
-    ICSState *ics = spapr->icp->ics;
-    uint32_t nr, server, priority;
-
-    if ((nargs != 3) || (nret != 1)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    nr = rtas_ld(args, 0);
-    server = get_cpu_index_by_dt_id(rtas_ld(args, 1));
-    priority = rtas_ld(args, 2);
-
-    if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
-        || (priority > 0xff)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    ics_write_xive(ics, nr, server, priority, priority);
-
-    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
-static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                          uint32_t token,
-                          uint32_t nargs, target_ulong args,
-                          uint32_t nret, target_ulong rets)
-{
-    ICSState *ics = spapr->icp->ics;
-    uint32_t nr;
-
-    if ((nargs != 1) || (nret != 3)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    nr = rtas_ld(args, 0);
-
-    if (!ics_valid_irq(ics, nr)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-    rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
-    rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
-}
-
-static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                         uint32_t token,
-                         uint32_t nargs, target_ulong args,
-                         uint32_t nret, target_ulong rets)
-{
-    ICSState *ics = spapr->icp->ics;
-    uint32_t nr;
-
-    if ((nargs != 1) || (nret != 1)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    nr = rtas_ld(args, 0);
-
-    if (!ics_valid_irq(ics, nr)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
-                   ics->irqs[nr - ics->offset].priority);
-
-    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
-static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
-                        uint32_t token,
-                        uint32_t nargs, target_ulong args,
-                        uint32_t nret, target_ulong rets)
-{
-    ICSState *ics = spapr->icp->ics;
-    uint32_t nr;
-
-    if ((nargs != 1) || (nret != 1)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    nr = rtas_ld(args, 0);
-
-    if (!ics_valid_irq(ics, nr)) {
-        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
-        return;
-    }
-
-    ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
-                   ics->irqs[nr - ics->offset].saved_priority,
-                   ics->irqs[nr - ics->offset].saved_priority);
-
-    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
 /*
  * XICS
  */
 
-static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
+void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
 {
     icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
 }
 
-static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
-                                Error **errp)
+void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp)
 {
     int i;
 
@@ -1006,77 +706,9 @@ static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
     }
 }
 
-static void xics_spapr_realize(DeviceState *dev, Error **errp)
-{
-    XICSState *icp = XICS(dev);
-    Error *error = NULL;
-    int i;
-
-    if (!icp->nr_servers) {
-        error_setg(errp, "Number of servers needs to be greater 0");
-        return;
-    }
-
-    /* Registration of global state belongs into realize */
-    spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
-    spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
-    spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
-    spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
-
-    spapr_register_hypercall(H_CPPR, h_cppr);
-    spapr_register_hypercall(H_IPI, h_ipi);
-    spapr_register_hypercall(H_XIRR, h_xirr);
-    spapr_register_hypercall(H_XIRR_X, h_xirr_x);
-    spapr_register_hypercall(H_EOI, h_eoi);
-    spapr_register_hypercall(H_IPOLL, h_ipoll);
-
-    object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
-    if (error) {
-        error_propagate(errp, error);
-        return;
-    }
-
-    for (i = 0; i < icp->nr_servers; i++) {
-        object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
-        if (error) {
-            error_propagate(errp, error);
-            return;
-        }
-    }
-}
-
-static void xics_spapr_initfn(Object *obj)
-{
-    XICSState *xics = XICS(obj);
-
-    xics->ics = ICS(object_new(TYPE_ICS));
-    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
-    xics->ics->icp = xics;
-}
-
-static void xics_spapr_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
-
-    dc->realize = xics_spapr_realize;
-    xsc->set_nr_irqs = xics_set_nr_irqs;
-    xsc->set_nr_servers = xics_set_nr_servers;
-}
-
-static const TypeInfo xics_spapr_info = {
-    .name          = TYPE_XICS_SPAPR,
-    .parent        = TYPE_XICS_COMMON,
-    .instance_size = sizeof(XICSState),
-    .class_size = sizeof(XICSStateClass),
-    .class_init    = xics_spapr_class_init,
-    .instance_init = xics_spapr_initfn,
-};
-
 static void xics_register_types(void)
 {
     type_register_static(&xics_common_info);
-    type_register_static(&xics_spapr_info);
     type_register_static(&ics_info);
     type_register_static(&icp_info);
 }
hw/intc/xics_spapr.c new file +401 -0
new file mode 100644
index 0000000..820fe79
@@ -0,0 +1,401 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
+ *
+ * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "hw/hw.h"
+#include "trace.h"
+#include "qemu/timer.h"
+#include "hw/ppc/spapr.h"
+#include "hw/ppc/xics.h"
+#include "qemu/error-report.h"
+#include "qapi/visitor.h"
+
+/*
+ * Guest interfaces
+ */
+
+static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                           target_ulong opcode, target_ulong *args)
+{
+    CPUState *cs = CPU(cpu);
+    target_ulong cppr = args[0];
+
+    icp_set_cppr(spapr->icp, cs->cpu_index, cppr);
+    return H_SUCCESS;
+}
+
+static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                          target_ulong opcode, target_ulong *args)
+{
+    target_ulong server = get_cpu_index_by_dt_id(args[0]);
+    target_ulong mfrr = args[1];
+
+    if (server >= spapr->icp->nr_servers) {
+        return H_PARAMETER;
+    }
+
+    icp_set_mfrr(spapr->icp, server, mfrr);
+    return H_SUCCESS;
+}
+
+static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                           target_ulong opcode, target_ulong *args)
+{
+    CPUState *cs = CPU(cpu);
+    uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index);
+
+    args[0] = xirr;
+    return H_SUCCESS;
+}
+
+static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                             target_ulong opcode, target_ulong *args)
+{
+    CPUState *cs = CPU(cpu);
+    ICPState *ss = &spapr->icp->ss[cs->cpu_index];
+    uint32_t xirr = icp_accept(ss);
+
+    args[0] = xirr;
+    args[1] = cpu_get_host_ticks();
+    return H_SUCCESS;
+}
+
+static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                          target_ulong opcode, target_ulong *args)
+{
+    CPUState *cs = CPU(cpu);
+    target_ulong xirr = args[0];
+
+    icp_eoi(spapr->icp, cs->cpu_index, xirr);
+    return H_SUCCESS;
+}
+
+static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                            target_ulong opcode, target_ulong *args)
+{
+    CPUState *cs = CPU(cpu);
+    ICPState *ss = &spapr->icp->ss[cs->cpu_index];
+
+    args[0] = ss->xirr;
+    args[1] = ss->mfrr;
+
+    return H_SUCCESS;
+}
+
+static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                          uint32_t token,
+                          uint32_t nargs, target_ulong args,
+                          uint32_t nret, target_ulong rets)
+{
+    ICSState *ics = spapr->icp->ics;
+    uint32_t nr, server, priority;
+
+    if ((nargs != 3) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+    server = get_cpu_index_by_dt_id(rtas_ld(args, 1));
+    priority = rtas_ld(args, 2);
+
+    if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
+        || (priority > 0xff)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ics_write_xive(ics, nr, server, priority, priority);
+
+    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                          uint32_t token,
+                          uint32_t nargs, target_ulong args,
+                          uint32_t nret, target_ulong rets)
+{
+    ICSState *ics = spapr->icp->ics;
+    uint32_t nr;
+
+    if ((nargs != 1) || (nret != 3)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+
+    if (!ics_valid_irq(ics, nr)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+    rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
+    rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
+}
+
+static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                         uint32_t token,
+                         uint32_t nargs, target_ulong args,
+                         uint32_t nret, target_ulong rets)
+{
+    ICSState *ics = spapr->icp->ics;
+    uint32_t nr;
+
+    if ((nargs != 1) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+
+    if (!ics_valid_irq(ics, nr)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
+                   ics->irqs[nr - ics->offset].priority);
+
+    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+                        uint32_t token,
+                        uint32_t nargs, target_ulong args,
+                        uint32_t nret, target_ulong rets)
+{
+    ICSState *ics = spapr->icp->ics;
+    uint32_t nr;
+
+    if ((nargs != 1) || (nret != 1)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    nr = rtas_ld(args, 0);
+
+    if (!ics_valid_irq(ics, nr)) {
+        rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+        return;
+    }
+
+    ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
+                   ics->irqs[nr - ics->offset].saved_priority,
+                   ics->irqs[nr - ics->offset].saved_priority);
+
+    rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void xics_spapr_realize(DeviceState *dev, Error **errp)
+{
+    XICSState *icp = XICS(dev);
+    Error *error = NULL;
+    int i;
+
+    if (!icp->nr_servers) {
+        error_setg(errp, "Number of servers needs to be greater 0");
+        return;
+    }
+
+    /* Registration of global state belongs into realize */
+    spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
+    spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
+    spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
+    spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
+
+    spapr_register_hypercall(H_CPPR, h_cppr);
+    spapr_register_hypercall(H_IPI, h_ipi);
+    spapr_register_hypercall(H_XIRR, h_xirr);
+    spapr_register_hypercall(H_XIRR_X, h_xirr_x);
+    spapr_register_hypercall(H_EOI, h_eoi);
+    spapr_register_hypercall(H_IPOLL, h_ipoll);
+
+    object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
+    if (error) {
+        error_propagate(errp, error);
+        return;
+    }
+
+    for (i = 0; i < icp->nr_servers; i++) {
+        object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
+        if (error) {
+            error_propagate(errp, error);
+            return;
+        }
+    }
+}
+
+static void xics_spapr_initfn(Object *obj)
+{
+    XICSState *xics = XICS(obj);
+
+    xics->ics = ICS(object_new(TYPE_ICS));
+    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
+    xics->ics->icp = xics;
+}
+
+static void xics_spapr_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
+
+    dc->realize = xics_spapr_realize;
+    xsc->set_nr_irqs = xics_set_nr_irqs;
+    xsc->set_nr_servers = xics_set_nr_servers;
+}
+
+static const TypeInfo xics_spapr_info = {
+    .name          = TYPE_XICS_SPAPR,
+    .parent        = TYPE_XICS_COMMON,
+    .instance_size = sizeof(XICSState),
+    .class_size = sizeof(XICSStateClass),
+    .class_init    = xics_spapr_class_init,
+    .instance_init = xics_spapr_initfn,
+};
+
+#define ICS_IRQ_FREE(ics, srcno)   \
+    (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
+
+static int ics_find_free_block(ICSState *ics, int num, int alignnum)
+{
+    int first, i;
+
+    for (first = 0; first < ics->nr_irqs; first += alignnum) {
+        if (num > (ics->nr_irqs - first)) {
+            return -1;
+        }
+        for (i = first; i < first + num; ++i) {
+            if (!ICS_IRQ_FREE(ics, i)) {
+                break;
+            }
+        }
+        if (i == (first + num)) {
+            return first;
+        }
+    }
+
+    return -1;
+}
+
+int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
+{
+    ICSState *ics = &icp->ics[src];
+    int irq;
+
+    if (irq_hint) {
+        assert(src == xics_find_source(icp, irq_hint));
+        if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
+            trace_xics_alloc_failed_hint(src, irq_hint);
+            return -1;
+        }
+        irq = irq_hint;
+    } else {
+        irq = ics_find_free_block(ics, 1, 1);
+        if (irq < 0) {
+            trace_xics_alloc_failed_no_left(src);
+            return -1;
+        }
+        irq += ics->offset;
+    }
+
+    ics_set_irq_type(ics, irq - ics->offset, lsi);
+    trace_xics_alloc(src, irq);
+
+    return irq;
+}
+
+/*
+ * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
+ * If align==true, aligns the first IRQ number to num.
+ */
+int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
+{
+    int i, first = -1;
+    ICSState *ics = &icp->ics[src];
+
+    assert(src == 0);
+    /*
+     * MSIMesage::data is used for storing VIRQ so
+     * it has to be aligned to num to support multiple
+     * MSI vectors. MSI-X is not affected by this.
+     * The hint is used for the first IRQ, the rest should
+     * be allocated continuously.
+     */
+    if (align) {
+        assert((num == 1) || (num == 2) || (num == 4) ||
+               (num == 8) || (num == 16) || (num == 32));
+        first = ics_find_free_block(ics, num, num);
+    } else {
+        first = ics_find_free_block(ics, num, 1);
+    }
+
+    if (first >= 0) {
+        for (i = first; i < first + num; ++i) {
+            ics_set_irq_type(ics, i, lsi);
+        }
+    }
+    first += ics->offset;
+
+    trace_xics_alloc_block(src, first, num, lsi, align);
+
+    return first;
+}
+
+static void ics_free(ICSState *ics, int srcno, int num)
+{
+    int i;
+
+    for (i = srcno; i < srcno + num; ++i) {
+        if (ICS_IRQ_FREE(ics, i)) {
+            trace_xics_ics_free_warn(ics - ics->icp->ics, i + ics->offset);
+        }
+        memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
+    }
+}
+
+void xics_spapr_free(XICSState *icp, int irq, int num)
+{
+    int src = xics_find_source(icp, irq);
+
+    if (src >= 0) {
+        ICSState *ics = &icp->ics[src];
+
+        /* FIXME: implement multiple sources */
+        assert(src == 0);
+
+        trace_xics_ics_free(ics - icp->ics, irq, num);
+        ics_free(ics, irq - ics->offset, num);
+    }
+}
+
+static void xics_spapr_register_types(void)
+{
+    type_register_static(&xics_spapr_info);
+}
+
+type_init(xics_spapr_register_types)
include/hw/ppc/xics.h +23 -0
index 8d33dfa..8efff94 100644
@@ -141,6 +141,12 @@ struct ICSState {
     XICSState *icp;
 };
 
+static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
+{
+    return (nr >= ics->offset)
+        && (nr < (ics->offset + ics->nr_irqs));
+}
+
 struct ICSIRQState {
     uint32_t server;
     uint8_t priority;
@@ -168,4 +174,21 @@ void xics_spapr_free(XICSState *icp, int irq, int num);
 
 void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
 
+/* Internal XICS interfaces */
+int get_cpu_index_by_dt_id(int cpu_dt_id);
+
+void icp_set_cppr(XICSState *icp, int server, uint8_t cppr);
+void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr);
+uint32_t icp_accept(ICPState *ss);
+void icp_eoi(XICSState *icp, int server, uint32_t xirr);
+
+void ics_write_xive(ICSState *ics, int nr, int server,
+                    uint8_t priority, uint8_t saved_priority);
+
+void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
+
+void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp);
+void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp);
+int xics_find_source(XICSState *icp, int irq);
+
 #endif /* __XICS_H__ */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#87 lore raw

PATCH 30/77 ppc/xics: Implement H_IPOLL using an accessor

In reply to Benjamin Herrenschmidt

None of the other presenter functions directly mucks with the internal state, so don't do it there either.

---
 hw/intc/xics.c        | 8 ++++++++
 hw/intc/xics_spapr.c  | 7 ++++---
 include/hw/ppc/xics.h | 1 +
 3 files changed, 13 insertions(+), 3 deletions(-)
hw/intc/xics.c +8 -0
index 38cacd8..165ff0b 100644
@@ -271,6 +271,14 @@ uint32_t icp_accept(ICPState *ss)
     return xirr;
 }
 
+uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
+{
+    if (mfrr) {
+        *mfrr = ss->mfrr;
+    }
+    return ss->xirr;
+}
+
 void icp_eoi(XICSState *icp, int server, uint32_t xirr)
 {
     ICPState *ss = icp->ss + server;
hw/intc/xics_spapr.c +4 -3
index 820fe79..aae1665 100644
@@ -97,10 +97,11 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
                             target_ulong opcode, target_ulong *args)
 {
     CPUState *cs = CPU(cpu);
-    ICPState *ss = &spapr->icp->ss[cs->cpu_index];
+    uint32_t mfrr;
+    uint32_t xirr = icp_ipoll(spapr->icp->ss + cs->cpu_index, &mfrr);
 
-    args[0] = ss->xirr;
-    args[1] = ss->mfrr;
+    args[0] = xirr;
+    args[1] = mfrr;
 
     return H_SUCCESS;
 }
include/hw/ppc/xics.h +1 -0
index 8efff94..ad39c8c 100644
@@ -180,6 +180,7 @@ int get_cpu_index_by_dt_id(int cpu_dt_id);
 void icp_set_cppr(XICSState *icp, int server, uint8_t cppr);
 void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr);
 uint32_t icp_accept(ICPState *ss);
+uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr);
 void icp_eoi(XICSState *icp, int server, uint32_t xirr);
 
 void ics_write_xive(ICSState *ics, int nr, int server,
Signature · 1 line
2.5.0
#88 lore raw

PATCH 31/77 ppc/xics: Remove unused xics_set_irq_type()

In reply to Benjamin Herrenschmidt
---
 hw/intc/xics.c        | 11 -----------
 include/hw/ppc/xics.h |  1 -
 2 files changed, 12 deletions(-)
hw/intc/xics.c +0 -11
index 165ff0b..197df33 100644
@@ -678,17 +678,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
         lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
 }
 
-void xics_set_irq_type(XICSState *icp, int irq, bool lsi)
-{
-    int src = xics_find_source(icp, irq);
-    ICSState *ics;
-
-    assert(src >= 0);
-
-    ics = &icp->ics[src];
-    ics_set_irq_type(ics, irq - ics->offset, lsi);
-}
-
 /*
  * XICS
  */
include/hw/ppc/xics.h +0 -1
index ad39c8c..8e7998f 100644
@@ -166,7 +166,6 @@ struct ICSIRQState {
 #define XICS_IRQS_SPAPR               1024
 
 qemu_irq xics_get_qirq(XICSState *icp, int irq);
-void xics_set_irq_type(XICSState *icp, int irq, bool lsi);
 
 int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
 int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
Signature · 1 line
2.5.0
Reviewed-by on v1 31/77
#89 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 31/77] ppc/xics: Remove unused xics_set_irq_type()

Quoted 1 line · Benjamin Herrenschmidt

In fact, this is a sufficiently good clean up that I think I'll merge it shortly, regardless of what happens with the rest of the pnv series.

Quoted 39 lines
---
 hw/intc/xics.c        | 11 -----------
 include/hw/ppc/xics.h |  1 -
 2 files changed, 12 deletions(-)
hw/intc/xics.c +0 -11
index 165ff0b..197df33 100644
@@ -678,17 +678,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
         lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
 }
 
-void xics_set_irq_type(XICSState *icp, int irq, bool lsi)
-{
-    int src = xics_find_source(icp, irq);
-    ICSState *ics;
-
-    assert(src >= 0);
-
-    ics = &icp->ics[src];
-    ics_set_irq_type(ics, irq - ics->offset, lsi);
-}
-
 /*
  * XICS
  */
include/hw/ppc/xics.h +0 -1
index ad39c8c..8e7998f 100644
@@ -166,7 +166,6 @@ struct ICSIRQState {
 #define XICS_IRQS_SPAPR               1024
 
 qemu_irq xics_get_qirq(XICSState *icp, int irq);
-void xics_set_irq_type(XICSState *icp, int irq, bool lsi);
 
 int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
 int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#90 lore raw

PATCH 32/77 ppc/xics: Replace "icp" with "xics" in most places

In reply to Benjamin Herrenschmidt

The "ICP" is a different object than the "XICS". For historical reasons, we have a number of places where we name a variable "icp" while it contains a XICSState pointer. There *is* an ICPState structure too so this makes the code really confusing.

This is a mechanical replacement of all those instances to use the name "xics" instead. There should be no functional change.

---
 hw/intc/xics.c              | 132 ++++++++++++++++++++++----------------------
 hw/intc/xics_kvm.c          |  54 +++++++++---------
 hw/intc/xics_spapr.c        |  56 +++++++++----------
 hw/ppc/spapr.c              |  22 ++++----
 hw/ppc/spapr_events.c       |   8 +--
 hw/ppc/spapr_pci.c          |   8 +--
 hw/ppc/spapr_vio.c          |   2 +-
 include/hw/pci-host/spapr.h |   2 +-
 include/hw/ppc/spapr.h      |   2 +-
 include/hw/ppc/spapr_vio.h  |   2 +-
 include/hw/ppc/xics.h       |   2 +-
 11 files changed, 145 insertions(+), 145 deletions(-)
hw/intc/xics.c +66 -66
index 197df33..d21471f 100644
@@ -44,17 +44,17 @@ int get_cpu_index_by_dt_id(int cpu_dt_id)
     return -1;
 }
 
-void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
+void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    ICPState *ss = &icp->ss[cs->cpu_index];
-    XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+    ICPState *ss = &xics->ss[cs->cpu_index];
+    XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
 
-    assert(cs->cpu_index < icp->nr_servers);
+    assert(cs->cpu_index < xics->nr_servers);
 
     if (info->cpu_setup) {
-        info->cpu_setup(icp, cpu);
+        info->cpu_setup(xics, cpu);
     }
 
     switch (PPC_INPUT(env)) {
@@ -78,21 +78,21 @@ void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
  */
 static void xics_common_reset(DeviceState *d)
 {
-    XICSState *icp = XICS_COMMON(d);
+    XICSState *xics = XICS_COMMON(d);
     int i;
 
-    for (i = 0; i < icp->nr_servers; i++) {
-        device_reset(DEVICE(&icp->ss[i]));
+    for (i = 0; i < xics->nr_servers; i++) {
+        device_reset(DEVICE(&xics->ss[i]));
     }
 
-    device_reset(DEVICE(icp->ics));
+    device_reset(DEVICE(xics->ics));
 }
 
 static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
                                   void *opaque, const char *name, Error **errp)
 {
-    XICSState *icp = XICS_COMMON(obj);
-    int64_t value = icp->nr_irqs;
+    XICSState *xics = XICS_COMMON(obj);
+    int64_t value = xics->nr_irqs;
 
     visit_type_int(v, &value, name, errp);
 }
@@ -100,8 +100,8 @@ static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
 static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
                                   void *opaque, const char *name, Error **errp)
 {
-    XICSState *icp = XICS_COMMON(obj);
-    XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+    XICSState *xics = XICS_COMMON(obj);
+    XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
     Error *error = NULL;
     int64_t value;
 
@@ -110,23 +110,23 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
         error_propagate(errp, error);
         return;
     }
-    if (icp->nr_irqs) {
+    if (xics->nr_irqs) {
         error_setg(errp, "Number of interrupts is already set to %u",
-                   icp->nr_irqs);
+                   xics->nr_irqs);
         return;
     }
 
     assert(info->set_nr_irqs);
-    assert(icp->ics);
-    info->set_nr_irqs(icp, value, errp);
+    assert(xics->ics);
+    info->set_nr_irqs(xics, value, errp);
 }
 
 static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
                                      void *opaque, const char *name,
                                      Error **errp)
 {
-    XICSState *icp = XICS_COMMON(obj);
-    int64_t value = icp->nr_servers;
+    XICSState *xics = XICS_COMMON(obj);
+    int64_t value = xics->nr_servers;
 
     visit_type_int(v, &value, name, errp);
 }
@@ -135,8 +135,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
                                      void *opaque, const char *name,
                                      Error **errp)
 {
-    XICSState *icp = XICS_COMMON(obj);
-    XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+    XICSState *xics = XICS_COMMON(obj);
+    XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
     Error *error = NULL;
     int64_t value;
 
@@ -145,14 +145,14 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
         error_propagate(errp, error);
         return;
     }
-    if (icp->nr_servers) {
+    if (xics->nr_servers) {
         error_setg(errp, "Number of servers is already set to %u",
-                   icp->nr_servers);
+                   xics->nr_servers);
         return;
     }
 
     assert(info->set_nr_servers);
-    info->set_nr_servers(icp, value, errp);
+    info->set_nr_servers(xics, value, errp);
 }
 
 static void xics_common_initfn(Object *obj)
@@ -195,9 +195,9 @@ static void ics_reject(ICSState *ics, int nr);
 static void ics_resend(ICSState *ics);
 static void ics_eoi(ICSState *ics, int nr);
 
-static void icp_check_ipi(XICSState *icp, int server)
+static void icp_check_ipi(XICSState *xics, int server)
 {
-    ICPState *ss = icp->ss + server;
+    ICPState *ss = xics->ss + server;
 
     if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
         return;
@@ -206,7 +206,7 @@ static void icp_check_ipi(XICSState *icp, int server)
     trace_xics_icp_check_ipi(server, ss->mfrr);
 
     if (XISR(ss)) {
-        ics_reject(icp->ics, XISR(ss));
+        ics_reject(xics->ics, XISR(ss));
     }
 
     ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
@@ -214,19 +214,19 @@ static void icp_check_ipi(XICSState *icp, int server)
     qemu_irq_raise(ss->output);
 }
 
-static void icp_resend(XICSState *icp, int server)
+static void icp_resend(XICSState *xics, int server)
 {
-    ICPState *ss = icp->ss + server;
+    ICPState *ss = xics->ss + server;
 
     if (ss->mfrr < CPPR(ss)) {
-        icp_check_ipi(icp, server);
+        icp_check_ipi(xics, server);
     }
-    ics_resend(icp->ics);
+    ics_resend(xics->ics);
 }
 
-void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
+void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
 {
-    ICPState *ss = icp->ss + server;
+    ICPState *ss = xics->ss + server;
     uint8_t old_cppr;
     uint32_t old_xisr;
 
@@ -239,22 +239,22 @@ void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
             ss->xirr &= ~XISR_MASK; /* Clear XISR */
             ss->pending_priority = 0xff;
             qemu_irq_lower(ss->output);
-            ics_reject(icp->ics, old_xisr);
+            ics_reject(xics->ics, old_xisr);
         }
     } else {
         if (!XISR(ss)) {
-            icp_resend(icp, server);
+            icp_resend(xics, server);
         }
     }
 }
 
-void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr)
+void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
 {
-    ICPState *ss = icp->ss + server;
+    ICPState *ss = xics->ss + server;
 
     ss->mfrr = mfrr;
     if (mfrr < CPPR(ss)) {
-        icp_check_ipi(icp, server);
+        icp_check_ipi(xics, server);
     }
 }
 
@@ -279,31 +279,31 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
     return ss->xirr;
 }
 
-void icp_eoi(XICSState *icp, int server, uint32_t xirr)
+void icp_eoi(XICSState *xics, int server, uint32_t xirr)
 {
-    ICPState *ss = icp->ss + server;
+    ICPState *ss = xics->ss + server;
 
     /* Send EOI -> ICS */
     ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
     trace_xics_icp_eoi(server, xirr, ss->xirr);
-    ics_eoi(icp->ics, xirr & XISR_MASK);
+    ics_eoi(xics->ics, xirr & XISR_MASK);
     if (!XISR(ss)) {
-        icp_resend(icp, server);
+        icp_resend(xics, server);
     }
 }
 
-static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority)
+static void icp_irq(XICSState *xics, int server, int nr, uint8_t priority)
 {
-    ICPState *ss = icp->ss + server;
+    ICPState *ss = xics->ss + server;
 
     trace_xics_icp_irq(server, nr, priority);
 
     if ((priority >= CPPR(ss))
         || (XISR(ss) && (ss->pending_priority <= priority))) {
-        ics_reject(icp->ics, nr);
+        ics_reject(xics->ics, nr);
     } else {
         if (XISR(ss)) {
-            ics_reject(icp->ics, XISR(ss));
+            ics_reject(xics->ics, XISR(ss));
         }
         ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
         ss->pending_priority = priority;
@@ -388,7 +388,7 @@ static void resend_msi(ICSState *ics, int srcno)
     if (irq->status & XICS_STATUS_REJECTED) {
         irq->status &= ~XICS_STATUS_REJECTED;
         if (irq->priority != 0xff) {
-            icp_irq(ics->icp, irq->server, srcno + ics->offset,
+            icp_irq(ics->xics, irq->server, srcno + ics->offset,
                     irq->priority);
         }
     }
@@ -402,7 +402,7 @@ static void resend_lsi(ICSState *ics, int srcno)
         && (irq->status & XICS_STATUS_ASSERTED)
         && !(irq->status & XICS_STATUS_SENT)) {
         irq->status |= XICS_STATUS_SENT;
-        icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+        icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
     }
 }
 
@@ -417,7 +417,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
             irq->status |= XICS_STATUS_MASKED_PENDING;
             trace_xics_masked_pending();
         } else  {
-            icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+            icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
         }
     }
 }
@@ -456,7 +456,7 @@ static void write_xive_msi(ICSState *ics, int srcno)
     }
 
     irq->status &= ~XICS_STATUS_MASKED_PENDING;
-    icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+    icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
 }
 
 static void write_xive_lsi(ICSState *ics, int srcno)
@@ -465,7 +465,7 @@ static void write_xive_lsi(ICSState *ics, int srcno)
 }
 
 void ics_write_xive(ICSState *ics, int nr, int server,
-		    uint8_t priority, uint8_t saved_priority)
+                    uint8_t priority, uint8_t saved_priority)
 {
     int srcno = nr - ics->offset;
     ICSIRQState *irq = ics->irqs + srcno;
@@ -541,8 +541,8 @@ static int ics_post_load(ICSState *ics, int version_id)
 {
     int i;
 
-    for (i = 0; i < ics->icp->nr_servers; i++) {
-        icp_resend(ics->icp, i);
+    for (i = 0; i < ics->xics->nr_servers; i++) {
+        icp_resend(ics->xics, i);
     }
 
     return 0;
@@ -642,14 +642,14 @@ static const TypeInfo ics_info = {
 /*
  * Exported functions
  */
-int xics_find_source(XICSState *icp, int irq)
+int xics_find_source(XICSState *xics, int irq)
 {
     int sources = 1;
     int src;
 
     /* FIXME: implement multiple sources */
     for (src = 0; src < sources; ++src) {
-        ICSState *ics = &icp->ics[src];
+        ICSState *ics = &xics->ics[src];
         if (ics_valid_irq(ics, irq)) {
             return src;
         }
@@ -658,12 +658,12 @@ int xics_find_source(XICSState *icp, int irq)
     return -1;
 }
 
-qemu_irq xics_get_qirq(XICSState *icp, int irq)
+qemu_irq xics_get_qirq(XICSState *xics, int irq)
 {
-    int src = xics_find_source(icp, irq);
+    int src = xics_find_source(xics, irq);
 
     if (src >= 0) {
-        ICSState *ics = &icp->ics[src];
+        ICSState *ics = &xics->ics[src];
         return ics->qirqs[irq - ics->offset];
     }
 
@@ -682,23 +682,23 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
  * XICS
  */
 
-void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
+void xics_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp)
 {
-    icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
+    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
 }
 
-void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp)
+void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp)
 {
     int i;
 
-    icp->nr_servers = nr_servers;
+    xics->nr_servers = nr_servers;
 
-    icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
-    for (i = 0; i < icp->nr_servers; i++) {
+    xics->ss = g_malloc0(xics->nr_servers*sizeof(ICPState));
+    for (i = 0; i < xics->nr_servers; i++) {
         char buffer[32];
-        object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
+        object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP);
         snprintf(buffer, sizeof(buffer), "icp[%d]", i);
-        object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
+        object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
                                   errp);
     }
 }
hw/intc/xics_kvm.c +27 -27

141 lines not on this page · raw message

hw/intc/xics_spapr.c +28 -28

187 lines not on this page · raw message

hw/ppc/spapr.c +11 -11
index bf94426..d8a84ca 100644
@@ -113,25 +113,25 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
 static XICSState *xics_system_init(MachineState *machine,
                                    int nr_servers, int nr_irqs)
 {
-    XICSState *icp = NULL;
+    XICSState *xics = NULL;
 
     if (kvm_enabled()) {
         Error *err = NULL;
 
         if (machine_kernel_irqchip_allowed(machine)) {
-            icp = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs, &err);
+            xics = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs, &err);
         }
-        if (machine_kernel_irqchip_required(machine) && !icp) {
+        if (machine_kernel_irqchip_required(machine) && !xics) {
             error_report("kernel_irqchip requested but unavailable: %s",
                          error_get_pretty(err));
         }
     }
 
-    if (!icp) {
-        icp = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, &error_abort);
+    if (!xics) {
+        xics = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, &error_abort);
     }
 
-    return icp;
+    return xics;
 }
 
 static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
@@ -1615,7 +1615,7 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
         }
     }
 
-    xics_cpu_setup(spapr->icp, cpu);
+    xics_cpu_setup(spapr->xics, cpu);
 
     qemu_register_reset(spapr_cpu_reset, cpu);
 }
@@ -1765,10 +1765,10 @@ static void ppc_spapr_init(MachineState *machine)
     spapr_alloc_htab(spapr);
 
     /* Set up Interrupt Controller before we create the VCPUs */
-    spapr->icp = xics_system_init(machine,
-                                  DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(),
-                                               smp_threads),
-                                  XICS_IRQS_SPAPR);
+    spapr->xics = xics_system_init(machine,
+                                   DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(),
+                                                smp_threads),
+                                   XICS_IRQS_SPAPR);
 
     if (smc->dr_lmb_enabled) {
         spapr_validate_node_memory(machine);
hw/ppc/spapr_events.c +4 -4
index 3b3663e..c06deea 100644
@@ -383,7 +383,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
 
     rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
 
-    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+    qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
 }
 
 static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
@@ -452,7 +452,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
 
     rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
 
-    qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+    qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
 }
 
 void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
@@ -535,7 +535,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
      * interrupts.
      */
     if (rtas_event_log_contains(mask, true)) {
-        qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+        qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
     }
 
     return;
@@ -587,7 +587,7 @@ out_no_events:
 void spapr_events_init(sPAPRMachineState *spapr)
 {
     QTAILQ_INIT(&spapr->pending_events);
-    spapr->check_exception_irq = xics_spapr_alloc(spapr->icp, 0, 0, false);
+    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false);
     spapr->epow_notifier.notify = spapr_powerdown_req;
     qemu_register_powerdown_notifier(&spapr->epow_notifier);
     spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
hw/ppc/spapr_pci.c +4 -4
index 8b613a8..cf3192e 100644
@@ -313,7 +313,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
             return;
         }
 
-        xics_spapr_free(spapr->icp, msi->first_irq, msi->num);
+        xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
         if (msi_present(pdev)) {
             spapr_msi_setmsg(pdev, 0, false, 0, num);
         }
@@ -351,7 +351,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     }
 
     /* Allocate MSIs */
-    irq = xics_spapr_alloc_block(spapr->icp, 0, req_num, false,
+    irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
                            ret_intr_type == RTAS_TYPE_MSI);
     if (!irq) {
         error_report("Cannot allocate MSIs for device %x", config_addr);
@@ -729,7 +729,7 @@ static void spapr_msi_write(void *opaque, hwaddr addr,
 
     trace_spapr_pci_msi_write(addr, data, irq);
 
-    qemu_irq_pulse(xics_get_qirq(spapr->icp, irq));
+    qemu_irq_pulse(xics_get_qirq(spapr->xics, irq));
 }
 
 static const MemoryRegionOps spapr_msi_ops = {
@@ -1360,7 +1360,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
     for (i = 0; i < PCI_NUM_PINS; i++) {
         uint32_t irq;
 
-        irq = xics_spapr_alloc_block(spapr->icp, 0, 1, true, false);
+        irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false);
         if (!irq) {
             error_setg(errp, "spapr_allocate_lsi failed");
             return;
hw/ppc/spapr_vio.c +1 -1
index 7b718cc..fc731eb 100644
@@ -462,7 +462,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
         dev->qdev.id = id;
     }
 
-    dev->irq = xics_spapr_alloc(spapr->icp, 0, dev->irq, false);
+    dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false);
     if (!dev->irq) {
         error_setg(errp, "can't allocate IRQ");
         return;
include/hw/pci-host/spapr.h +1 -1
index 7de5e02..117c1f8 100644
@@ -120,7 +120,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
 
-    return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq);
+    return xics_get_qirq(spapr->xics, phb->lsi_table[pin].irq);
 }
 
 PCIHostState *spapr_create_phb(sPAPRMachineState *spapr, int index);
include/hw/ppc/spapr.h +1 -1
index 5baa906..c6a0136 100644
@@ -48,7 +48,7 @@ struct sPAPRMachineState {
     struct VIOsPAPRBus *vio_bus;
     QLIST_HEAD(, sPAPRPHBState) phbs;
     struct sPAPRNVRAM *nvram;
-    XICSState *icp;
+    XICSState *xics;
     DeviceState *rtc;
 
     void *htab;
include/hw/ppc/spapr_vio.h +1 -1
index 2299a54..e10fd52 100644
@@ -90,7 +90,7 @@ static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev)
 {
     sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
 
-    return xics_get_qirq(spapr->icp, dev->irq);
+    return xics_get_qirq(spapr->xics, dev->irq);
 }
 
 static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr,
include/hw/ppc/xics.h +1 -1
index 8e7998f..e670e89 100644
@@ -138,7 +138,7 @@ struct ICSState {
     uint32_t offset;
     qemu_irq *qirqs;
     ICSIRQState *irqs;
-    XICSState *icp;
+    XICSState *xics;
 };
 
 static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
Signature · 1 line
2.5.0
#91 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 32/77] ppc/xics: Replace "icp" with "xics" in most places

Quoted 9 lines · Benjamin Herrenschmidt

The "ICP" is a different object than the "XICS". For historical reasons, we have a number of places where we name a variable "icp" while it contains a XICSState pointer. There *is* an ICPState structure too so this makes the code really confusing.

This is a mechanical replacement of all those instances to use the name "xics" instead. There should be no functional change.

Ah, good idea. I think I'll take this one independently as well.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#92 lore raw

PATCH 33/77 ppc/xics: Make the ICSState a list

In reply to Benjamin Herrenschmidt

Instead of an array of fixed sized blocks, use a list, as we will need to have sources with variable number of interrupts. SPAPR only uses a single entry. Native will create more. If performance becomes an issue we can add some hashed lookup but for now this will do fine.

---
 hw/intc/xics.c        | 86 +++++++++++++++++++++++++++++++--------------------
 hw/intc/xics_kvm.c    | 28 +++++++++--------
 hw/intc/xics_spapr.c  | 75 ++++++++++++++++++++++++--------------------
 hw/ppc/spapr_events.c |  2 +-
 hw/ppc/spapr_pci.c    |  4 +--
 hw/ppc/spapr_vio.c    |  2 +-
 include/hw/ppc/xics.h | 10 +++---
 7 files changed, 118 insertions(+), 89 deletions(-)
hw/intc/xics.c +52 -34
index d21471f..c4ac057 100644
@@ -79,13 +79,16 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
 static void xics_common_reset(DeviceState *d)
 {
     XICSState *xics = XICS_COMMON(d);
+    ICSState *ics;
     int i;
 
     for (i = 0; i < xics->nr_servers; i++) {
         device_reset(DEVICE(&xics->ss[i]));
     }
 
-    device_reset(DEVICE(xics->ics));
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        device_reset(DEVICE(ics));
+    }
 }
 
 static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
@@ -117,7 +120,6 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
     }
 
     assert(info->set_nr_irqs);
-    assert(xics->ics);
     info->set_nr_irqs(xics, value, errp);
 }
 
@@ -195,33 +197,35 @@ static void ics_reject(ICSState *ics, int nr);
 static void ics_resend(ICSState *ics);
 static void ics_eoi(ICSState *ics, int nr);
 
-static void icp_check_ipi(XICSState *xics, int server)
+static void icp_check_ipi(ICPState *ss, int server)
 {
-    ICPState *ss = xics->ss + server;
-
     if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
         return;
     }
 
     trace_xics_icp_check_ipi(server, ss->mfrr);
 
-    if (XISR(ss)) {
-        ics_reject(xics->ics, XISR(ss));
+    if (XISR(ss) && ss->xirr_owner) {
+        ics_reject(ss->xirr_owner, XISR(ss));
     }
 
     ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
     ss->pending_priority = ss->mfrr;
+    ss->xirr_owner = NULL;
     qemu_irq_raise(ss->output);
 }
 
 static void icp_resend(XICSState *xics, int server)
 {
     ICPState *ss = xics->ss + server;
+    ICSState *ics;
 
     if (ss->mfrr < CPPR(ss)) {
-        icp_check_ipi(xics, server);
+        icp_check_ipi(ss, server);
+    }
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        ics_resend(ics);
     }
-    ics_resend(xics->ics);
 }
 
 void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
@@ -239,7 +243,10 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
             ss->xirr &= ~XISR_MASK; /* Clear XISR */
             ss->pending_priority = 0xff;
             qemu_irq_lower(ss->output);
-            ics_reject(xics->ics, old_xisr);
+            if (ss->xirr_owner) {
+                ics_reject(ss->xirr_owner, old_xisr);
+                ss->xirr_owner = NULL;
+            }
         }
     } else {
         if (!XISR(ss)) {
@@ -254,7 +261,7 @@ void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
 
     ss->mfrr = mfrr;
     if (mfrr < CPPR(ss)) {
-        icp_check_ipi(xics, server);
+        icp_check_ipi(ss, server);
     }
 }
 
@@ -265,6 +272,7 @@ uint32_t icp_accept(ICPState *ss)
     qemu_irq_lower(ss->output);
     ss->xirr = ss->pending_priority << 24;
     ss->pending_priority = 0xff;
+    ss->xirr_owner = NULL;
 
     trace_xics_icp_accept(xirr, ss->xirr);
 
@@ -282,30 +290,40 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
 void icp_eoi(XICSState *xics, int server, uint32_t xirr)
 {
     ICPState *ss = xics->ss + server;
+    ICSState *ics;
+    uint32_t irq;
 
     /* Send EOI -> ICS */
     ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
     trace_xics_icp_eoi(server, xirr, ss->xirr);
-    ics_eoi(xics->ics, xirr & XISR_MASK);
+    irq = xirr & XISR_MASK;
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        if (ics_valid_irq(ics, irq)) {
+            ics_eoi(ics, irq);
+        }
+    }
     if (!XISR(ss)) {
         icp_resend(xics, server);
     }
 }
 
-static void icp_irq(XICSState *xics, int server, int nr, uint8_t priority)
+static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority)
 {
+    XICSState *xics = ics->xics;
     ICPState *ss = xics->ss + server;
 
     trace_xics_icp_irq(server, nr, priority);
 
     if ((priority >= CPPR(ss))
         || (XISR(ss) && (ss->pending_priority <= priority))) {
-        ics_reject(xics->ics, nr);
+        ics_reject(ics, nr);
     } else {
-        if (XISR(ss)) {
-            ics_reject(xics->ics, XISR(ss));
+        if (XISR(ss) && ss->xirr_owner) {
+            ics_reject(ss->xirr_owner, XISR(ss));
+            ss->xirr_owner = NULL;
         }
         ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
+        ss->xirr_owner = ics;
         ss->pending_priority = priority;
         trace_xics_icp_raise(ss->xirr, ss->pending_priority);
         qemu_irq_raise(ss->output);
@@ -388,8 +406,7 @@ static void resend_msi(ICSState *ics, int srcno)
     if (irq->status & XICS_STATUS_REJECTED) {
         irq->status &= ~XICS_STATUS_REJECTED;
         if (irq->priority != 0xff) {
-            icp_irq(ics->xics, irq->server, srcno + ics->offset,
-                    irq->priority);
+            icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
         }
     }
 }
@@ -402,7 +419,7 @@ static void resend_lsi(ICSState *ics, int srcno)
         && (irq->status & XICS_STATUS_ASSERTED)
         && !(irq->status & XICS_STATUS_SENT)) {
         irq->status |= XICS_STATUS_SENT;
-        icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
+        icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
     }
 }
 
@@ -417,7 +434,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
             irq->status |= XICS_STATUS_MASKED_PENDING;
             trace_xics_masked_pending();
         } else  {
-            icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
+            icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
         }
     }
 }
@@ -456,7 +473,7 @@ static void write_xive_msi(ICSState *ics, int srcno)
     }
 
     irq->status &= ~XICS_STATUS_MASKED_PENDING;
-    icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
+    icp_irq(ics, irq->server, srcno + ics->offset, irq->priority);
 }
 
 static void write_xive_lsi(ICSState *ics, int srcno)
@@ -642,28 +659,23 @@ static const TypeInfo ics_info = {
 /*
  * Exported functions
  */
-int xics_find_source(XICSState *xics, int irq)
+ICSState *xics_find_source(XICSState *xics, int irq)
 {
-    int sources = 1;
-    int src;
+    ICSState *ics;
 
-    /* FIXME: implement multiple sources */
-    for (src = 0; src < sources; ++src) {
-        ICSState *ics = &xics->ics[src];
+    QLIST_FOREACH(ics, &xics->ics, list) {
         if (ics_valid_irq(ics, irq)) {
-            return src;
+            return ics;
         }
     }
-
-    return -1;
+    return NULL;
 }
 
 qemu_irq xics_get_qirq(XICSState *xics, int irq)
 {
-    int src = xics_find_source(xics, irq);
+    ICSState *ics = xics_find_source(xics, irq);
 
-    if (src >= 0) {
-        ICSState *ics = &xics->ics[src];
+    if (ics) {
         return ics->qirqs[irq - ics->offset];
     }
 
@@ -684,7 +696,13 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
 
 void xics_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp)
 {
-    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
+    ICSState *ics = QLIST_FIRST(&xics->ics);
+
+    /* This needs to be deprecated ... */
+    xics->nr_irqs = nr_irqs;
+    if (ics) {
+        ics->nr_irqs = nr_irqs;
+    }
 }
 
 void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp)
hw/intc/xics_kvm.c +15 -13
index 7d86157..a478d25 100644
@@ -356,11 +356,6 @@ static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
     }
 }
 
-static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp)
-{
-    xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
-}
-
 static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers,
                                     Error **errp)
 {
@@ -391,6 +386,7 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
 {
     KVMXICSState *xicskvm = KVM_XICS(dev);
     XICSState *xics = XICS_COMMON(dev);
+    ICSState *ics;
     int i, rc;
     Error *error = NULL;
     struct kvm_create_device xics_create_device = {
@@ -442,10 +438,12 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
 
     xicskvm->kernel_xics_fd = xics_create_device.fd;
 
-    object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
-    if (error) {
-        error_propagate(errp, error);
-        goto fail;
+    QLIST_FOREACH(ics, &xics->ics, list) {
+        object_property_set_bool(OBJECT(ics), true, "realized", &error);
+        if (error) {
+            error_propagate(errp, error);
+            goto fail;
+        }
     }
 
     assert(xics->nr_servers);
@@ -473,10 +471,14 @@ fail:
 static void xics_kvm_initfn(Object *obj)
 {
     XICSState *xics = XICS_COMMON(obj);
+    ICSState *ics;
+
+    QLIST_INIT(&xics->ics);
 
-    xics->ics = ICS(object_new(TYPE_KVM_ICS));
-    object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
-    xics->ics->xics = xics;
+    ics = ICS(object_new(TYPE_KVM_ICS));
+    object_property_add_child(obj, "ics", OBJECT(ics), NULL);
+    ics->xics = xics;
+    QLIST_INSERT_HEAD(&xics->ics, ics, list);
 }
 
 static void xics_kvm_class_init(ObjectClass *oc, void *data)
@@ -486,7 +488,7 @@ static void xics_kvm_class_init(ObjectClass *oc, void *data)
 
     dc->realize = xics_kvm_realize;
     xsc->cpu_setup = xics_kvm_cpu_setup;
-    xsc->set_nr_irqs = xics_kvm_set_nr_irqs;
+    xsc->set_nr_irqs = xics_set_nr_irqs;
     xsc->set_nr_servers = xics_kvm_set_nr_servers;
 }
 
hw/intc/xics_spapr.c +41 -34

177 lines not on this page · raw message

hw/ppc/spapr_events.c +1 -1
index c06deea..6335ead 100644
@@ -587,7 +587,7 @@ out_no_events:
 void spapr_events_init(sPAPRMachineState *spapr)
 {
     QTAILQ_INIT(&spapr->pending_events);
-    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false);
+    spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, false);
     spapr->epow_notifier.notify = spapr_powerdown_req;
     qemu_register_powerdown_notifier(&spapr->epow_notifier);
     spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
hw/ppc/spapr_pci.c +2 -2
index cf3192e..9b13f85 100644
@@ -351,7 +351,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
     }
 
     /* Allocate MSIs */
-    irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
+    irq = xics_spapr_alloc_block(spapr->xics, req_num, false,
                            ret_intr_type == RTAS_TYPE_MSI);
     if (!irq) {
         error_report("Cannot allocate MSIs for device %x", config_addr);
@@ -1360,7 +1360,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
     for (i = 0; i < PCI_NUM_PINS; i++) {
         uint32_t irq;
 
-        irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false);
+        irq = xics_spapr_alloc_block(spapr->xics, 1, true, false);
         if (!irq) {
             error_setg(errp, "spapr_allocate_lsi failed");
             return;
hw/ppc/spapr_vio.c +1 -1
index fc731eb..1a84815 100644
@@ -462,7 +462,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
         dev->qdev.id = id;
     }
 
-    dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false);
+    dev->irq = xics_spapr_alloc(spapr->xics, dev->irq, false);
     if (!dev->irq) {
         error_setg(errp, "can't allocate IRQ");
         return;
include/hw/ppc/xics.h +6 -4
index e670e89..12fc584 100644
@@ -79,7 +79,7 @@ struct XICSState {
     uint32_t nr_servers;
     uint32_t nr_irqs;
     ICPState *ss;
-    ICSState *ics;
+    QLIST_HEAD(, ICSState) ics;
 };
 
 #define TYPE_ICP "icp"
@@ -105,6 +105,7 @@ struct ICPState {
     DeviceState parent_obj;
     /*< public >*/
     CPUState *cs;
+    ICSState *xirr_owner;
     uint32_t xirr;
     uint8_t pending_priority;
     uint8_t mfrr;
@@ -139,6 +140,7 @@ struct ICSState {
     qemu_irq *qirqs;
     ICSIRQState *irqs;
     XICSState *xics;
+    QLIST_ENTRY(ICSState) list;
 };
 
 static inline bool ics_valid_irq(ICSState *ics, uint32_t nr)
@@ -167,8 +169,8 @@ struct ICSIRQState {
 
 qemu_irq xics_get_qirq(XICSState *icp, int irq);
 
-int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi);
-int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align);
+int xics_spapr_alloc(XICSState *icp, int irq_hint, bool lsi);
+int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align);
 void xics_spapr_free(XICSState *icp, int irq, int num);
 
 void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
@@ -189,6 +191,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi);
 
 void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp);
 void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp);
-int xics_find_source(XICSState *icp, int irq);
+ICSState *xics_find_source(XICSState *icp, int irq);
 
 #endif /* __XICS_H__ */
Signature · 1 line
2.5.0