From 671651eb074e8bb98eb63f199375f179b3662d8a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:47 +0200 Subject: [PATCH 01/81] 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. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 5282533b38587..4c0dc02e86d57 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1303,9 +1303,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) { From 60843ec7131ec802f3ee8f4e6e3b73c5d6335d91 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:47 +0200 Subject: [PATCH 02/81] ppc: Use split I/D mmu modes to avoid flushes on interrupts 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. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 11 +++++--- target-ppc/excp_helper.c | 11 -------- target-ppc/helper_regs.h | 54 ++++++++++++++++++++++++++++++++++------ target-ppc/machine.c | 5 +++- target-ppc/translate.c | 7 +++--- 5 files changed, 63 insertions(+), 25 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 4c0dc02e86d57..9588b30ee855e 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -470,6 +470,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 */ @@ -521,6 +523,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) @@ -1000,7 +1004,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 @@ -1164,7 +1168,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); @@ -1306,7 +1311,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" diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index ca4ffe8ad660b..cf882ebdad4c0 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -645,9 +645,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 @@ -720,14 +717,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) diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index 271fddf17f0aa..f7edd5bc59452 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -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) && diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 46684fb933722..cca2a57e54afa 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -92,9 +92,12 @@ 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 mmu_idx */ qemu_get_sbe32(f); /* Discard unused power_mode */ + /* Recompute mmu indices */ + hreg_compute_mem_idx(env); + return 0; } diff --git a/target-ppc/translate.c b/target-ppc/translate.c index b3860ecdea9cd..1119a301154ca 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -11247,8 +11247,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) @@ -11455,7 +11456,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; From 31d0ff5e9b2c083cd8bc68f3d41a76ba55629d79 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 03/81] ppc: Do some batching of TCG tlb flushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Signed-off-by: Benjamin Herrenschmidt [clg: added a 'CPUPPCState *' variable in h_remove() and h_bulk_remove() ] Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_hcall.c | 14 +++++++++++--- 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 | 11 +++-------- target-ppc/mmu_helper.c | 9 ++++++++- target-ppc/translate.c | 39 ++++++++++++++++++++++++++++++++++++--- 8 files changed, 83 insertions(+), 15 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 8f40602a5efbe..2713087c1e5d0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -183,6 +183,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; @@ -193,6 +194,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: @@ -229,7 +231,9 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; int i; + target_ulong rc = H_SUCCESS; for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { target_ulong *tsh = &args[i*2]; @@ -262,14 +266,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, diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 9588b30ee855e..2a96efcbf8135 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1069,6 +1069,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; diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index cf882ebdad4c0..85f38640bdf48 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -717,6 +717,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) @@ -738,6 +743,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); @@ -942,6 +948,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) diff --git a/target-ppc/helper.h b/target-ppc/helper.h index e5a8f7b9b5399..0526322f4d273 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -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) diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index f7edd5bc59452..57da931e3c4d9 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -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__) */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 72c4ab5d751c6..44fc1bfc288cb 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -98,10 +98,8 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) 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]; @@ -112,12 +110,9 @@ 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) @@ -137,7 +132,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; } } diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index ff217941b5a77..930e9d31cfde3 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -26,6 +26,7 @@ #include "mmu-hash32.h" #include "exec/cpu_ldst.h" #include "exec/log.h" +#include "helper_regs.h" //#define DEBUG_MMU //#define DEBUG_BATS @@ -1923,6 +1924,7 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) case POWERPC_MMU_2_06a: case POWERPC_MMU_2_07: case POWERPC_MMU_2_07a: + env->tlb_need_flush = 0; #endif /* defined(TARGET_PPC64) */ tlb_flush(CPU(cpu), 1); break; @@ -1985,7 +1987,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: @@ -2874,6 +2876,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 diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 1119a301154ca..62fabe952c359 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -3312,9 +3312,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); } @@ -3471,6 +3494,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 */ @@ -4878,10 +4910,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 } From 70df180efd059d20292079f16d1f5744badb07cf Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 04/81] ppc: Add a bunch of hypervisor SPRs to Book3s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't give them a KVM reg number yet as no current KVM version supports HV mode. Signed-off-by: Benjamin Herrenschmidt [clg: SPRs AMOR,DAWR,DARWX were already included in commit f401dd32cb8e9] Signed-off-by: Cédric Le Goater --- target-ppc/translate_init.c | 119 +++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 3 deletions(-) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index f51572552bc2e..10a92fdbbdd70 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7788,15 +7788,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); } @@ -8040,6 +8141,17 @@ static void gen_spr_power8_book4(CPUPPCState *env) #endif } +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); @@ -8094,6 +8206,7 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) gen_spr_vtb(env); gen_spr_power8_ic(env); gen_spr_power8_book4(env); + gen_spr_power8_rpr(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); From cf2da03a3c592d67a09398253260c106de1c6dad Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 05/81] ppc: Fix hreg_store_msr() so that non-HV mode cannot alter MSR:HV 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 Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/excp_helper.c | 8 ++++++-- target-ppc/helper_regs.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 85f38640bdf48..b48a34bfab8b2 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -708,8 +708,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); diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index 57da931e3c4d9..12af61cbf19be 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -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; } From 8d0e2960e6d35be17dc1b0bd2ea210152b496725 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 06/81] ppc: Fix rfi/rfid/hrfi/... emulation This reworks emulation of the various "rfi" variants. I removed some masking bits that I couldn't make sense of, the only bit that I am aware we should mask here is POW, the CPU's MSR mask should take care of the rest. This also fixes some problems when running 32-bit userspace under a 64-bit kernel. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/excp_helper.c | 51 ++++++++++++++++------------------------ target-ppc/translate.c | 7 ++++++ 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index b48a34bfab8b2..e3560aa0ea05c 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -922,25 +922,20 @@ void helper_store_msr(CPUPPCState *env, target_ulong val) } } -static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, - target_ulong msrm, int keep_msrh) +static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) { CPUState *cs = CPU(ppc_env_get_cpu(env)); + /* MSR:POW cannot be set by any form of rfi */ + msr &= ~(1ULL << MSR_POW); + #if defined(TARGET_PPC64) - if (msr_is_64bit(env, msr)) { - nip = (uint64_t)nip; - msr &= (uint64_t)msrm; - } else { + /* Switching to 32-bit ? Crop the nip */ + if (!msr_is_64bit(env, msr)) { nip = (uint32_t)nip; - msr = (uint32_t)(msr & msrm); - if (keep_msrh) { - msr |= env->msr & ~((uint64_t)0xFFFFFFFF); - } } #else nip = (uint32_t)nip; - msr &= (uint32_t)msrm; #endif /* XXX: beware: this is false if VLE is supported */ env->nip = nip & ~((target_ulong)0x00000003); @@ -959,26 +954,24 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, void helper_rfi(CPUPPCState *env) { - if (env->excp_model == POWERPC_EXCP_BOOKE) { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0), 0); - } else { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 1); - } + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); } +#define MSR_BOOK3S_MASK #if defined(TARGET_PPC64) void helper_rfid(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 0); + /* The architeture defines a number of rules for which bits + * can change but in practice, we handle this in hreg_store_msr() + * which will be called by do_rfi(), so there is no need to filter + * here + */ + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); } void helper_hrfid(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], - ~((target_ulong)0x783F0000), 0); + do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); } #endif @@ -986,28 +979,24 @@ void helper_hrfid(CPUPPCState *env) /* Embedded PowerPC specific helpers */ void helper_40x_rfci(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3], - ~((target_ulong)0xFFFF0000), 0); + do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); } void helper_rfci(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], - ~((target_ulong)0), 0); + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); } void helper_rfdi(CPUPPCState *env) { /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1], - ~((target_ulong)0), 0); + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); } void helper_rfmci(CPUPPCState *env) { /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ - do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1], - ~((target_ulong)0), 0); + do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); } #endif @@ -1045,7 +1034,7 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, void helper_rfsvc(CPUPPCState *env) { - do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0); + do_rfi(env, env->lr, env->ctr & 0x0000FFFF); } /* Embedded.Processor Control */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 62fabe952c359..7a672cba796d9 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4086,6 +4086,13 @@ static void gen_rfi(DisasContext *ctx) #if defined(CONFIG_USER_ONLY) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); #else + /* This instruction doesn't exist anymore on 64-bit server + * processors compliant with arch 2.x + */ + if (ctx->insns_flags & PPC_SEGMENT_64B) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + return; + } /* Restore CPU state */ if (unlikely(ctx->pr)) { gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); From b24cde5186febdf7690597fac4efca57a9f57e26 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 07/81] ppc: Better figure out if processor has HV mode 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. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/cpu.h | 4 ++++ target-ppc/translate.c | 4 +++- target-ppc/translate_init.c | 21 ++++++++++++++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 2a96efcbf8135..02f2e72e6d143 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1161,6 +1161,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 */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 7a672cba796d9..6f55bcd34a740 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -11495,8 +11495,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_hv || !env->has_hv_mode; +#endif ctx.insns_flags = env->insns_flags; ctx.insns_flags2 = env->insns_flags2; ctx.access_type = -1; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 10a92fdbbdd70..df656e6021b4b 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8579,7 +8579,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) | @@ -9975,10 +9976,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; @@ -10079,6 +10077,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; From 93786421b8f87f5f38a1d723b20c09e17e8b4c6d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 08/81] ppc: tlbie, tlbia and tlbisync are HV only Not that anything remotely recent supports tlbia but ... Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 6f55bcd34a740..5518f01ec3cdf 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4864,7 +4864,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; } @@ -4878,7 +4878,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; } @@ -4892,7 +4892,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; } From 3b764e000a1b2e0f32d188f10671cf15f599943d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 09/81] ppc: Change 'invalid' bit mask of tlbiel and tlbie 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. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/translate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 5518f01ec3cdf..4f18dc8bca2f3 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -9980,8 +9980,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), From 33df300502913990a8bd66b0407902f36f0fef4c Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 12 May 2016 11:32:48 +0200 Subject: [PATCH 10/81] ppc: Fix sign extension issue in mtmsr(d) emulation Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 4f18dc8bca2f3..3fe08e0920d8a 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4415,7 +4415,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 { @@ -4446,7 +4446,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 { From ec7fa089a88323600ee6b4d72acf118a0d82aaaf Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 11/81] ppc: Get out of emulation on SMT "OR" ops 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. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/translate.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 3fe08e0920d8a..875862db33ee6 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -1398,6 +1398,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) { @@ -1453,7 +1466,7 @@ static void gen_or(DisasContext *ctx) } break; case 7: - if (ctx->hv) { + if (ctx->hv && !ctx->pr) { /* Set process priority to very high */ prio = 7; } @@ -1470,6 +1483,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 } @@ -1495,8 +1512,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); From b0920ac9d6ea5d7539255a21b7af8296ab0b1c08 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 12/81] ppc: Add PPC_64H instruction flag to POWER7 and POWER8 This will enable decoding of hrfid Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/translate_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index df656e6021b4b..ccb1b45c1c6ea 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8488,7 +8488,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 | @@ -8568,7 +8568,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 | From 1b1c5bcc7da217fe991918991ad586e870c378de Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 13/81] ppc: Add number of threads per core to the processor definition 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. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/cpu-qom.h | 1 + target-ppc/translate_init.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 7d5e2b36a997c..735981309c5b1 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -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 diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index ccb1b45c1c6ea..c95090f5c549c 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8349,6 +8349,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, const char *name, @@ -8526,6 +8527,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) @@ -8611,6 +8613,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; } #if !defined(CONFIG_USER_ONLY) @@ -9339,6 +9342,12 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) #endif #if !defined(CONFIG_USER_ONLY) + if (pcc->threads_per_core == 0) { + pcc->threads_per_core = 1; + } + 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"); @@ -9359,7 +9368,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 From 6c8e917785f0907bcfc8423fd8b528f275617986 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 14/81] ppc: Create cpu_ppc_set_papr() helper 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. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/translate_init.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index c95090f5c549c..069b2c4b0cf9b 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8621,6 +8621,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) void cpu_ppc_set_papr(PowerPCCPU *cpu) { CPUPPCState *env = &cpu->env; + ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR]; ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; /* PAPR always has exception vectors in RAM not ROM. To ensure this, @@ -8630,6 +8631,19 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu) */ 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; + /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; From f5e021603270ca023e5660f7ee8800d27d273bbd Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 15/81] ppc: Rework POWER7 & POWER8 exception model Properly implement LPES0/1 handling for HV vs. !HV mode and fix AIL implementation. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/excp_helper.c | 134 +++++++++++++-------------------------- 1 file changed, 45 insertions(+), 89 deletions(-) diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index e3560aa0ea05c..e189ae6bf3b6f 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -76,18 +76,8 @@ 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, ail; - - 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; - } + int srr0, srr1, asrr0, asrr1, lev, ail; + bool lpes0; qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx " => %08x (%02x)\n", env->nip, excp, env->error_code); @@ -99,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); /* target registers */ srr0 = SPR_SRR0; @@ -109,6 +101,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) 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 @@ -116,6 +114,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) #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 { @@ -124,6 +123,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } else #endif /* defined(TARGET_PPC64) */ { + lpes0 = true; ail = 0; } @@ -161,10 +161,7 @@ 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 */ @@ -190,23 +187,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 */ @@ -214,9 +208,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)) @@ -231,9 +222,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; @@ -242,23 +230,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; @@ -270,27 +249,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 */ @@ -360,21 +335,13 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) 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; - } + 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; @@ -383,9 +350,6 @@ 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 */ - if (lpes1 == 0) { - new_msr |= (target_ulong)MSR_HVB; - } goto store_next; case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ srr0 = SPR_HSRR0; @@ -412,19 +376,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"); @@ -443,9 +398,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: @@ -462,9 +414,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: @@ -481,9 +430,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: @@ -589,9 +535,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"); @@ -635,6 +578,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]; @@ -643,14 +592,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 || - excp_model == POWERPC_EXCP_POWER8) { - if (env->spr[SPR_LPCR] & LPCR_ILE) { + if (excp_model == POWERPC_EXCP_POWER7) { + 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) { @@ -673,7 +628,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* 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)) { + if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) || + ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) { ail = 0; } /* Handle AIL */ From 81a4addd40663497747d002c00ad678cea382b79 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 16/81] ppc: Fix POWER7 and POWER8 exception definitions We were initializing unused ones and missing some Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/cpu.h | 11 ++++++++++- target-ppc/translate_init.c | 27 +++++++++++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 02f2e72e6d143..bf754f2c780e7 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -205,6 +205,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 */ @@ -247,8 +250,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 */ @@ -2257,6 +2264,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) */ diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 069b2c4b0cf9b..b19bc69cf630e 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -3187,18 +3187,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 /*****************************************************************************/ @@ -8234,10 +8246,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(); } From 89d4791f46c37d53be124b27489c509fd9e92dc1 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:49 +0200 Subject: [PATCH 17/81] ppc: Fix generation if ISI/DSI vs. HV mode 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 Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/mmu-hash64.c | 66 +++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 44fc1bfc288cb..9a23cbc51a24b 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -584,6 +584,44 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } +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) { @@ -594,7 +632,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; @@ -628,26 +666,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(cpu, 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; } @@ -676,14 +709,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; } @@ -693,7 +721,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; } From de00766f66ed667c77d869f898d2569fc61d5f8f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 18/81] ppc: Rework generation of priv and inval interrupts 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). Signed-off-by: Benjamin Herrenschmidt --- linux-user/main.c | 1 + target-ppc/excp_helper.c | 19 ++ target-ppc/translate.c | 677 ++++++++++++++++----------------------- 3 files changed, 302 insertions(+), 395 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 5f3ec9747acd7..02cb8ff23ef97 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1688,6 +1688,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: diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index e189ae6bf3b6f..d4f912a5a9c31 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -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 */ @@ -248,6 +261,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 */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 875862db33ee6..a5e1047640450 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -323,7 +323,19 @@ static inline void gen_debug_exception(DisasContext *ctx) static inline void gen_inval_exception(DisasContext *ctx, uint32_t error) { - gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | error); + /* Will be converted to program check if needed */ + gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_INVAL | error); +} + +static inline void gen_priv_exception(DisasContext *ctx, uint32_t error) +{ + gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_PRIV | error); +} + +static inline void gen_hvpriv_exception(DisasContext *ctx, uint32_t error) +{ + /* Will be converted to program check if needed */ + gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_PRIV | error); } /* Stop translation */ @@ -364,6 +376,20 @@ typedef struct opcode_t { const char *oname; } opcode_t; +/* Helpers for priv. check */ +#define GEN_PRIV do { gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } while(0) + +#if defined(CONFIG_USER_ONLY) +#define CHK_HV GEN_PRIV +#define CHK_SV 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) +#endif + +#define CHK_NONE + + /*****************************************************************************/ /*** Instruction decoding ***/ #define EXTRACT_HELPER(name, shift, nb) \ @@ -2965,7 +2991,7 @@ static void gen_lq(DisasContext *ctx) bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; if (!legal_in_user_mode && ctx->pr) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -3088,7 +3114,7 @@ static void gen_std(DisasContext *ctx) bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; if (!legal_in_user_mode && ctx->pr) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } @@ -4099,7 +4125,7 @@ static void gen_mcrf(DisasContext *ctx) static void gen_rfi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else /* This instruction doesn't exist anymore on 64-bit server * processors compliant with arch 2.x @@ -4109,10 +4135,7 @@ static void gen_rfi(DisasContext *ctx) return; } /* Restore CPU state */ - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_update_cfar(ctx, ctx->nip); gen_helper_rfi(cpu_env); gen_sync_exception(ctx); @@ -4123,13 +4146,10 @@ static void gen_rfi(DisasContext *ctx) static void gen_rfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else /* Restore CPU state */ - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_update_cfar(ctx, ctx->nip); gen_helper_rfid(cpu_env); gen_sync_exception(ctx); @@ -4139,13 +4159,10 @@ static void gen_rfid(DisasContext *ctx) static void gen_hrfid(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else /* Restore CPU state */ - if (unlikely(!ctx->hv)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_HV; gen_helper_hrfid(cpu_env); gen_sync_exception(ctx); #endif @@ -4308,15 +4325,8 @@ static void gen_mfcr(DisasContext *ctx) /* mfmsr */ static void gen_mfmsr(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; tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr); -#endif } static void spr_noaccess(DisasContext *ctx, int gprn, int sprn) @@ -4362,9 +4372,15 @@ static inline void gen_op_mfspr(DisasContext *ctx) 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 { + /* ISA 2.07 defines these as no-ops */ + if ((ctx->insns_flags2 & PPC2_ISA207S) && + (sprn >= 808 && sprn <= 811)) { + /* This is a nop */ + return; + } /* Not defined */ fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); @@ -4372,7 +4388,19 @@ static inline void gen_op_mfspr(DisasContext *ctx) qemu_log("Trying to read 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 { + if (ctx->pr || sprn == 0 || sprn == 4 || sprn == 5 || sprn == 6) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } } } @@ -4419,13 +4447,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(); @@ -4444,20 +4468,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)); @@ -4514,9 +4534,16 @@ static void gen_mtspr(DisasContext *ctx) qemu_log("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 { + /* ISA 2.07 defines these as no-ops */ + if ((ctx->insns_flags2 & PPC2_ISA207S) && + (sprn >= 808 && sprn <= 811)) { + /* This is a nop */ + return; + } + /* Not defined */ if (qemu_log_separate()) { qemu_log("Trying to write invalid spr %d (0x%03x) at " @@ -4524,7 +4551,19 @@ static void gen_mtspr(DisasContext *ctx) } fprintf(stderr, "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 { + if (ctx->pr || sprn == 0) { + gen_hvpriv_exception(ctx, POWERPC_EXCP_INVAL_SPR); + } + } } } @@ -4546,13 +4585,11 @@ static void gen_dcbf(DisasContext *ctx) static void gen_dcbi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv EA, val; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; EA = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); gen_addr_reg_index(ctx, EA); @@ -4562,7 +4599,7 @@ static void gen_dcbi(DisasContext *ctx) gen_qemu_st8(ctx, val, EA); tcg_temp_free(val); tcg_temp_free(EA); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* dcdst */ @@ -4683,72 +4720,64 @@ static void gen_dcba(DisasContext *ctx) static void gen_mfsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfsrin */ static void gen_mfsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsr */ static void gen_mtsr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsrin */ static void gen_mtsrin(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rD(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } #if defined(TARGET_PPC64) @@ -4758,115 +4787,101 @@ static void gen_mtsrin(DisasContext *ctx) static void gen_mfsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfsrin */ static void gen_mfsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsr */ static void gen_mtsr_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_const_tl(SR(ctx->opcode)); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtsrin */ static void gen_mtsrin_64b(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; t0 = tcg_temp_new(); tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28); tcg_gen_andi_tl(t0, t0, 0xF); gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* slbmte */ static void gen_slbmte(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + gen_helper_store_slb(cpu_env, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_slbmfee(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + gen_helper_load_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_slbmfev(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; + gen_helper_load_slb_vsid(cpu_gpr[rS(ctx->opcode)], cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4877,40 +4892,34 @@ static void gen_slbmfev(DisasContext *ctx) static void gen_tlbia(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - 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_HV; + gen_helper_tlbia(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbiel */ static void gen_tlbiel(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - 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; + gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbie */ static void gen_tlbie(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - 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_HV; + if (NARROW_MODE(ctx)) { TCGv t0 = tcg_temp_new(); tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); @@ -4919,56 +4928,52 @@ static void gen_tlbie(DisasContext *ctx) } else { gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsync */ static void gen_tlbsync(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_HV; + /* 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_check_tlb_flush(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } #if defined(TARGET_PPC64) + /* slbia */ static void gen_slbia(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_slbia(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* slbie */ static void gen_slbie(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_slbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } -#endif + +#endif /* defined(TARGET_PPC64) */ /*** External control ***/ /* Optional: */ @@ -5667,14 +5672,11 @@ static void gen_esa(DisasContext *ctx) static void gen_mfrom(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* 602 - 603 - G2 TLB management */ @@ -5683,28 +5685,22 @@ static void gen_mfrom(DisasContext *ctx) static void gen_tlbld_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbli */ static void gen_tlbli_6xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* 74xx TLB management */ @@ -5713,28 +5709,22 @@ static void gen_tlbli_6xx(DisasContext *ctx) static void gen_tlbld_74xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_74xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbli */ static void gen_tlbli_74xx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_helper_74xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* POWER instructions not in PowerPC 601 */ @@ -5748,15 +5738,12 @@ static void gen_clf(DisasContext *ctx) /* cli */ static void gen_cli(DisasContext *ctx) { - /* Cache line invalidate: privileged and treated as no-op */ #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } -#endif + /* Cache line invalidate: privileged and treated as no-op */ + CHK_SV; +#endif /* defined(CONFIG_USER_ONLY) */ } /* dclst */ @@ -5768,15 +5755,13 @@ static void gen_dclst(DisasContext *ctx) static void gen_mfsri(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else int ra = rA(ctx->opcode); int rd = rD(ctx->opcode); TCGv t0; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); tcg_gen_shri_tl(t0, t0, 28); @@ -5785,38 +5770,34 @@ static void gen_mfsri(DisasContext *ctx) tcg_temp_free(t0); if (ra != 0 && ra != rd) tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_rac(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; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_rac(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_rfsvc(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + gen_helper_rfsvc(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* svc is not implemented for now */ @@ -5969,18 +5950,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)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_tlbiva(cpu_env, cpu_gpr[rB(ctx->opcode)]); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* All 405 MAC instructions are translated here */ @@ -6202,38 +6181,34 @@ GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C); static void gen_mfdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv dcrn; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); dcrn = tcg_const_tl(SPR(ctx->opcode)); gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); tcg_temp_free(dcrn); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtdcr */ static void gen_mtdcr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else TCGv dcrn; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); dcrn = tcg_const_tl(SPR(ctx->opcode)); gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); tcg_temp_free(dcrn); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfdcrx */ @@ -6241,18 +6216,15 @@ static void gen_mtdcr(DisasContext *ctx) static void gen_mfdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, cpu_gpr[rA(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mtdcrx */ @@ -6260,18 +6232,15 @@ static void gen_mfdcrx(DisasContext *ctx) static void gen_mtdcrx(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } + CHK_SV; /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); /* Note: Rc update flag set leads to undefined state of Rc0 */ -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* mfdcrux (PPC 460) : user-mode access to DCR */ @@ -6297,28 +6266,19 @@ static void gen_mtdcrux(DisasContext *ctx) /* dccci */ static void gen_dccci(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; - } + CHK_SV; /* interpreted as no-op */ -#endif } /* dcread */ static void gen_dcread(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else TCGv EA, val; - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + + CHK_SV; gen_set_access_type(ctx, ACCESS_CACHE); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -6327,7 +6287,7 @@ static void gen_dcread(DisasContext *ctx) tcg_temp_free(val); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], EA); tcg_temp_free(EA); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* icbt */ @@ -6342,60 +6302,40 @@ static void gen_icbt_40x(DisasContext *ctx) /* iccci */ static void gen_iccci(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; - } + CHK_SV; /* interpreted as no-op */ -#endif } /* icread */ static void gen_icread(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; - } + CHK_SV; /* interpreted as no-op */ -#endif } /* rfci (supervisor only) */ static void gen_rfci_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_40x_rfci(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_rfci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_rfci(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* BookE specific */ @@ -6404,32 +6344,26 @@ static void gen_rfci(DisasContext *ctx) static void gen_rfdi(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_rfdi(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* XXX: not implemented on 440 ? */ static void gen_rfmci(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; /* Restore CPU state */ gen_helper_rfmci(cpu_env); gen_sync_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* TLB management - PowerPC 405 implementation */ @@ -6438,12 +6372,9 @@ static void gen_rfmci(DisasContext *ctx) static void gen_tlbre_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; switch (rB(ctx->opcode)) { case 0: gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_env, @@ -6457,20 +6388,18 @@ static void gen_tlbre_40x(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsx - tlbsx. */ static void gen_tlbsx_40x(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; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -6482,19 +6411,17 @@ static void gen_tlbsx_40x(DisasContext *ctx) tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); gen_set_label(l1); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbwe */ static void gen_tlbwe_40x(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + switch (rB(ctx->opcode)) { case 0: gen_helper_4xx_tlbwe_hi(cpu_env, cpu_gpr[rA(ctx->opcode)], @@ -6508,7 +6435,7 @@ static void gen_tlbwe_40x(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* TLB management - PowerPC 440 implementation */ @@ -6517,12 +6444,10 @@ static void gen_tlbwe_40x(DisasContext *ctx) static void gen_tlbre_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; + switch (rB(ctx->opcode)) { case 0: case 1: @@ -6538,20 +6463,18 @@ static void gen_tlbre_440(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsx - tlbsx. */ static void gen_tlbsx_440(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; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); @@ -6563,19 +6486,16 @@ static void gen_tlbsx_440(DisasContext *ctx) tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); gen_set_label(l1); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbwe */ static void gen_tlbwe_440(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; switch (rB(ctx->opcode)) { case 0: case 1: @@ -6591,7 +6511,7 @@ static void gen_tlbwe_440(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); break; } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* TLB management - PowerPC BookE 2.06 implementation */ @@ -6599,30 +6519,23 @@ static void gen_tlbwe_440(DisasContext *ctx) /* tlbre */ static void gen_tlbre_booke206(DisasContext *ctx) { -#if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + #if defined(CONFIG_USER_ONLY) + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - + CHK_SV; gen_helper_booke206_tlbre(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbsx - tlbsx. */ static void gen_tlbsx_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; if (rA(ctx->opcode)) { t0 = tcg_temp_new(); tcg_gen_mov_tl(t0, cpu_gpr[rD(ctx->opcode)]); @@ -6633,54 +6546,44 @@ static void gen_tlbsx_booke206(DisasContext *ctx) tcg_gen_add_tl(t0, t0, cpu_gpr[rB(ctx->opcode)]); gen_helper_booke206_tlbsx(cpu_env, t0); tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* tlbwe */ static void gen_tlbwe_booke206(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; gen_update_nip(ctx, ctx->nip - 4); gen_helper_booke206_tlbwe(cpu_env); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } 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; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - 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) - 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; t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); @@ -6700,7 +6603,7 @@ static void gen_tlbilx_booke206(DisasContext *ctx) } tcg_temp_free(t0); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } @@ -6708,13 +6611,11 @@ static void gen_tlbilx_booke206(DisasContext *ctx) static void gen_wrtee(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; t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE)); tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); @@ -6724,19 +6625,16 @@ static void gen_wrtee(DisasContext *ctx) * if we just set msr_ee to 1 */ gen_stop_exception(ctx); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* wrteei */ static void gen_wrteei(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } + CHK_SV; if (ctx->opcode & 0x00008000) { tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE)); /* Stop translation to have a chance to raise an exception */ @@ -6744,7 +6642,7 @@ static void gen_wrteei(DisasContext *ctx) } else { tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); } -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /* PowerPC 440 specific instructions */ @@ -6784,29 +6682,21 @@ static void gen_icbt_440(DisasContext *ctx) static void gen_msgclr(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - + CHK_SV; gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_msgsnd(DisasContext *ctx) { #if defined(CONFIG_USER_ONLY) - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); + GEN_PRIV; #else - if (unlikely(ctx->pr)) { - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); - return; - } - + CHK_SV; gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]); -#endif +#endif /* defined(CONFIG_USER_ONLY) */ } /*** Altivec vector extension ***/ @@ -9808,7 +9698,7 @@ static void gen_tcheck(DisasContext *ctx) #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ + gen_priv_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ } #else @@ -9816,10 +9706,7 @@ static inline void gen_##name(DisasContext *ctx) \ #define GEN_TM_PRIV_NOOP(name) \ static inline void gen_##name(DisasContext *ctx) \ { \ - if (unlikely(ctx->pr)) { \ - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \ - return; \ - } \ + CHK_SV; \ if (unlikely(!ctx->tm_enabled)) { \ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \ return; \ From 2ad504472475b626d5133eabf6dac0f5e5874a1b Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 19/81] ppc: Add real mode CI load/store instructions for P7 and P8 Those instructions are only available in hypervisor real mode and allow cache inhibited garded access to devices in that mode. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 4 ++- target-ppc/translate.c | 54 +++++++++++++++++++++++++++++-------- target-ppc/translate_init.c | 6 +++-- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index bf754f2c780e7..b89f29d29a76f 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1973,6 +1973,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 */ @@ -2087,7 +2089,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 */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index a5e1047640450..00cd051915ce7 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -191,7 +191,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 */ @@ -382,9 +382,11 @@ typedef struct opcode_t { #if defined(CONFIG_USER_ONLY) #define CHK_HV GEN_PRIV #define CHK_SV 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) +#define CHK_HVRM do { if (unlikely(ctx->pr || !ctx->hv || ctx->dr)) GEN_PRIV; } while(0) #endif #define CHK_NONE @@ -2918,18 +2920,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); \ @@ -2955,6 +2962,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; @@ -3073,10 +3086,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); \ @@ -3084,7 +3098,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); \ @@ -3101,6 +3118,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) { @@ -3186,7 +3207,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 */ @@ -3212,7 +3233,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 ***/ @@ -10185,7 +10206,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) \ @@ -10202,7 +10223,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) @@ -10218,7 +10245,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) \ @@ -10232,7 +10259,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) @@ -11400,6 +11431,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_hv || !env->has_hv_mode; #endif diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index b19bc69cf630e..d65fa08a3d18d 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8506,7 +8506,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 | @@ -8587,7 +8588,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 | From ce82ebfcad3be87556765b3fdbbeef36672076e6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 20/81] ppc: Turn a bunch of booleans from int to bool Signed-off-by: Benjamin Herrenschmidt Reviewed-by: David Gibson --- target-ppc/translate.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 00cd051915ce7..818127bfcc72c 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -191,21 +191,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; @@ -11438,31 +11437,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) From 24f0afc633c61bc3c5034a76596d029fda9310d6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 21/81] ppc: Move exception generation code out of line 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). Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 818127bfcc72c..d4464b5a893f4 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -281,7 +281,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) { @@ -295,7 +296,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) { @@ -307,7 +309,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; From b814866895bc16a8aa78244c95ef455f9dd21c18 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 22/81] ppc: Add P7/P8 Power Management instructions This adds the ISA 2.06 and later power management instructions (doze, nap, sleep and rvwinkle) and associated wakeup cause testing in LPCR Signed-off-by: Benjamin Herrenschmidt --- 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(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index b89f29d29a76f..a480e324e5604 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -304,6 +304,15 @@ enum { POWERPC_EXCP_TRAP = 0x40, }; +/*****************************************************************************/ +/* 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; @@ -496,6 +505,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) @@ -1172,6 +1189,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 */ @@ -2129,6 +2151,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 | \ @@ -2136,7 +2160,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) }; /*****************************************************************************/ diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index d4f912a5a9c31..3e94abd0c79be 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -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 @@ -897,6 +935,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) { CPUState *cs = CPU(ppc_env_get_cpu(env)); diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 0526322f4d273..b88e47246091f 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -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 diff --git a/target-ppc/translate.c b/target-ppc/translate.c index d4464b5a893f4..9cc37c4ea297b 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -3580,6 +3580,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) \ @@ -9857,6 +9919,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), diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index d65fa08a3d18d..b39210f88563f 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8483,10 +8483,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"; @@ -8495,6 +8530,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 | @@ -8511,7 +8547,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) | @@ -8565,10 +8602,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"; @@ -8577,6 +8657,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 | @@ -8596,7 +8677,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) | @@ -8656,6 +8737,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... */ From a666eee8fb0c4bf000bb5a48bcf26a6a141ad09b Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 23/81] ppc/pnv: Add skeletton PowerNV platform No devices yet, not even an interrupt controller, just to get started. (Folded in Stewart Smith patch to add command lien support) Signed-off-by: Benjamin Herrenschmidt --- default-configs/ppc64-softmmu.mak | 1 + hw/ppc/Makefile.objs | 2 + hw/ppc/pnv.c | 605 ++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 35 ++ 4 files changed, 643 insertions(+) create mode 100644 hw/ppc/pnv.c create mode 100644 include/hw/ppc/pnv.h diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index bb71b23ee78a3..96574c8def324 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -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 diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index c1ffc7771b5d1..cd74c96127923 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -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 diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c new file mode 100644 index 0000000000000..a53439b90e29d --- /dev/null +++ b/hw/ppc/pnv.c @@ -0,0 +1,605 @@ +/* + * 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 "qemu/osdep.h" +#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 + +#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); +} + +#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) + +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", + 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"); + } + 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; + } + _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, const char *kernel_cmdline, uint32_t initrd_base, uint32_t initrd_size) +{ + 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 + * 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); + } + + 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"))); + 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", + &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, + sizeof(hypercall)); + _FDT((fdt_property(fdt, "hcall-instructions", hypercall, + sizeof(hypercall)))); + } + _FDT((fdt_end_node(fdt))); + } + + _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); + 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; +} + +static const VMStateDescription vmstate_powernv = { + .name = "powernv", + .version_id = 1, + .minimum_version_id = 1, +}; + +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; +} + +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, kernel_filename); + 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, machine->kernel_cmdline, + 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) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h new file mode 100644 index 0000000000000..d444dedf2d8fd --- /dev/null +++ b/include/hw/ppc/pnv.h @@ -0,0 +1,35 @@ +#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 . + */ + +#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 */ From 341326bc26a4728660787c06a996711cf968483c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:50 +0200 Subject: [PATCH 24/81] ppc/pnv: Add XSCOM infrastructure 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 Signed-off-by: Benjamin Herrenschmidt --- 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 diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index cd74c96127923..2a7dd42bc601e 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -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 diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a53439b90e29d..e0317d9e30421 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -42,6 +42,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" @@ -311,6 +312,7 @@ static void *powernv_create_fdt(PnvSystem *sys, const char *kernel_cmdline, uint 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))); @@ -371,6 +373,12 @@ static void *powernv_create_fdt(PnvSystem *sys, const char *kernel_cmdline, uint /* 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]; @@ -428,6 +436,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) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c new file mode 100644 index 0000000000000..9e9a8bee272e5 --- /dev/null +++ b/hw/ppc/pnv_xscom.c @@ -0,0 +1,415 @@ + +/* + * QEMU PowerNV XSCOM bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus code: + * Copyright (c) 2009 Alexander Graf + * + * 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 . + */ + +/* 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 "qemu/osdep.h" +#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 + +#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 >*/ + + 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); +} + +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 + */ +} + +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; + } + } + } + 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) +{ + 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 + + /* 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); + XScomState *s = XSCOM(dev); + char *name; + + assert(s->chip_id >= 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); + 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; +} + +#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) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index d444dedf2d8fd..14139a452e021 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -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 { diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h new file mode 100644 index 0000000000000..99de078c88946 --- /dev/null +++ b/include/hw/ppc/pnv_xscom.h @@ -0,0 +1,73 @@ +#ifndef _HW_XSCOM_H +#define _HW_XSCOM_H +/* + * QEMU PowerNV XSCOM bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus definitions: + * Copyright (c) 2009 Alexander Graf + * + * 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 . + */ + +#include + +#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 */ From 41090c2b3201168826c9bf5df5727397835459c7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 25/81] ppc/xics: Rename existing XICS classe to XICS_SPAPR 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. Signed-off-by: Benjamin Herrenschmidt --- 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 | 10 +++++----- hw/ppc/spapr_vio.c | 2 +- include/hw/ppc/xics.h | 24 ++++++++++++------------ 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 8659be0171be9..354fbd862990c 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -715,7 +715,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, Error **errp) +int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp) { ICSState *ics = &icp->ics[src]; int irq; @@ -746,7 +746,7 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp) * 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, Error **errp) { int i, first = -1; @@ -796,7 +796,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); @@ -1015,7 +1015,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; @@ -1054,7 +1054,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); @@ -1063,29 +1063,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); } diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 9029d9ee0bd28..828f3c6eb671c 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -494,8 +494,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, @@ -504,7 +504,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); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b69995e0dce1b..829fa2ac001b3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -121,7 +121,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_reportf_err(err, @@ -132,7 +132,7 @@ static XICSState *xics_system_init(MachineState *machine, } if (!icp) { - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp); + icp = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, errp); } return icp; @@ -1774,7 +1774,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, &error_fatal); + XICS_IRQS_SPAPR, &error_fatal); if (smc->dr_lmb_enabled) { spapr_validate_node_memory(machine, &error_fatal); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 049fb1b325869..fa6f141a53373 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -600,7 +600,7 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, 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, &error_fatal); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 573e635bfb1c9..1d4f84552520f 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -321,7 +321,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, 0); } @@ -359,7 +359,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, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", @@ -370,7 +370,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* Release previous MSIs */ if (msi) { - xics_free(spapr->icp, msi->first_irq, msi->num); + xics_spapr_free(spapr->icp, msi->first_irq, msi->num); g_hash_table_remove(phb->msi, &config_addr); } @@ -1444,7 +1444,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) uint32_t irq; Error *local_err = NULL; - irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err); + irq = xics_spapr_alloc_block(spapr->icp, 0, 1, true, false, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "can't allocate LSIs: "); @@ -1790,7 +1790,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 diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 8aa021fde99c2..a97745de73c8d 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -465,7 +465,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, &local_err); + dev->irq = xics_spapr_alloc(spapr->icp, 0, dev->irq, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index f60b06ae829e0..2f4a94c9e7dc5 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -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,14 +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, Error **errp); -int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align, +int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp); +int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align, Error **errp); -void xics_free(XICSState *icp, int irq, int num); +void xics_spapr_free(XICSState *icp, int irq, int num); void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu); From ddd7fc9024d2e3eb5d939d8489642e81b5730250 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 26/81] ppc/xics: Move SPAPR specific code to a separate file Leave the core ICP/ICS logic in xics.c and move the top level class wrapper, hypercall and RTAS handlers to xics_spapr.c Signed-off-by: Benjamin Herrenschmidt --- default-configs/ppc64-softmmu.mak | 1 + hw/intc/Makefile.objs | 1 + hw/intc/xics.c | 395 +---------------------------- hw/intc/xics_spapr.c | 407 ++++++++++++++++++++++++++++++ include/hw/ppc/xics.h | 23 ++ 5 files changed, 443 insertions(+), 384 deletions(-) create mode 100644 hw/intc/xics_spapr.c diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 96574c8def324..516a6e25aba3a 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -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 diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 0e47f0f9ec1a0..0ab2fe191736c 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -27,6 +27,7 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.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 diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 354fbd862990c..63b4bdcc287cf 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -37,7 +37,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); @@ -228,7 +228,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; @@ -252,7 +252,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; @@ -262,7 +262,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; @@ -275,7 +275,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; @@ -376,12 +376,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; @@ -466,8 +460,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; @@ -644,7 +638,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; @@ -672,7 +666,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)); @@ -691,315 +685,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, Error **errp) -{ - 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)) { - error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); - return -1; - } - irq = irq_hint; - } else { - irq = ics_find_free_block(ics, 1, 1); - if (irq < 0) { - error_setg(errp, "can't allocate IRQ: no IRQ left"); - 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, - Error **errp) -{ - 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) { - error_setg(errp, "can't find a free %d-IRQ block", num); - return -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; @@ -1015,77 +710,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); } diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c new file mode 100644 index 0000000000000..0c6e6374e7cc0 --- /dev/null +++ b/hw/intc/xics_spapr.c @@ -0,0 +1,407 @@ +/* + * 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "trace.h" +#include "qemu/timer.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/xics.h" +#include "qapi/visitor.h" +#include "qapi/error.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, Error **errp) +{ + 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)) { + error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); + return -1; + } + irq = irq_hint; + } else { + irq = ics_find_free_block(ics, 1, 1); + if (irq < 0) { + error_setg(errp, "can't allocate IRQ: no IRQ left"); + 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, + Error **errp) +{ + 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) { + error_setg(errp, "can't find a free %d-IRQ block", num); + return -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) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 2f4a94c9e7dc5..0f565454f3a87 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -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__ */ From 196c17334809b7d6f7615a61cfb9013130ffddf7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 27/81] ppc/xics: Implement H_IPOLL using an accessor None of the other presenter functions directly mucks with the internal state, so don't do it there either. Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 8 ++++++++ hw/intc/xics_spapr.c | 7 ++++--- include/hw/ppc/xics.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 63b4bdcc287cf..ca330d41566b4 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -275,6 +275,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; diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 0c6e6374e7cc0..aa5251903f7a4 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -98,10 +98,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; } diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 0f565454f3a87..184e7a871ebdb 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -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, From 2bad6d02e5023e4e2b60a023b246c35a8ec8184f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 28/81] ppc/xics: Remove unused xics_set_irq_type() Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 11 ----------- include/hw/ppc/xics.h | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index ca330d41566b4..ba6bf146e729b 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -682,17 +682,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 */ diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 184e7a871ebdb..f4e6de936d644 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -166,7 +166,7 @@ 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, Error **errp); int xics_spapr_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align, Error **errp); From c179cd0e3af30e1a4945a6528c408d81a27e120d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 29/81] ppc/xics: Replace "icp" with "xics" in most places 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. Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 132 ++++++++++++++++++------------------ hw/intc/xics_kvm.c | 54 +++++++-------- hw/intc/xics_spapr.c | 56 +++++++-------- hw/ppc/spapr.c | 4 +- hw/ppc/spapr_events.c | 8 +-- hw/ppc/spapr_pci.c | 10 +-- 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, 137 insertions(+), 137 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index ba6bf146e729b..159c366550ee7 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -48,17 +48,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)) { @@ -82,21 +82,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, const char *name, void *opaque, 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, name, &value, errp); } @@ -104,8 +104,8 @@ static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name, static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name, void *opaque, 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; @@ -114,23 +114,23 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name, 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, const char *name, void *opaque, 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, name, &value, errp); } @@ -139,8 +139,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v, const char *name, void *opaque, 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; @@ -149,14 +149,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) @@ -199,9 +199,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; @@ -210,7 +210,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; @@ -218,19 +218,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; @@ -243,22 +243,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); } } @@ -283,31 +283,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; @@ -392,7 +392,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); } } @@ -406,7 +406,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); } } @@ -421,7 +421,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); } } } @@ -460,7 +460,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) @@ -469,7 +469,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; @@ -545,8 +545,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; @@ -646,14 +646,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; } @@ -662,12 +662,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]; } @@ -686,23 +686,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); } } diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 828f3c6eb671c..0fd039562d49a 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -142,7 +142,7 @@ static const TypeInfo icp_kvm_info = { */ static void ics_get_kvm_state(ICSState *ics) { - KVMXICSState *icpkvm = KVM_XICS(ics->icp); + KVMXICSState *xicskvm = KVM_XICS(ics->xics); uint64_t state; struct kvm_device_attr attr = { .flags = 0, @@ -157,7 +157,7 @@ static void ics_get_kvm_state(ICSState *ics) attr.attr = i + ics->offset; - ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); + ret = ioctl(xicskvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); if (ret != 0) { error_report("Unable to retrieve KVM interrupt controller state" " for IRQ %d: %s", i + ics->offset, strerror(errno)); @@ -201,7 +201,7 @@ static void ics_get_kvm_state(ICSState *ics) static int ics_set_kvm_state(ICSState *ics, int version_id) { - KVMXICSState *icpkvm = KVM_XICS(ics->icp); + KVMXICSState *xicskvm = KVM_XICS(ics->xics); uint64_t state; struct kvm_device_attr attr = { .flags = 0, @@ -235,7 +235,7 @@ static int ics_set_kvm_state(ICSState *ics, int version_id) } } - ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); + ret = ioctl(xicskvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); if (ret != 0) { error_report("Unable to restore KVM interrupt controller state" " for IRQs %d: %s", i + ics->offset, strerror(errno)); @@ -321,17 +321,17 @@ static const TypeInfo ics_kvm_info = { /* * XICS-KVM */ -static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu) { CPUState *cs; ICPState *ss; - KVMXICSState *icpkvm = KVM_XICS(icp); + KVMXICSState *xicskvm = KVM_XICS(xics); cs = CPU(cpu); - ss = &icp->ss[cs->cpu_index]; + ss = &xics->ss[cs->cpu_index]; - assert(cs->cpu_index < icp->nr_servers); - if (icpkvm->kernel_xics_fd == -1) { + assert(cs->cpu_index < xics->nr_servers); + if (xicskvm->kernel_xics_fd == -1) { abort(); } @@ -344,13 +344,13 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) return; } - if (icpkvm->kernel_xics_fd != -1) { + if (xicskvm->kernel_xics_fd != -1) { int ret; ss->cs = cs; ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, - icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs)); + xicskvm->kernel_xics_fd, kvm_arch_vcpu_id(cs)); if (ret < 0) { error_report("Unable to connect CPU%ld to kernel XICS: %s", kvm_arch_vcpu_id(cs), strerror(errno)); @@ -360,24 +360,24 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) } } -static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) +static void xics_kvm_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; } -static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers, +static void xics_kvm_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_KVM_ICP); + object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_KVM_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); } } @@ -393,8 +393,8 @@ static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void xics_kvm_realize(DeviceState *dev, Error **errp) { - KVMXICSState *icpkvm = KVM_XICS(dev); - XICSState *icp = XICS_COMMON(dev); + KVMXICSState *xicskvm = KVM_XICS(dev); + XICSState *xics = XICS_COMMON(dev); int i, rc; Error *error = NULL; struct kvm_create_device xics_create_device = { @@ -444,17 +444,17 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp) goto fail; } - icpkvm->kernel_xics_fd = xics_create_device.fd; + xicskvm->kernel_xics_fd = xics_create_device.fd; - object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); + object_property_set_bool(OBJECT(xics->ics), true, "realized", &error); if (error) { error_propagate(errp, error); goto fail; } - assert(icp->nr_servers); - for (i = 0; i < icp->nr_servers; i++) { - object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); + assert(xics->nr_servers); + for (i = 0; i < xics->nr_servers; i++) { + object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized", &error); if (error) { error_propagate(errp, error); goto fail; @@ -480,7 +480,7 @@ static void xics_kvm_initfn(Object *obj) xics->ics = ICS(object_new(TYPE_KVM_ICS)); object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); - xics->ics->icp = xics; + xics->ics->xics = xics; } static void xics_kvm_class_init(ObjectClass *oc, void *data) diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index aa5251903f7a4..a36de02079cad 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -44,7 +44,7 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUState *cs = CPU(cpu); target_ulong cppr = args[0]; - icp_set_cppr(spapr->icp, cs->cpu_index, cppr); + icp_set_cppr(spapr->xics, cs->cpu_index, cppr); return H_SUCCESS; } @@ -54,11 +54,11 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong server = get_cpu_index_by_dt_id(args[0]); target_ulong mfrr = args[1]; - if (server >= spapr->icp->nr_servers) { + if (server >= spapr->xics->nr_servers) { return H_PARAMETER; } - icp_set_mfrr(spapr->icp, server, mfrr); + icp_set_mfrr(spapr->xics, server, mfrr); return H_SUCCESS; } @@ -66,7 +66,7 @@ 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); + uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index); args[0] = xirr; return H_SUCCESS; @@ -76,7 +76,7 @@ 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]; + ICPState *ss = &spapr->xics->ss[cs->cpu_index]; uint32_t xirr = icp_accept(ss); args[0] = xirr; @@ -90,7 +90,7 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUState *cs = CPU(cpu); target_ulong xirr = args[0]; - icp_eoi(spapr->icp, cs->cpu_index, xirr); + icp_eoi(spapr->xics, cs->cpu_index, xirr); return H_SUCCESS; } @@ -99,7 +99,7 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, { CPUState *cs = CPU(cpu); uint32_t mfrr; - uint32_t xirr = icp_ipoll(spapr->icp->ss + cs->cpu_index, &mfrr); + uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr); args[0] = xirr; args[1] = mfrr; @@ -112,7 +112,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->icp->ics; + ICSState *ics = spapr->xics->ics; uint32_t nr, server, priority; if ((nargs != 3) || (nret != 1)) { @@ -124,7 +124,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, 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) + if (!ics_valid_irq(ics, nr) || (server >= ics->xics->nr_servers) || (priority > 0xff)) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; @@ -140,7 +140,7 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->icp->ics; + ICSState *ics = spapr->xics->ics; uint32_t nr; if ((nargs != 1) || (nret != 3)) { @@ -165,7 +165,7 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->icp->ics; + ICSState *ics = spapr->xics->ics; uint32_t nr; if ((nargs != 1) || (nret != 1)) { @@ -191,7 +191,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->icp->ics; + ICSState *ics = spapr->xics->ics; uint32_t nr; if ((nargs != 1) || (nret != 1)) { @@ -215,11 +215,11 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void xics_spapr_realize(DeviceState *dev, Error **errp) { - XICSState *icp = XICS(dev); + XICSState *xics = XICS(dev); Error *error = NULL; int i; - if (!icp->nr_servers) { + if (!xics->nr_servers) { error_setg(errp, "Number of servers needs to be greater 0"); return; } @@ -237,14 +237,14 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp) spapr_register_hypercall(H_EOI, h_eoi); spapr_register_hypercall(H_IPOLL, h_ipoll); - object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); + object_property_set_bool(OBJECT(xics->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); + for (i = 0; i < xics->nr_servers; i++) { + object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized", &error); if (error) { error_propagate(errp, error); return; @@ -258,7 +258,7 @@ static void xics_spapr_initfn(Object *obj) xics->ics = ICS(object_new(TYPE_ICS)); object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); - xics->ics->icp = xics; + xics->ics->xics = xics; } static void xics_spapr_class_init(ObjectClass *oc, void *data) @@ -304,13 +304,13 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum) return -1; } -int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp) +int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi, Error **errp) { - ICSState *ics = &icp->ics[src]; + ICSState *ics = &xics->ics[src]; int irq; if (irq_hint) { - assert(src == xics_find_source(icp, irq_hint)); + assert(src == xics_find_source(xics, irq_hint)); if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); return -1; @@ -335,11 +335,11 @@ int xics_spapr_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **er * 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 xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi, bool align, Error **errp) { int i, first = -1; - ICSState *ics = &icp->ics[src]; + ICSState *ics = &xics->ics[src]; assert(src == 0); /* @@ -379,23 +379,23 @@ static void ics_free(ICSState *ics, int srcno, int num) 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); + trace_xics_ics_free_warn(ics - ics->xics->ics, i + ics->offset); } memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); } } -void xics_spapr_free(XICSState *icp, int irq, int num) +void xics_spapr_free(XICSState *xics, int irq, int num) { - 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]; /* FIXME: implement multiple sources */ assert(src == 0); - trace_xics_ics_free(ics - icp->ics, irq, num); + trace_xics_ics_free(ics - xics->ics, irq, num); ics_free(ics, irq - ics->offset, num); } } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 829fa2ac001b3..4f48eacaaed0c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1626,7 +1626,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); } @@ -1771,7 +1771,7 @@ static void ppc_spapr_init(MachineState *machine) load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; /* Set up Interrupt Controller before we create the VCPUs */ - spapr->icp = xics_system_init(machine, + spapr->xics = xics_system_init(machine, DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), smp_threads), XICS_IRQS_SPAPR, &error_fatal); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index fa6f141a53373..e67e969d8c30b 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -386,7 +386,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_set_signalled(uint32_t drc_index) @@ -465,7 +465,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) @@ -548,7 +548,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; @@ -600,7 +600,7 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, 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, &error_fatal); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 1d4f84552520f..594dac532685e 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -321,7 +321,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, 0); } @@ -359,7 +359,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, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", @@ -370,7 +370,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* Release previous MSIs */ if (msi) { - xics_spapr_free(spapr->icp, msi->first_irq, msi->num); + xics_spapr_free(spapr->xics, msi->first_irq, msi->num); g_hash_table_remove(phb->msi, &config_addr); } @@ -732,7 +732,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 = { @@ -1444,7 +1444,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) uint32_t irq; Error *local_err = NULL; - irq = xics_spapr_alloc_block(spapr->icp, 0, 1, true, false, &local_err); + irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "can't allocate LSIs: "); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index a97745de73c8d..118fdfefc3592 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -465,7 +465,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, &local_err); + dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 03ee006406f6d..a2b3f0b7f8bfe 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -93,7 +93,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); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 815d5eec45d1f..4fd022870ac10 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -49,7 +49,7 @@ struct sPAPRMachineState { struct VIOsPAPRBus *vio_bus; QLIST_HEAD(, sPAPRPHBState) phbs; struct sPAPRNVRAM *nvram; - XICSState *icp; + XICSState *xics; DeviceState *rtc; void *htab; diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index c9733e7552638..714fe76ddb1eb 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -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, diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index f4e6de936d644..cf80784c69832 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -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) From d85896ca782a4a3892dd02a79318e34852f11c39 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 30/81] ppc/xics: Make the ICSState a list 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. Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 86 ++++++++++++++++++++++++++----------------- hw/intc/xics_kvm.c | 28 +++++++------- hw/intc/xics_spapr.c | 71 +++++++++++++++++++---------------- 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, 116 insertions(+), 87 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 159c366550ee7..8a50d851b9cef 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -83,13 +83,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, const char *name, @@ -121,7 +124,6 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name, } assert(info->set_nr_irqs); - assert(xics->ics); info->set_nr_irqs(xics, value, errp); } @@ -199,33 +201,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) @@ -243,7 +247,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)) { @@ -258,7 +265,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); } } @@ -269,6 +276,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); @@ -286,30 +294,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); @@ -392,8 +410,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); } } } @@ -406,7 +423,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); } } @@ -421,7 +438,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); } } } @@ -460,7 +477,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) @@ -646,28 +663,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]; } @@ -688,7 +700,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) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 0fd039562d49a..9bc44e6bc5ee9 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -360,11 +360,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) { @@ -395,6 +390,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 = { @@ -446,10 +442,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); @@ -477,10 +475,14 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp) 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) @@ -490,7 +492,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; } diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index a36de02079cad..9cbe3eb2323a9 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -112,10 +112,10 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->xics->ics; + ICSState *ics = QLIST_FIRST(&spapr->xics->ics); uint32_t nr, server, priority; - if ((nargs != 3) || (nret != 1)) { + if ((nargs != 3) || (nret != 1) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -140,10 +140,10 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->xics->ics; + ICSState *ics = QLIST_FIRST(&spapr->xics->ics); uint32_t nr; - if ((nargs != 1) || (nret != 3)) { + if ((nargs != 1) || (nret != 3) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -165,10 +165,10 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->xics->ics; + ICSState *ics = QLIST_FIRST(&spapr->xics->ics); uint32_t nr; - if ((nargs != 1) || (nret != 1)) { + if ((nargs != 1) || (nret != 1) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -191,10 +191,10 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - ICSState *ics = spapr->xics->ics; + ICSState *ics = QLIST_FIRST(&spapr->xics->ics); uint32_t nr; - if ((nargs != 1) || (nret != 1)) { + if ((nargs != 1) || (nret != 1) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } @@ -216,6 +216,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void xics_spapr_realize(DeviceState *dev, Error **errp) { XICSState *xics = XICS(dev); + ICSState *ics; Error *error = NULL; int i; @@ -237,10 +238,12 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp) spapr_register_hypercall(H_EOI, h_eoi); spapr_register_hypercall(H_IPOLL, h_ipoll); - object_property_set_bool(OBJECT(xics->ics), true, "realized", &error); - if (error) { - error_propagate(errp, error); - return; + QLIST_FOREACH(ics, &xics->ics, list) { + object_property_set_bool(OBJECT(ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } } for (i = 0; i < xics->nr_servers; i++) { @@ -255,10 +258,14 @@ static void xics_spapr_realize(DeviceState *dev, Error **errp) static void xics_spapr_initfn(Object *obj) { XICSState *xics = XICS(obj); + ICSState *ics; + + QLIST_INIT(&xics->ics); - xics->ics = ICS(object_new(TYPE_ICS)); - object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); - xics->ics->xics = xics; + ics = ICS(object_new(TYPE_ICS)); + object_property_add_child(obj, "ics", OBJECT(ics), NULL); + ics->xics = xics; + QLIST_INSERT_HEAD(&xics->ics, ics, list); } static void xics_spapr_class_init(ObjectClass *oc, void *data) @@ -304,13 +311,15 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum) return -1; } -int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi, Error **errp) +int xics_spapr_alloc(XICSState *xics, int irq_hint, bool lsi, Error **errp) { - ICSState *ics = &xics->ics[src]; + ICSState *ics = QLIST_FIRST(&xics->ics); int irq; + if (!ics) { + return -1; + } if (irq_hint) { - assert(src == xics_find_source(xics, irq_hint)); if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); return -1; @@ -326,7 +335,7 @@ int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi, Error **e } ics_set_irq_type(ics, irq - ics->offset, lsi); - trace_xics_alloc(src, irq); + trace_xics_alloc(0, irq); return irq; } @@ -335,13 +344,16 @@ int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi, Error **e * 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 *xics, int src, int num, bool lsi, bool align, +int xics_spapr_alloc_block(XICSState *xics, int num, bool lsi, bool align, Error **errp) { + ICSState *ics = QLIST_FIRST(&xics->ics); int i, first = -1; - ICSState *ics = &xics->ics[src]; - assert(src == 0); + if (!ics) { + return -1; + } + /* * MSIMesage::data is used for storing VIRQ so * it has to be aligned to num to support multiple @@ -368,7 +380,7 @@ int xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi, bool ali } first += ics->offset; - trace_xics_alloc_block(src, first, num, lsi, align); + trace_xics_alloc_block(0, first, num, lsi, align); return first; } @@ -379,7 +391,7 @@ static void ics_free(ICSState *ics, int srcno, int num) for (i = srcno; i < srcno + num; ++i) { if (ICS_IRQ_FREE(ics, i)) { - trace_xics_ics_free_warn(ics - ics->xics->ics, i + ics->offset); + trace_xics_ics_free_warn(0, i + ics->offset); } memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); } @@ -387,15 +399,10 @@ static void ics_free(ICSState *ics, int srcno, int num) void xics_spapr_free(XICSState *xics, int irq, int num) { - int src = xics_find_source(xics, irq); - - if (src >= 0) { - ICSState *ics = &xics->ics[src]; - - /* FIXME: implement multiple sources */ - assert(src == 0); + ICSState *ics = xics_find_source(xics, irq); - trace_xics_ics_free(ics - xics->ics, irq, num); + if (ics) { + trace_xics_ics_free(0, irq, num); ics_free(ics, irq - ics->offset, num); } } diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index e67e969d8c30b..b8f953f8f8b03 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -600,7 +600,7 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, 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, &error_fatal); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 594dac532685e..f1dd87540686d 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -359,7 +359,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, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", @@ -1444,7 +1444,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) uint32_t irq; Error *local_err = NULL; - irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false, &local_err); + irq = xics_spapr_alloc_block(spapr->xics, 1, true, false, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "can't allocate LSIs: "); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 118fdfefc3592..5bf540df5b243 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -465,7 +465,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, &local_err); + dev->irq = xics_spapr_alloc(spapr->xics, dev->irq, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index cf80784c69832..5d63851298ec4 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -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, Error **errp); -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, Error **errp); +int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align, Error **errp); void xics_spapr_free(XICSState *icp, int irq, int num); @@ -190,6 +192,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__ */ From f21030a3906f7c57f4d5592a93990dec2f04682c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 31/81] ppc/xics: An ICS with offset 0 is assumed to be uninitialized This will make life easier for dealing with dynamically configured ICSes such as PHB3 Signed-off-by: Benjamin Herrenschmidt --- include/hw/ppc/xics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 5d63851298ec4..e6095b22dd99f 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -145,7 +145,7 @@ struct ICSState { static inline bool ics_valid_irq(ICSState *ics, uint32_t nr) { - return (nr >= ics->offset) + return (ics->offset != 0) && (nr >= ics->offset) && (nr < (ics->offset + ics->nr_irqs)); } From f3cecdd077058e562693c978d2dedf017a8d6caa Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 32/81] ppc/xics: Move xics_set_nr_irqs() to xics_spapr.c and xics_kvm.c It will not be used by the native implementation. This allows us to also remove the include of spapr.h from the common code Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 12 ------------ hw/intc/xics_kvm.c | 13 ++++++++++++- hw/intc/xics_spapr.c | 13 ++++++++++++- include/hw/ppc/xics.h | 1 - 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 8a50d851b9cef..584b59fd4bae4 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -32,7 +32,6 @@ #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" @@ -698,17 +697,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) * XICS */ -void xics_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp) -{ - 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) { int i; diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 9bc44e6bc5ee9..3dac64b3562c9 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -472,6 +472,17 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp) kvmppc_define_rtas_kernel_token(0, "ibm,int-off"); } +static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp) +{ + ICSState *ics = QLIST_FIRST(&xics->ics); + + /* This needs to be deprecated ... */ + xics->nr_irqs = nr_irqs; + if (ics) { + ics->nr_irqs = nr_irqs; + } +} + static void xics_kvm_initfn(Object *obj) { XICSState *xics = XICS_COMMON(obj); @@ -492,7 +503,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_set_nr_irqs; + xsc->set_nr_irqs = xics_kvm_set_nr_irqs; xsc->set_nr_servers = xics_kvm_set_nr_servers; } diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 9cbe3eb2323a9..3f1f6da7ccc84 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -268,13 +268,24 @@ static void xics_spapr_initfn(Object *obj) QLIST_INSERT_HEAD(&xics->ics, ics, list); } +static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp) +{ + ICSState *ics = QLIST_FIRST(&xics->ics); + + /* This needs to be deprecated ... */ + xics->nr_irqs = nr_irqs; + if (ics) { + ics->nr_irqs = nr_irqs; + } +} + 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_irqs = xics_spapr_set_nr_irqs; xsc->set_nr_servers = xics_set_nr_servers; } diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index e6095b22dd99f..26e361285bf9f 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -190,7 +190,6 @@ void ics_write_xive(ICSState *ics, int nr, int server, 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); ICSState *xics_find_source(XICSState *icp, int irq); From c15422b883c2615f610f76aaef8382b02139cf53 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:51 +0200 Subject: [PATCH 33/81] ppc/xics: Use a helper to add a new ICS Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 6 ++++++ hw/intc/xics_spapr.c | 3 +-- include/hw/ppc/xics.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 584b59fd4bae4..94310e7aacdcf 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -94,6 +94,12 @@ static void xics_common_reset(DeviceState *d) } } +void xics_add_ics(XICSState *xics, ICSState *ics) +{ + ics->xics = xics; + QLIST_INSERT_HEAD(&xics->ics, ics, list); +} + static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 3f1f6da7ccc84..4747183ef011d 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -264,8 +264,7 @@ static void xics_spapr_initfn(Object *obj) ics = ICS(object_new(TYPE_ICS)); object_property_add_child(obj, "ics", OBJECT(ics), NULL); - ics->xics = xics; - QLIST_INSERT_HEAD(&xics->ics, ics, list); + xics_add_ics(xics, ics); } static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, Error **errp) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 26e361285bf9f..91f824af24c73 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -192,5 +192,6 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi); void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp); ICSState *xics_find_source(XICSState *icp, int irq); +void xics_add_ics(XICSState *xics, ICSState *ics); #endif /* __XICS_H__ */ From 5cdfe6db2165f190f0ae4b23c392d5e9db9b4784 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 34/81] ppc/xics: Split ICS into base class and "simple" implementation The existing implementation becomes the "ics-simple" subclass of ICS, so there should be no change in behaviour for SPAPR. This will allow different implementations for the source controllers such as the MSI support of PHB3 on Power8 which uses in-memory state tables for example. Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 123 +++++++++++++++++++++++++++--------------- hw/intc/xics_kvm.c | 2 +- hw/intc/xics_spapr.c | 34 ++++++------ include/hw/ppc/xics.h | 12 +++-- 4 files changed, 108 insertions(+), 63 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 94310e7aacdcf..6ff9f448e3e5c 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -202,9 +202,32 @@ static const TypeInfo xics_common_info = { #define XISR(ss) (((ss)->xirr) & XISR_MASK) #define CPPR(ss) (((ss)->xirr) >> 24) -static void ics_reject(ICSState *ics, int nr); -static void ics_resend(ICSState *ics); -static void ics_eoi(ICSState *ics, int nr); +static void ics_reject(ICSState *ics, uint32_t nr) +{ + ICSStateClass *k = ICS_GET_CLASS(ics); + + if (k->reject) { + k->reject(ics, nr); + } +} + +static void ics_resend(ICSState *ics) +{ + ICSStateClass *k = ICS_GET_CLASS(ics); + + if (k->resend) { + k->resend(ics); + } +} + +static void ics_eoi(ICSState *ics, int nr) +{ + ICSStateClass *k = ICS_GET_CLASS(ics); + + if (k->eoi) { + k->eoi(ics, nr); + } +} static void icp_check_ipi(ICPState *ss, int server) { @@ -407,7 +430,7 @@ static const TypeInfo icp_info = { /* * ICS: Source layer */ -static void resend_msi(ICSState *ics, int srcno) +static void ics_simple_resend_msi(ICSState *ics, int srcno) { ICSIRQState *irq = ics->irqs + srcno; @@ -420,7 +443,7 @@ static void resend_msi(ICSState *ics, int srcno) } } -static void resend_lsi(ICSState *ics, int srcno) +static void ics_simple_resend_lsi(ICSState *ics, int srcno) { ICSIRQState *irq = ics->irqs + srcno; @@ -432,7 +455,7 @@ static void resend_lsi(ICSState *ics, int srcno) } } -static void set_irq_msi(ICSState *ics, int srcno, int val) +static void ics_simple_set_irq_msi(ICSState *ics, int srcno, int val) { ICSIRQState *irq = ics->irqs + srcno; @@ -448,7 +471,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val) } } -static void set_irq_lsi(ICSState *ics, int srcno, int val) +static void ics_simple_set_irq_lsi(ICSState *ics, int srcno, int val) { ICSIRQState *irq = ics->irqs + srcno; @@ -458,21 +481,21 @@ static void set_irq_lsi(ICSState *ics, int srcno, int val) } else { irq->status &= ~XICS_STATUS_ASSERTED; } - resend_lsi(ics, srcno); + ics_simple_resend_lsi(ics, srcno); } -static void ics_set_irq(void *opaque, int srcno, int val) +static void ics_simple_set_irq(void *opaque, int srcno, int val) { ICSState *ics = (ICSState *)opaque; if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - set_irq_lsi(ics, srcno, val); + ics_simple_set_irq_lsi(ics, srcno, val); } else { - set_irq_msi(ics, srcno, val); + ics_simple_set_irq_msi(ics, srcno, val); } } -static void write_xive_msi(ICSState *ics, int srcno) +static void ics_simple_write_xive_msi(ICSState *ics, int srcno) { ICSIRQState *irq = ics->irqs + srcno; @@ -485,31 +508,30 @@ static void write_xive_msi(ICSState *ics, int srcno) icp_irq(ics, irq->server, srcno + ics->offset, irq->priority); } -static void write_xive_lsi(ICSState *ics, int srcno) +static void ics_simple_write_xive_lsi(ICSState *ics, int srcno) { - resend_lsi(ics, srcno); + ics_simple_resend_lsi(ics, srcno); } -void ics_write_xive(ICSState *ics, int nr, int server, - uint8_t priority, uint8_t saved_priority) +void ics_simple_write_xive(ICSState *ics, int srcno, int server, + uint8_t priority, uint8_t saved_priority) { - int srcno = nr - ics->offset; ICSIRQState *irq = ics->irqs + srcno; irq->server = server; irq->priority = priority; irq->saved_priority = saved_priority; - trace_xics_ics_write_xive(nr, srcno, server, priority); + trace_xics_ics_write_xive(ics->offset + srcno, srcno, server, priority); if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - write_xive_lsi(ics, srcno); + ics_simple_write_xive_lsi(ics, srcno); } else { - write_xive_msi(ics, srcno); + ics_simple_write_xive_msi(ics, srcno); } } -static void ics_reject(ICSState *ics, int nr) +static void ics_simple_reject(ICSState *ics, uint32_t nr) { ICSIRQState *irq = ics->irqs + nr - ics->offset; @@ -518,21 +540,21 @@ static void ics_reject(ICSState *ics, int nr) irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ } -static void ics_resend(ICSState *ics) +static void ics_simple_resend(ICSState *ics) { int i; for (i = 0; i < ics->nr_irqs; i++) { /* FIXME: filter by server#? */ if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { - resend_lsi(ics, i); + ics_simple_resend_lsi(ics, i); } else { - resend_msi(ics, i); + ics_simple_resend_msi(ics, i); } } } -static void ics_eoi(ICSState *ics, int nr) +static void ics_simple_eoi(ICSState *ics, uint32_t nr) { int srcno = nr - ics->offset; ICSIRQState *irq = ics->irqs + srcno; @@ -544,9 +566,9 @@ static void ics_eoi(ICSState *ics, int nr) } } -static void ics_reset(DeviceState *dev) +static void ics_simple_reset(DeviceState *dev) { - ICSState *ics = ICS(dev); + ICSState *ics = ICS_SIMPLE(dev); int i; uint8_t flags[ics->nr_irqs]; @@ -563,7 +585,7 @@ static void ics_reset(DeviceState *dev) } } -static int ics_post_load(ICSState *ics, int version_id) +static int ics_simple_post_load(ICSState *ics, int version_id) { int i; @@ -596,7 +618,7 @@ static int ics_dispatch_post_load(void *opaque, int version_id) return 0; } -static const VMStateDescription vmstate_ics_irq = { +static const VMStateDescription vmstate_ics_simple_irq = { .name = "ics/irq", .version_id = 2, .minimum_version_id = 1, @@ -610,7 +632,7 @@ static const VMStateDescription vmstate_ics_irq = { }, }; -static const VMStateDescription vmstate_ics = { +static const VMStateDescription vmstate_ics_simple = { .name = "ics", .version_id = 1, .minimum_version_id = 1, @@ -621,48 +643,60 @@ static const VMStateDescription vmstate_ics = { VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, - vmstate_ics_irq, ICSIRQState), + vmstate_ics_simple_irq, + ICSIRQState), VMSTATE_END_OF_LIST() }, }; -static void ics_initfn(Object *obj) +static void ics_simple_initfn(Object *obj) { - ICSState *ics = ICS(obj); + ICSState *ics = ICS_SIMPLE(obj); ics->offset = XICS_IRQ_BASE; } -static void ics_realize(DeviceState *dev, Error **errp) +static void ics_simple_realize(DeviceState *dev, Error **errp) { - ICSState *ics = ICS(dev); + ICSState *ics = ICS_SIMPLE(dev); if (!ics->nr_irqs) { error_setg(errp, "Number of interrupts needs to be greater 0"); return; } ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); - ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); + ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); } -static void ics_class_init(ObjectClass *klass, void *data) +static void ics_simple_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); - dc->realize = ics_realize; - dc->vmsd = &vmstate_ics; - dc->reset = ics_reset; - isc->post_load = ics_post_load; + dc->realize = ics_simple_realize; + dc->vmsd = &vmstate_ics_simple; + dc->reset = ics_simple_reset; + isc->post_load = ics_simple_post_load; + isc->reject = ics_simple_reject; + isc->resend = ics_simple_resend; + isc->eoi = ics_simple_eoi; } +static const TypeInfo ics_simple_info = { + .name = TYPE_ICS_SIMPLE, + .parent = TYPE_ICS, + .instance_size = sizeof(ICSState), + .class_init = ics_simple_class_init, + .class_size = sizeof(ICSStateClass), + .instance_init = ics_simple_initfn, +}; + static const TypeInfo ics_info = { .name = TYPE_ICS, .parent = TYPE_DEVICE, + .abstract = true, .instance_size = sizeof(ICSState), - .class_init = ics_class_init, .class_size = sizeof(ICSStateClass), - .instance_init = ics_initfn, }; /* @@ -691,7 +725,7 @@ qemu_irq xics_get_qirq(XICSState *xics, int irq) return NULL; } -void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) +void ics_simple_set_irq_type(ICSState *ics, int srcno, bool lsi) { assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK)); @@ -722,6 +756,7 @@ void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp) static void xics_register_types(void) { type_register_static(&xics_common_info); + type_register_static(&ics_simple_info); type_register_static(&ics_info); type_register_static(&icp_info); } diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 3dac64b3562c9..8d300c99cfdcb 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -313,7 +313,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) static const TypeInfo ics_kvm_info = { .name = TYPE_KVM_ICS, - .parent = TYPE_ICS, + .parent = TYPE_ICS_SIMPLE, .instance_size = sizeof(ICSState), .class_init = ics_kvm_class_init, }; diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 4747183ef011d..f4376eebef731 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -113,7 +113,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nret, target_ulong rets) { ICSState *ics = QLIST_FIRST(&spapr->xics->ics); - uint32_t nr, server, priority; + uint32_t nr, src_no, server, priority; if ((nargs != 3) || (nret != 1) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -130,7 +130,8 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - ics_write_xive(ics, nr, server, priority, priority); + src_no = nr - ics->offset; + ics_simple_write_xive(ics, src_no, server, priority, priority); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -141,7 +142,7 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nret, target_ulong rets) { ICSState *ics = QLIST_FIRST(&spapr->xics->ics); - uint32_t nr; + uint32_t nr, src_no; if ((nargs != 1) || (nret != 3) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -156,8 +157,9 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, } 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); + src_no = nr - ics->offset; + rtas_st(rets, 1, ics->irqs[src_no].server); + rtas_st(rets, 2, ics->irqs[src_no].priority); } static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -166,7 +168,7 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nret, target_ulong rets) { ICSState *ics = QLIST_FIRST(&spapr->xics->ics); - uint32_t nr; + uint32_t nr, src_no; if ((nargs != 1) || (nret != 1) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -180,8 +182,9 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, - ics->irqs[nr - ics->offset].priority); + src_no = nr - ics->offset; + ics_simple_write_xive(ics, src_no, ics->irqs[src_no].server, 0xff, + ics->irqs[src_no].priority); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -192,7 +195,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t nret, target_ulong rets) { ICSState *ics = QLIST_FIRST(&spapr->xics->ics); - uint32_t nr; + uint32_t nr, src_no; if ((nargs != 1) || (nret != 1) || !ics) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -206,9 +209,10 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, 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); + src_no = nr - ics->offset; + ics_simple_write_xive(ics, src_no, ics->irqs[src_no].server, + ics->irqs[src_no].saved_priority, + ics->irqs[src_no].saved_priority); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -262,7 +266,7 @@ static void xics_spapr_initfn(Object *obj) QLIST_INIT(&xics->ics); - ics = ICS(object_new(TYPE_ICS)); + ics = ICS(object_new(TYPE_ICS_SIMPLE)); object_property_add_child(obj, "ics", OBJECT(ics), NULL); xics_add_ics(xics, ics); } @@ -344,7 +348,7 @@ int xics_spapr_alloc(XICSState *xics, int irq_hint, bool lsi, Error **errp) irq += ics->offset; } - ics_set_irq_type(ics, irq - ics->offset, lsi); + ics_simple_set_irq_type(ics, irq - ics->offset, lsi); trace_xics_alloc(0, irq); return irq; @@ -385,7 +389,7 @@ int xics_spapr_alloc_block(XICSState *xics, int num, bool lsi, bool align, if (first >= 0) { for (i = first; i < first + num; ++i) { - ics_set_irq_type(ics, i, lsi); + ics_simple_set_irq_type(ics, i, lsi); } } first += ics->offset; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 91f824af24c73..74a37eddf3de3 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -116,6 +116,9 @@ struct ICPState { #define TYPE_ICS "ics" #define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS) +#define TYPE_ICS_SIMPLE "ics-simple" +#define ICS_SIMPLE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_SIMPLE) + #define TYPE_KVM_ICS "icskvm" #define KVM_ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_KVM_ICS) @@ -129,6 +132,9 @@ struct ICSStateClass { void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); + void (*reject)(ICSState *s, uint32_t irq); + void (*resend)(ICSState *s); + void (*eoi)(ICSState *s, uint32_t irq); }; struct ICSState { @@ -185,10 +191,10 @@ 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, - uint8_t priority, uint8_t saved_priority); +void ics_simple_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 ics_simple_set_irq_type(ICSState *ics, int srcno, bool lsi); void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp); ICSState *xics_find_source(XICSState *icp, int irq); From 86608cfe2c558abf4c9fa76a19f4d0b6ca8bc863 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 35/81] ppc/xics: Add "native" XICS subclass This provides MMIO based ICP access as found on POWER8 Signed-off-by: Benjamin Herrenschmidt --- default-configs/ppc64-softmmu.mak | 3 +- hw/intc/Makefile.objs | 1 + hw/intc/xics_native.c | 295 ++++++++++++++++++++++++++++++ include/hw/ppc/xics.h | 14 ++ 4 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 hw/intc/xics_native.c diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 516a6e25aba3a..d30176ed4ce98 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -49,8 +49,9 @@ CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y CONFIG_LIBDECNUMBER=y # For pSeries -CONFIG_XICS=$(CONFIG_PSERIES) +CONFIG_XICS=$(or $(CONFIG_PSERIES),$(CONFIG_POWERNV)) CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) +CONFIG_XICS_NATIVE=$(CONFIG_POWERNV) CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP CONFIG_MC146818RTC=y diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 0ab2fe191736c..4d3b180a5b7d4 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -28,6 +28,7 @@ obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o +obj-$(CONFIG_XICS_NATIVE) += xics_native.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o diff --git a/hw/intc/xics_native.c b/hw/intc/xics_native.c new file mode 100644 index 0000000000000..90cae1ecc8dbf --- /dev/null +++ b/hw/intc/xics_native.c @@ -0,0 +1,295 @@ +/* + * QEMU PowerPC hardware System Emulator + * + * Native version of ICS/ICP + * + * 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "trace.h" +#include "qemu/timer.h" +#include "hw/ppc/xics.h" +#include "qapi/visitor.h" +#include "qapi/error.h" + +#include + +//#define DEBUG_MM(fmt...) printf(fmt) +#define DEBUG_MM(fmt...) do { } while(0) + +static void xics_native_initfn(Object *obj) +{ + XICSState *xics = XICS_NATIVE(obj); + + QLIST_INIT(&xics->ics); +} + +static uint64_t icp_mm_read(void *opaque, hwaddr addr, unsigned width) +{ + XICSState *s = opaque; + int32_t cpu_id, server; + uint32_t val; + ICPState *ss; + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + cpu_id = (addr & (ICP_MM_SIZE - 1)) >> 12; + server = get_cpu_index_by_dt_id(cpu_id); + if (server < 0) { + fprintf(stderr, "XICS: Bad ICP server %d\n", server); + goto bad_access; + } + ss = &s->ss[server]; + + switch(addr & 0xffc) { + case 0: /* poll */ + val = icp_ipoll(ss, NULL); + if (byte0) { + val >>= 24; + } else if (width != 4) { + goto bad_access; + } + break; + case 4: /* xirr */ + if (byte0) { + val = icp_ipoll(ss, NULL) >> 24; + } else if (width == 4) { + val = icp_accept(ss); + } else { + goto bad_access; + } + break; + case 12: + if (byte0) { + val = ss->mfrr; + } else { + goto bad_access; + } + break; + case 16: + if (width == 4) { + val = ss->links[0]; + } else { + goto bad_access; + } + break; + case 20: + if (width == 4) { + val = ss->links[1]; + } else { + goto bad_access; + } + break; + case 24: + if (width == 4) { + val = ss->links[2]; + } else { + goto bad_access; + } + break; + default: +bad_access: + fprintf(stderr, "XICS: Bad ICP access %llx/%d\n", + (unsigned long long)addr, width); + val = 0xffffffff; + } + DEBUG_MM("icp_mm_read(addr=%016llx,serv=0x%x/%d,off=%d,w=%d,val=0x%08x)\n", + (unsigned long long)addr, cpu_id, server, (int)(addr & 0xffc), + width, val); + + return val; +} + +static void icp_mm_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + XICSState *s = opaque; + int32_t cpu_id, server; + ICPState *ss; + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + cpu_id = (addr & (ICP_MM_SIZE - 1)) >> 12; + server = get_cpu_index_by_dt_id(cpu_id); + if (server < 0) { + fprintf(stderr, "XICS: Bad ICP server %d\n", server); + goto bad_access; + } + ss = &s->ss[server]; + + DEBUG_MM("icp_mm_write(addr=%016llx,serv=0x%x/%d,off=%d,w=%d,val=0x%08x)\n", + (unsigned long long)addr, cpu_id, server, + (int)(addr & 0xffc), width, (uint32_t)val); + + switch(addr & 0xffc) { + case 4: /* xirr */ + if (byte0) { + icp_set_cppr(s, server, val); + } else if (width == 4) { + icp_eoi(s, server, val); + } else { + goto bad_access; + } + break; + case 12: + if (byte0) { + icp_set_mfrr(s, server, val); + } else { + goto bad_access; + } + break; + case 16: + if (width == 4) { + ss->links[0] = val; + } else { + goto bad_access; + } + break; + case 20: + if (width == 4) { + ss->links[1] = val; + } else { + goto bad_access; + } + break; + case 24: + if (width == 4) { + ss->links[2] = val; + } else { + goto bad_access; + } + break; + default: + bad_access: + val = 0xffffffff; + } +} + +static const MemoryRegionOps icp_mm_ops = { + .read = icp_mm_read, + .write = icp_mm_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + +#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) + +void xics_create_native_icp_node(XICSState *s, void *fdt, + uint32_t base, uint32_t count) +{ + uint64_t addr; + char *name; + const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; + uint32_t irange[2], i, rsize; + uint64_t *reg; + + addr = ICP_MM_BASE | (base << 12); + + irange[0] = cpu_to_be32(base); + irange[1] = cpu_to_be32(count); + + rsize = sizeof(uint64_t) * 2 * count; + reg = g_malloc(rsize); + for (i = 0; i < count; i++) { + reg[i * 2] = cpu_to_be64(addr | ((base + i) * 0x1000)); + reg[i * 2 + 1] = cpu_to_be64(0x1000); + } + + name = g_strdup_printf("interrupt-controller@%"PRIX64, addr); + + /* interrupt controller */ + _FDT((fdt_begin_node(fdt, name))); + g_free(name); + + _FDT((fdt_property(fdt, "compatible", compat, sizeof(compat)))); + _FDT((fdt_property(fdt, "reg", reg, rsize))); + _FDT((fdt_property_string(fdt, "device_type", + "PowerPC-External-Interrupt-Presentation"))); + _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); + _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", + irange, sizeof(irange)))); + _FDT((fdt_property_cell(fdt, "#interrupt-cells", 1))); + _FDT((fdt_property_cell(fdt, "#address-cells", 0))); + _FDT((fdt_end_node(fdt))); +} + +static void xics_native_realize(DeviceState *dev, Error **errp) +{ + XICSState *s = XICS_NATIVE(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *error = NULL; + int i; + + if (!s->nr_servers) { + error_setg(errp, "Number of servers needs to be greater 0"); + return; + } + + /* Register MMIO regions */ + memory_region_init_io(&s->icp_mmio, OBJECT(s), &icp_mm_ops, s, "icp", + ICP_MM_SIZE); + sysbus_init_mmio(sbd, &s->icp_mmio); + sysbus_mmio_map(sbd, 0, ICP_MM_BASE); + + for (i = 0; i < s->nr_servers; i++) { + object_property_set_bool(OBJECT(&s->ss[i]), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + } +} + +static void xics_native_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + XICSStateClass *xsc = XICS_NATIVE_CLASS(oc); + + dc->realize = xics_native_realize; + xsc->set_nr_servers = xics_set_nr_servers; +} + +static const TypeInfo xics_native_info = { + .name = TYPE_XICS_NATIVE, + .parent = TYPE_XICS_COMMON, + .instance_size = sizeof(XICSState), + .class_size = sizeof(XICSStateClass), + .class_init = xics_native_class_init, + .instance_init = xics_native_initfn, +}; + +static void xics_native_register_types(void) +{ + type_register_static(&xics_native_info); +} +type_init(xics_native_register_types) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 74a37eddf3de3..6281ef6386e4d 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -38,6 +38,9 @@ #define TYPE_XICS_SPAPR_KVM "xics-spapr-kvm" #define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_XICS_SPAPR_KVM) +#define TYPE_XICS_NATIVE "xics-native" +#define XICS_NATIVE(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_NATIVE) + #define XICS_COMMON_CLASS(klass) \ OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON) #define XICS_SPAPR_CLASS(klass) \ @@ -46,6 +49,8 @@ OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON) #define XICS_SPAPR_GET_CLASS(obj) \ OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_SPAPR) +#define XICS_NATIVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_NATIVE) #define XICS_IPI 0x2 #define XICS_BUID 0x1 @@ -80,6 +85,7 @@ struct XICSState { uint32_t nr_irqs; ICPState *ss; QLIST_HEAD(, ICSState) ics; + MemoryRegion icp_mmio; }; #define TYPE_ICP "icp" @@ -111,8 +117,13 @@ struct ICPState { uint8_t mfrr; qemu_irq output; bool cap_irq_xics_enabled; + uint32_t links[3]; }; +/* This should be an XSCOM BAR ... the size is arbitrary as well */ +#define ICP_MM_BASE 0x0003FFFF80000000 +#define ICP_MM_SIZE 0x0000000010000000 + #define TYPE_ICS "ics" #define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS) @@ -182,6 +193,9 @@ void xics_spapr_free(XICSState *icp, int irq, int num); void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu); +void xics_create_native_icp_node(XICSState *s, void *fdt, + uint32_t base, uint32_t count); + /* Internal XICS interfaces */ int get_cpu_index_by_dt_id(int cpu_dt_id); From a75d59e5db8db0c63b34cfd311c6a1526ca4fc7a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 36/81] ppc/xics: Add xics to the monitor "info pic" command Useful to debug interrupt problems. Signed-off-by: Benjamin Herrenschmidt --- hmp-commands-info.hx | 2 ++ hw/intc/xics.c | 38 ++++++++++++++++++++++++++++++++++++++ hw/ppc/ppc.c | 14 ++++++++++++++ include/hw/ppc/ppc.h | 2 ++ include/hw/ppc/xics.h | 2 ++ monitor.c | 4 ++++ 6 files changed, 62 insertions(+) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 52539c3109176..33122644a24e9 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -203,6 +203,8 @@ ETEXI .mhandler.cmd = sun4m_hmp_info_pic, #elif defined(TARGET_LM32) .mhandler.cmd = lm32_hmp_info_pic, +#elif defined(TARGET_PPC) + .mhandler.cmd = ppc_hmp_info_pic, #else .mhandler.cmd = hmp_info_pic, #endif diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 6ff9f448e3e5c..7e1dcdf6c77b5 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -35,6 +35,9 @@ #include "hw/ppc/xics.h" #include "qemu/error-report.h" #include "qapi/visitor.h" +#include "monitor/monitor.h" + +static XICSState *g_xics; int get_cpu_index_by_dt_id(int cpu_dt_id) { @@ -174,6 +177,9 @@ static void xics_common_initfn(Object *obj) object_property_add(obj, "nr_servers", "int", xics_prop_get_nr_servers, xics_prop_set_nr_servers, NULL, NULL, NULL); + + /* For exclusive use of monitor command */ + g_xics = XICS_COMMON(obj); } static void xics_common_class_init(ObjectClass *oc, void *data) @@ -618,6 +624,38 @@ static int ics_dispatch_post_load(void *opaque, int version_id) return 0; } +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict) +{ + ICSState *ics; + uint32_t i; + + for (i = 0; i < g_xics->nr_servers; i++) { + ICPState *icp = &g_xics->ss[i]; + + if (!icp->output) { + continue; + } + monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", + i, icp->xirr, icp->xirr_owner, + icp->pending_priority, icp->mfrr); + } + QLIST_FOREACH(ics, &g_xics->ics, list) { + monitor_printf(mon, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = ics->irqs + i; + + if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { + continue; + } + monitor_printf(mon, " %4x %s %02x %02x\n", + ics->offset + i, + (irq->flags & XICS_FLAGS_IRQ_LSI) ? "LSI" : "MSI", + irq->priority, irq->status); + } + } +} + static const VMStateDescription vmstate_ics_simple_irq = { .name = "ics/irq", .version_id = 2, diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 38ff2e1596ca9..c57fdf24d494f 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -27,6 +27,7 @@ #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" +#include "hw/i386/pc.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" @@ -38,6 +39,10 @@ #include "kvm_ppc.h" #include "trace.h" +#if defined(TARGET_PPC64) +#include "hw/ppc/xics.h" +#endif + //#define PPC_DEBUG_IRQ //#define PPC_DEBUG_TB @@ -1343,3 +1348,12 @@ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) return NULL; } + +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict) +{ + /* Call in turn every PIC around. OpenPIC doesn't have one yet */ +#ifdef TARGET_PPC64 + xics_hmp_info_pic(mon, qdict); +#endif + hmp_info_pic(mon, qdict); +} diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 14efd0ca31780..d5c648d322ac8 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -1,6 +1,8 @@ #ifndef HW_PPC_H #define HW_PPC_H 1 +void ppc_hmp_info_pic(Monitor *mon, const QDict *qdict); + void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); /* PowerPC hardware exceptions management helpers */ diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 6281ef6386e4d..dfcdfaa156a4d 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -214,4 +214,6 @@ void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, Error **errp); ICSState *xics_find_source(XICSState *icp, int irq); void xics_add_ics(XICSState *xics, ICSState *ics); +void xics_hmp_info_pic(Monitor *mon, const QDict *qdict); + #endif /* __XICS_H__ */ diff --git a/monitor.c b/monitor.c index d1c193013e365..7f039a1524566 100644 --- a/monitor.c +++ b/monitor.c @@ -90,6 +90,10 @@ #include "hw/s390x/storage-keys.h" #endif +#if defined(TARGET_PPC) +#include "hw/ppc/ppc.h" +#endif + /* * Supported types: * From 81d19c29b44dc9203723883c1495ddc014a1756c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 37/81] ppc/pnv: Wire up XICS native with PowerNV platform Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/pnv.c | 71 ++++++++++++++++++++++++++++++++++++++++++- include/hw/ppc/pnv.h | 2 ++ include/hw/ppc/xics.h | 2 ++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index e0317d9e30421..93a074cbd2523 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -42,11 +42,12 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/pnv.h" #include "hw/loader.h" +#include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" #include "exec/address-spaces.h" #include "qemu/config-file.h" -#include "qemu/error-report.h" +#include "qapi/error.h" #include "trace.h" #include "hw/nmi.h" @@ -82,6 +83,59 @@ struct sPowerNVMachineState { PnvSystem sys; }; +static XICSState *try_create_xics(const char *type, int nr_servers, + int nr_irqs, Error **errp) +{ + Error *err = NULL; + DeviceState *dev; + + dev = qdev_create(NULL, type); + qdev_prop_set_uint32(dev, "nr_servers", nr_servers); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); + return NULL; + } + + return XICS_COMMON(dev); +} + +static XICSState *xics_system_init(int nr_servers, int nr_irqs) +{ + XICSState *xics = NULL; + +#if 0 /* Some fixing needed to handle native ICS in KVM mode */ + if (kvm_enabled()) { + QemuOpts *machine_opts = qemu_get_machine_opts(); + bool irqchip_allowed = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", true); + bool irqchip_required = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", false); + if (irqchip_allowed) { + icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, + &error_abort); + } + + if (irqchip_required && !icp) { + perror("Failed to create in-kernel XICS\n"); + abort(); + } + } +#endif + + if (!xics) { + xics = try_create_xics(TYPE_XICS_NATIVE, nr_servers, nr_irqs, + &error_abort); + } + + if (!xics) { + perror("Failed to create XICS\n"); + abort(); + } + return xics; +} + static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, size_t maxsize) { @@ -370,6 +424,13 @@ static void *powernv_create_fdt(PnvSystem *sys, const char *kernel_cmdline, uint _FDT((fdt_end_node(fdt))); + /* ICPs */ + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t base_server = ppc_get_vcpu_dt_id(cpu); + xics_create_native_icp_node(sys->xics, fdt, base_server, smt); + } + /* Memory */ _FDT((powernv_populate_memory(fdt))); @@ -455,11 +516,17 @@ static void ppc_powernv_init(MachineState *machine) MemoryRegion *ram = g_new(MemoryRegion, 1); sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine); PnvSystem *sys = &pnv_machine->sys; + XICSState *xics; long fw_size; char *filename; void *fdt; int i; + /* Set up Interrupt Controller before we create the VCPUs */ + xics = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads, + XICS_IRQS_POWERNV); + sys->xics = xics; + /* init CPUs */ if (cpu_model == NULL) { cpu_model = kvm_enabled() ? "host" : "POWER8"; @@ -479,6 +546,8 @@ static void ppc_powernv_init(MachineState *machine) /* MSR[IP] doesn't exist nowadays */ env->msr_mask &= ~(1 << 6); + xics_cpu_setup(xics, cpu); + qemu_register_reset(powernv_cpu_reset, cpu); } diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 14139a452e021..90b76ecf3b2c5 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -21,6 +21,7 @@ #include "hw/hw.h" typedef struct XScomBus XScomBus; +typedef struct XICSState XICSState; /* Should we turn that into a QOjb of some sort ? */ typedef struct PnvChip { @@ -29,6 +30,7 @@ typedef struct PnvChip { } PnvChip; typedef struct PnvSystem { + XICSState *xics; uint32_t num_chips; #define PNV_MAX_CHIPS 1 PnvChip chips[PNV_MAX_CHIPS]; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index dfcdfaa156a4d..f3f6b472fb6b0 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -183,6 +183,8 @@ struct ICSIRQState { }; #define XICS_IRQS_SPAPR 1024 +#define XICS_IRQS_POWERNV (1 << 19) + qemu_irq xics_get_qirq(XICSState *icp, int irq); From 02d9e78fe1c1839abdc003c9f79dc900d5cd58dd Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 38/81] ppc/pnv: Add LPC controller and hook it up with a UART and RTC This adds a model of the POWER8 LPC controller. It is then used by the PowerNV code to attach a UART and RTC, which, with the right version of OPAL firmware, will provide a working console. This version of the LPC controller model doesn't yet implement support for the SerIRQ deserializer present in the Naples version of the chip though some preliminary work is there. Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/Makefile.objs | 2 +- hw/ppc/pnv.c | 49 +++- hw/ppc/pnv_lpc.c | 527 +++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 6 + 4 files changed, 579 insertions(+), 5 deletions(-) create mode 100644 hw/ppc/pnv_lpc.c diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 2a7dd42bc601e..5ebf0e0cd3d44 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -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 pnv_xscom.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_lpc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 93a074cbd2523..293318630beee 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -45,6 +45,9 @@ #include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" +#include "hw/isa/isa.h" +#include "hw/char/serial.h" +#include "hw/timer/mc146818rtc.h" #include "exec/address-spaces.h" #include "qemu/config-file.h" #include "qapi/error.h" @@ -487,7 +490,15 @@ static const VMStateDescription vmstate_powernv = { .minimum_version_id = 1, }; -static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no) +static void pnv_lpc_irq_handler_cpld(void *opaque, int n, int level) +{ + /* We don't yet emulate the PSI bridge which provides the external + * interrupt, so just drop interrupts on the floor + */ +} + +static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, + bool has_lpc, bool has_lpc_irq) { PnvChip *chip = &sys->chips[chip_no]; @@ -500,6 +511,27 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no) /* Set up XSCOM bus */ xscom_create(chip); + + /* Create LPC controller */ + if (has_lpc) { + pnv_lpc_create(chip, has_lpc_irq); + + /* If we don't use the built-in LPC interrupt deserializer, we need + * to provide a set of qirqs for the ISA bus or things will go bad. + * + * Most machines using pre-Naples chips (without said deserializer) + * have a CPLD that will collect the SerIRQ and shoot them as a + * single level interrupt to the P8 chip. So let's setup a hook + * for doing just that. + * + * Note: The actual interrupt input isn't emulated yet, this will + * come with the PSI bridge model. + */ + if (!has_lpc_irq) { + isa_bus_irqs(chip->lpc_bus, + qemu_allocate_irqs(pnv_lpc_irq_handler_cpld, NULL, 16)); + } + } } static void ppc_powernv_init(MachineState *machine) @@ -517,6 +549,7 @@ static void ppc_powernv_init(MachineState *machine) sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine); PnvSystem *sys = &pnv_machine->sys; XICSState *xics; + ISABus *isa_bus; long fw_size; char *filename; void *fdt; @@ -561,10 +594,18 @@ static void ppc_powernv_init(MachineState *machine) */ 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) + /* Create only one chip for now with an LPC bus */ - pnv_create_chip(sys, 0); + pnv_create_chip(sys, 0, true, false); + + /* Grab chip 0's ISA bus */ + isa_bus = sys->chips[0].lpc_bus; + + /* Create serial port */ + serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); + + /* Create an RTC ISA device too */ + rtc_init(isa_bus, 2000, NULL); if (bios_name == NULL) { bios_name = FW_FILE_NAME; diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c new file mode 100644 index 0000000000000..ea2241e97a30d --- /dev/null +++ b/hw/ppc/pnv_lpc.c @@ -0,0 +1,527 @@ + +/* + * QEMU PowerNV LPC bus definitions + * + * Copyright (c) 2010 David Gibson, IBM Corporation + * Based on the s390 virtio bus code: + * Copyright (c) 2009 Alexander Graf + * + * 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 . + */ + +#include "qemu/osdep.h" +#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/isa/isa.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv.h" + +#include + +enum { + ECCB_CTL = 0, + ECCB_RESET = 1, + ECCB_STAT = 2, + ECCB_DATA = 3, +}; + +#define LPCDBG(fmt...) do { } while(0) +//#define LPCDBG(fmt...) do { printf(fmt); } while(0) +#define OPBDBG(fmt...) do { } while(0) +//#define OPBDBG(fmt...) do { printf(fmt); } while(0) + +typedef struct PnvLpcController { + XScomDevice xd; + uint64_t eccb_stat_reg; + uint32_t eccb_data_reg; + bool has_serirq; + + /* OPB bus */ + MemoryRegion opb_mr; + AddressSpace opb_as; + /* ISA IO and Memory space */ + MemoryRegion isa_io; + MemoryRegion isa_mem; + ISABus *isa_bus; + /* Windows from OPB to ISA (aliases) */ + MemoryRegion opb_isa_io; + MemoryRegion opb_isa_mem; + MemoryRegion opb_isa_fw; + /* Registers */ + MemoryRegion lpc_hc_regs; + MemoryRegion opb_master_regs; + + /* OPB Master LS registers */ +#define OPB_MASTER_LS_IRQ_STAT 0x50 +#define OPB_MASTER_IRQ_LPC 0x00000800 + uint32_t opb_irq_stat; +#define OPB_MASTER_LS_IRQ_MASK 0x54 + uint32_t opb_irq_mask; +#define OPB_MASTER_LS_IRQ_POL 0x58 + uint32_t opb_irq_pol; + + /* LPC HC registers */ +#define LPC_HC_FW_SEG_IDSEL 0x24 + uint32_t lpc_hc_fw_seg_idsel; +#define LPC_HC_FW_RD_ACC_SIZE 0x28 +#define LPC_HC_FW_RD_1B 0x00000000 +#define LPC_HC_FW_RD_2B 0x01000000 +#define LPC_HC_FW_RD_4B 0x02000000 +#define LPC_HC_FW_RD_16B 0x04000000 +#define LPC_HC_FW_RD_128B 0x07000000 + uint32_t lpc_hc_fw_rd_acc_size; +#define LPC_HC_IRQSER_CTRL 0x30 +#define LPC_HC_IRQSER_EN 0x80000000 +#define LPC_HC_IRQSER_QMODE 0x40000000 +#define LPC_HC_IRQSER_START_MASK 0x03000000 +#define LPC_HC_IRQSER_START_4CLK 0x00000000 +#define LPC_HC_IRQSER_START_6CLK 0x01000000 +#define LPC_HC_IRQSER_START_8CLK 0x02000000 + uint32_t lpc_hc_irqser_ctrl; +#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ + uint32_t lpc_hc_irqmask; +#define LPC_HC_IRQSTAT 0x38 +#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ +#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ +#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 +#define LPC_HC_IRQ_LRESET 0x00000400 +#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 +#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 +#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 +#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 +#define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 +#define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 +#define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 +#define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 + uint32_t lpc_hc_irqstat; +#define LPC_HC_ERROR_ADDRESS 0x40 + uint32_t lpc_hc_error_addr; + +} PnvLpcController; + +#define ISA_IO_SIZE 0x00010000 +#define ISA_MEM_SIZE 0x10000000 +#define LPC_IO_OPB_ADDR 0xd0010000 +#define LPC_IO_OPB_SIZE 0x00010000 +#define LPC_MEM_OPB_ADDR 0xe0010000 +#define LPC_MEM_OPB_SIZE 0x10000000 +#define LPC_FW_OPB_ADDR 0xf0000000 +#define LPC_FW_OPB_SIZE 0x10000000 + +#define LPC_OPB_REGS_OPB_ADDR 0xc0010000 +#define LPC_OPB_REGS_OPB_SIZE 0x00002000 +#define LPC_HC_REGS_OPB_ADDR 0xc0012000 +#define LPC_HC_REGS_OPB_SIZE 0x00001000 + +#define TYPE_PNV_LPC_CONTROLLER "pnv-lpc" +#define PNV_LPC_CONTROLLER(obj) \ + OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC_CONTROLLER) + +#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) + +static int pnv_lpc_devnode(XScomDevice *dev, void *fdt) +{ + _FDT((fdt_property_cell(fdt, "#address-cells", 2))); + _FDT((fdt_property_cell(fdt, "#size-cells", 1))); + _FDT((fdt_property(fdt, "primary", NULL, 0))); + return 0; +} + +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) +{ + bool success; + + /* XXX Handle access size limits and FW read caching here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, false); + + LPCDBG("OPB read @0x%08x, sz=%d data=%02x %02x %02x %02x ok=%d\n", + addr, sz, data[0], data[1], data[2], data[3], success); + + return success; +} + +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) +{ + bool success; + + /* XXX Handle access size limits here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, true); + + LPCDBG("OPB write @0x%08x, sz=%d data=%02x %02x %02x %02x ok=%d\n", + addr, sz, data[0], data[1], data[2], data[3], success); + + return success; +} + +#define ECCB_CTL_READ (1ull << (63-15)) +#define ECCB_CTL_SZ_LSH (63-7) +#define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH) +#define ECCB_CTL_ADDR_MASK 0xffffffffu; + +#define ECCB_STAT_OP_DONE (1ull << (63-52)) +#define ECCB_STAT_OP_ERR (1ull << (63-52)) +#define ECCB_STAT_RD_DATA_LSH (63-37) +#define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) + +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) +{ + /* XXX Check for magic bits at the top, addr size etc... */ + unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; + uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; + uint8_t data[4]; + bool success; + + LPCDBG("ECCB cmd: %016llx data: %08x\n", + (unsigned long long)cmd, lpc->eccb_data_reg); + + if (cmd & ECCB_CTL_READ) { + success = opb_read(lpc, opb_addr, data, sz); + if (success) { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (((uint64_t)data[0]) << 24 | + ((uint64_t)data[1]) << 16 | + ((uint64_t)data[2]) << 8 | + ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; + } else { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (0xffffffffull << ECCB_STAT_RD_DATA_LSH); + } + } else { + data[0] = lpc->eccb_data_reg >> 24; + data[1] = lpc->eccb_data_reg >> 16; + data[2] = lpc->eccb_data_reg >> 8; + data[3] = lpc->eccb_data_reg; + + LPCDBG("OPB write @0x%08x, sz=%d data=%02x %02x %02x %02x\n", + opb_addr, sz, data[0], data[1], data[2], data[3]); + + success = opb_write(lpc, opb_addr, data, sz); + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; + } + /* XXX Which error bit (if any) to signal OPB error ? */ +} + +static bool pnv_lpc_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvLpcController *lpc = PNV_LPC_CONTROLLER(dev); + + switch(offset & 3) { + case ECCB_CTL: + case ECCB_RESET: + *out_val = 0; + break; + case ECCB_STAT: + *out_val = lpc->eccb_stat_reg; + lpc->eccb_stat_reg = 0; + break; + case ECCB_DATA: + *out_val = ((uint64_t)lpc->eccb_data_reg) << 32; + break; + } + return true; +} + +static bool pnv_lpc_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvLpcController *lpc = PNV_LPC_CONTROLLER(dev); + + switch(offset & 3) { + case ECCB_CTL: + pnv_lpc_do_eccb(lpc, val); + break; + case ECCB_RESET: + /* XXXX */ + break; + case ECCB_STAT: + break; + case ECCB_DATA: + lpc->eccb_data_reg = val >> 32; + break; + } + return true; +} + +static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) +{ + /* XXX TODO */ +} + +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "lpc_hc_read: Invalid size %d\n", size); + return 0xfffffffffffffffful; + } + + OPBDBG("LPC HC read @0x%08x\n", (unsigned int)addr); + + switch(addr) { + case LPC_HC_FW_SEG_IDSEL: + return lpc->lpc_hc_fw_seg_idsel; + case LPC_HC_FW_RD_ACC_SIZE: + return lpc->lpc_hc_fw_rd_acc_size; + case LPC_HC_IRQSER_CTRL: + return lpc->lpc_hc_irqser_ctrl; + case LPC_HC_IRQMASK: + return lpc->lpc_hc_irqmask; + case LPC_HC_IRQSTAT: + return lpc->lpc_hc_irqstat; + case LPC_HC_ERROR_ADDRESS: + return lpc->lpc_hc_error_addr; + default: + OPBDBG("LPC HC Unimplemented register !\n"); + return 0xfffffffffffffffful; + } +} + +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "lpc_hc_write: Invalid size %d\n", size); + return; + } + + OPBDBG("LPC HC write @0x%08x\n", (unsigned int)addr); + + /* XXX Filter out reserved bits */ + + switch(addr) { + case LPC_HC_FW_SEG_IDSEL: + /* XXX Actually figure out how that works as this impact + * memory regions/aliases\ + */ + lpc->lpc_hc_fw_seg_idsel = val; + case LPC_HC_FW_RD_ACC_SIZE: + lpc->lpc_hc_fw_rd_acc_size = val; + case LPC_HC_IRQSER_CTRL: + lpc->lpc_hc_irqser_ctrl = val; + case LPC_HC_IRQMASK: + lpc->lpc_hc_irqmask = val; + case LPC_HC_IRQSTAT: + lpc->lpc_hc_irqstat &= ~val; + case LPC_HC_ERROR_ADDRESS: + break; + default: + OPBDBG("LPC HC Unimplemented register !\n"); + } +} + +static const MemoryRegionOps lpc_hc_ops = { + .read = lpc_hc_read, + .write = lpc_hc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "opb_master_read: Invalid size %d\n", size); + return 0xfffffffffffffffful; + } + + OPBDBG("OPB MASTER read @0x%08x\n", (unsigned int)addr); + + switch(addr) { + case OPB_MASTER_LS_IRQ_STAT: + return lpc->opb_irq_stat; + case OPB_MASTER_LS_IRQ_MASK: + return lpc->opb_irq_mask; + case OPB_MASTER_LS_IRQ_POL: + return lpc->opb_irq_pol; + default: + OPBDBG("OPB MASTER Unimplemented register !\n"); + return 0xfffffffffffffffful; + } +} + +static void opb_master_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = opaque; + + if (size != 4) { + fprintf(stderr, "opb_master_write: Invalid size %d\n", size); + return; + } + + OPBDBG("OPB MASTER write @0x%08x\n", (unsigned int)addr); + + switch(addr) { + case OPB_MASTER_LS_IRQ_STAT: + lpc->opb_irq_stat &= ~val; + break; + case OPB_MASTER_LS_IRQ_MASK: + /* XXX Filter out reserved bits */ + lpc->opb_irq_mask = val; + break; + case OPB_MASTER_LS_IRQ_POL: + /* XXX Filter out reserved bits */ + lpc->opb_irq_pol = val; + break; + default: + OPBDBG("OPB MASTER Unimplemented register !\n"); + } +} + +static const MemoryRegionOps opb_master_ops = { + .read = opb_master_read, + .write = opb_master_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void pnv_lpc_realize(DeviceState *dev, Error **errp) +{ + PnvLpcController *lpc = PNV_LPC_CONTROLLER(dev); + + /* LPC XSCOM address is fixed */ + lpc->xd.ranges[0].addr = 0xb0020; + lpc->xd.ranges[0].size = 4; + + /* Reg inits */ + lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; + + /* Create address space and backing MR for the OPB bus */ + memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); + address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); + + /* Create ISA IO and Mem space regions which are the root of + * the ISA bus (ie, ISA address spaces). We don't create a + * separate one for FW which we alias to memory. + */ + memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); + memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); + + /* Create windows from the OPB space to the ISA space */ + memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", + &lpc->isa_io, 0, LPC_IO_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, + &lpc->opb_isa_io); + memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem", + &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, + &lpc->opb_isa_mem); + memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", + &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, + &lpc->opb_isa_fw); + + + /* Create MMIO regions for LPC HC and OPB registers */ + memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, + lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, + &lpc->opb_master_regs); + memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, + "lpc-hc", LPC_HC_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, + &lpc->lpc_hc_regs); + + /* Instanciate ISA bus */ + lpc->isa_bus = isa_bus_new(dev, &lpc->isa_mem, &lpc->isa_io, errp); + + /* Not all variants have a working serial irq decoder. If not, + * handling of LPC interrupts becomes a platform issue (some + * platforms have a CPLD to do it). + */ + if (lpc->has_serirq) { + isa_bus_irqs(lpc->isa_bus, + qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, 16)); + } +} + +void pnv_lpc_create(PnvChip *chip, bool has_serirq) +{ + struct DeviceState *dev; + PnvLpcController *lpc; + + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_LPC_CONTROLLER); + lpc = PNV_LPC_CONTROLLER(dev); + lpc->has_serirq = has_serirq; + qdev_init_nofail(dev); + chip->lpc = lpc; + chip->lpc_bus = lpc->isa_bus; +} + +static void pnv_lpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + static const char *compat[] = { "ibm,power8-lpc", NULL }; + + k->devnode = pnv_lpc_devnode; + k->read = pnv_lpc_xscom_read; + k->write = pnv_lpc_xscom_write; + k->dt_name = "isa"; + k->dt_compatible = compat; + + dc->realize = pnv_lpc_realize; +} + +static const TypeInfo pnv_lpc_info = { + .name = TYPE_PNV_LPC_CONTROLLER, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvLpcController), + .class_init = pnv_lpc_class_init, +}; + +static void pnv_lpc_register_types(void) +{ + type_register_static(&pnv_lpc_info); +} + +type_init(pnv_lpc_register_types) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 90b76ecf3b2c5..77b809a8dc233 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -21,12 +21,16 @@ #include "hw/hw.h" typedef struct XScomBus XScomBus; +typedef struct ISABus ISABus; +typedef struct PnvLpcController PnvLpcController; typedef struct XICSState XICSState; /* Should we turn that into a QOjb of some sort ? */ typedef struct PnvChip { uint32_t chip_id; XScomBus *xscom; + PnvLpcController *lpc; + ISABus *lpc_bus; } PnvChip; typedef struct PnvSystem { @@ -36,4 +40,6 @@ typedef struct PnvSystem { PnvChip chips[PNV_MAX_CHIPS]; } PnvSystem; +extern void pnv_lpc_create(PnvChip *chip, bool has_serirq); #endif /* _HW_PNV_LPC_H */ + From 8fd4dd2df6eaf33d3bac1fa44f71e74cb9e00b8a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 39/81] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt This adds just enough of the PSI bridge to handle various on-chip and the one external interrupt. The rest of PSI has to do with the link to the IBM FSP service processor which we don't plan to emulate (not used on OpenPower machines). Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/Makefile.objs | 2 +- hw/ppc/pnv.c | 28 +- hw/ppc/pnv_psi.c | 595 +++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 16 ++ 4 files changed, 633 insertions(+), 8 deletions(-) create mode 100644 hw/ppc/pnv_psi.c diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 5ebf0e0cd3d44..a5b3ce63afd87 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -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 pnv_xscom.o pnv_lpc.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_lpc.o pnv_psi.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 293318630beee..ded12709d176e 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -492,9 +492,22 @@ static const VMStateDescription vmstate_powernv = { static void pnv_lpc_irq_handler_cpld(void *opaque, int n, int level) { - /* We don't yet emulate the PSI bridge which provides the external - * interrupt, so just drop interrupts on the floor - */ +#define MAX_ISA_IRQ 16 + static uint32_t irqstate; + uint32_t old_state = irqstate; + PnvPsiController *psi = opaque; + + if (n >= MAX_ISA_IRQ) { + return; + } + if (level) { + irqstate |= 1u << n; + } else { + irqstate &= ~(1u << n); + } + if (irqstate != old_state) { + pnv_psi_irq_set(psi, PSIHB_IRQ_EXTERNAL, irqstate != 0); + } } static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, @@ -512,6 +525,9 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, /* Set up XSCOM bus */ xscom_create(chip); + /* Create PSI */ + pnv_psi_create(chip, sys->xics); + /* Create LPC controller */ if (has_lpc) { pnv_lpc_create(chip, has_lpc_irq); @@ -523,13 +539,11 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, * have a CPLD that will collect the SerIRQ and shoot them as a * single level interrupt to the P8 chip. So let's setup a hook * for doing just that. - * - * Note: The actual interrupt input isn't emulated yet, this will - * come with the PSI bridge model. */ if (!has_lpc_irq) { isa_bus_irqs(chip->lpc_bus, - qemu_allocate_irqs(pnv_lpc_irq_handler_cpld, NULL, 16)); + qemu_allocate_irqs(pnv_lpc_irq_handler_cpld, + chip->psi, 16)); } } } diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c new file mode 100644 index 0000000000000..f656ee2a0f2b2 --- /dev/null +++ b/hw/ppc/pnv_psi.c @@ -0,0 +1,595 @@ + +/* + * QEMU PowerNV Limited PSI interface + * + * Copyright 2015 IBM Corporation + * + * 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 . + */ + +#include "qemu/osdep.h" +#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 "exec/address-spaces.h" +#include "qapi/error.h" + +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/xics.h" +#include "hw/ppc/pnv.h" + +#include + +//#define PSIDBG(fmt...) printf("PSI "fmt) +#define PSIDBG(fmt...) do { } while(0) + +#define PSIHB_BAR_SIZE 0x100000ull + +#define PSIHB_XSCOM_FIR_RW 0x00 +#define PSIHB_XSCOM_FIR_AND 0x01 +#define PSIHB_XSCOM_FIR_OR 0x02 +#define PSIHB_XSCOM_FIRMASK_RW 0x03 +#define PSIHB_XSCOM_FIRMASK_AND 0x04 +#define PSIHB_XSCOM_FIRMASK_OR 0x05 +#define PSIHB_XSCOM_FIRACT0 0x06 +#define PSIHB_XSCOM_FIRACT1 0x07 +#define PSIHB_XSCOM_BAR 0x0a +#define PSIHB_BAR_EN 0x0000000000000001ull +#define PSIHB_XSCOM_FSPBAR 0x0b +#define PSIHB_XSCOM_CR 0x0e +#define PSIHB_CR_FSP_CMD_ENABLE 0x8000000000000000ull +#define PSIHB_CR_FSP_MMIO_ENABLE 0x4000000000000000ull +#define PSIHB_CR_FSP_IRQ_ENABLE 0x1000000000000000ull +#define PSIHB_CR_FSP_ERR_RSP_ENABLE 0x0800000000000000ull +#define PSIHB_CR_PSI_LINK_ENABLE 0x0400000000000000ull +#define PSIHB_CR_FSP_RESET 0x0200000000000000ull +#define PSIHB_CR_PSIHB_RESET 0x0100000000000000ull +#define PSIHB_CR_PSI_IRQ 0x0000800000000000ull +#define PSIHB_CR_FSP_IRQ 0x0000400000000000ull +#define PSIHB_CR_FSP_LINK_ACTIVE 0x0000200000000000ull + /* and more ... */ +#define PSIHB_XSCOM_SEMR 0x0f +#define PSIHB_XSCOM_XIVR_PSI 0x10 +#define PSIHB_XIVR_SERVER_SH 40 +#define PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH) +#define PSIHB_XIVR_PRIO_SH 32 +#define PSIHB_XIVR_PRIO_MSK (0xffull << PSIHB_XIVR_PRIO_SH) +#define PSIHB_XIVR_SRC_SH 29 +#define PSIHB_XIVR_SRC_MSK (0x7ull << PSIHB_XIVR_SRC_SH) +#define PSIHB_XIVR_PENDING 0x01000000ull +#define PSIHB_XSCOM_SCR 0x12 +#define PSIHB_XSCOM_CCR 0x13 +#define PSIHB_XSCOM_DMA_UPADD 0x14 +#define PSIHB_XSCOM_IRQ_STAT 0x15 +#define PSIHB_IRQ_STAT_OCC 0x0000001000000000ull +#define PSIHB_IRQ_STAT_FSI 0x0000000800000000ull +#define PSIHB_IRQ_STAT_LPCI2C 0x0000000400000000ull +#define PSIHB_IRQ_STAT_LOCERR 0x0000000200000000ull +#define PSIHB_IRQ_STAT_EXT 0x0000000100000000ull +#define PSIHB_XSCOM_XIVR_OCC 0x16 +#define PSIHB_XSCOM_XIVR_FSI 0x17 +#define PSIHB_XSCOM_XIVR_LPCI2C 0x18 +#define PSIHB_XSCOM_XIVR_LOCERR 0x19 +#define PSIHB_XSCOM_XIVR_EXT 0x1a +#define PSIHB_XSCOM_IRSN 0x1b +#define PSIHB_IRSN_COMP_SH 45 +#define PSIHB_IRSN_COMP_MSK (0x7ffffull << PSIHB_IRSN_COMP_SH) +#define PSIHB_IRSN_IRQ_MUX 0x0000000800000000ull +#define PSIHB_IRSN_IRQ_RESET 0x0000000400000000ull +#define PSIHB_IRSN_DOWNSTREAM_EN 0x0000000200000000ull +#define PSIHB_IRSN_UPSTREAM_EN 0x0000000100000000ull +#define PSIHB_IRSN_COMPMASK_SH 13 +#define PSIHB_IRSN_COMPMASK_MSK (0x7ffffull << PSIHB_IRSN_COMPMASK_SH) +#define PSIHB_XSCOM_MAX 0x20 + +#define PSIHB_MMIO_BAR 0x00 +#define PSIHB_MMIO_FSPBAR 0x08 +#define PSIHB_MMIO_CR 0x20 +#define PSIHB_MMIO_SEMR 0x28 +#define PSIHB_MMIO_XIVR_PSI 0x30 +#define PSIHB_MMIO_SCR 0x40 +#define PSIHB_MMIO_CCR 0x48 +#define PSIHB_MMIO_DMA_UPADD 0x50 +#define PSIHB_MMIO_IRQ_STAT 0x58 +#define PSIHB_MMIO_XIVR_OCC 0x60 +#define PSIHB_MMIO_XIVR_FSI 0x68 +#define PSIHB_MMIO_XIVR_LPCI2C 0x70 +#define PSIHB_MMIO_XIVR_LOCERR 0x78 +#define PSIHB_MMIO_XIVR_EXT 0x80 +#define PSIHB_MMIO_IRSN 0x88 +#define PSIHB_MMIO_MAX 0x100 + +struct PnvPsiController { + XScomDevice xd; + MemoryRegion regs_mr; + + /* FSP region not supported */ + /* MemoryRegion fsp_mr; */ + + /* Interrupt generation */ + XICSState *xics; + ICSState *ics; + + /* Registers */ + uint64_t regs[PSIHB_XSCOM_MAX]; +}; + +#define TYPE_PNV_PSI_CONTROLLER "pnv-psi" +#define PNV_PSI_CONTROLLER(obj) \ + OBJECT_CHECK(PnvPsiController, (obj), TYPE_PNV_PSI_CONTROLLER) + +static const uint32_t psi_mmio_to_xscom[PSIHB_MMIO_MAX/8] = { + [PSIHB_MMIO_BAR/8] = PSIHB_XSCOM_BAR, + [PSIHB_MMIO_FSPBAR/8] = PSIHB_XSCOM_FSPBAR, + [PSIHB_MMIO_CR/8] = PSIHB_XSCOM_CR, + [PSIHB_MMIO_SCR/8] = PSIHB_XSCOM_SCR, + [PSIHB_MMIO_CCR/8] = PSIHB_XSCOM_CCR, + [PSIHB_MMIO_SEMR/8] = PSIHB_XSCOM_SEMR, + [PSIHB_MMIO_XIVR_PSI/8] = PSIHB_XSCOM_XIVR_PSI, + [PSIHB_MMIO_XIVR_OCC/8] = PSIHB_XSCOM_XIVR_OCC, + [PSIHB_MMIO_XIVR_FSI/8] = PSIHB_XSCOM_XIVR_FSI, + [PSIHB_MMIO_XIVR_LPCI2C/8] = PSIHB_XSCOM_XIVR_LPCI2C, + [PSIHB_MMIO_XIVR_LOCERR/8] = PSIHB_XSCOM_XIVR_LOCERR, + [PSIHB_MMIO_XIVR_EXT/8] = PSIHB_XSCOM_XIVR_EXT, + [PSIHB_MMIO_IRQ_STAT/8] = PSIHB_XSCOM_IRQ_STAT, + [PSIHB_MMIO_DMA_UPADD/8] = PSIHB_XSCOM_DMA_UPADD, + [PSIHB_MMIO_IRSN/8] = PSIHB_XSCOM_IRSN, +}; + +static void pnv_psi_set_bar(PnvPsiController *psi, uint64_t bar) +{ + MemoryRegion *sysmem = get_system_memory(); + uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; + + psi->regs[PSIHB_XSCOM_BAR] = bar & 0x0003fffffff00001; + + /* Update MR, always remove it first */ + if (old & PSIHB_BAR_EN) { + memory_region_del_subregion(sysmem, &psi->regs_mr); + } + /* Then add it back if needed */ + if (bar & PSIHB_BAR_EN) { + uint64_t addr = bar & 0x0003fffffff00000; + memory_region_add_subregion(sysmem, addr, &psi->regs_mr); + } +} + +static void pnv_psi_update_fsp_mr(PnvPsiController *psi) +{ + /* XXX Update FSP MR if/when we support FSP BAR */ +} + +static void pnv_psi_set_cr(PnvPsiController *psi, uint64_t cr) +{ + uint64_t old = psi->regs[PSIHB_XSCOM_CR]; + + psi->regs[PSIHB_XSCOM_CR] = cr & 0x0003ffff00000000; + + /* Check some bit changes */ + if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) { + pnv_psi_update_fsp_mr(psi); + } +} + +static void pnv_psi_set_irsn(PnvPsiController *psi, uint64_t val) +{ + uint32_t offset; + + /* In this model we ignore the up/down enable bits for now + * as SW doesn't use them (other than setting them at boot). + * We ignore IRQ_MUX, its meaning isn't clear and we don't use + * it and finally we ignore reset (XXX fix that ?) + */ + psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK | + PSIHB_IRSN_IRQ_MUX | + PSIHB_IRSN_DOWNSTREAM_EN | + PSIHB_IRSN_DOWNSTREAM_EN | + PSIHB_IRSN_DOWNSTREAM_EN); + + /* We ignore the compare mask as well, our ICS emulation is too + * simplistic to make any use if it, and we extract the offset + * from the compare value + */ + offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH; + psi->ics->offset = offset; + PSIDBG("Interrupt offset=0x%x\n", offset); +} + +static bool pnv_psi_irq_bits(PnvPsiController *psi, PnvPsiIrq irq, + uint32_t *out_xivr_reg, + uint32_t *out_stat_reg, + uint64_t *out_stat_bit) +{ + switch(irq) { + case PSIHB_IRQ_PSI: + *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI; + *out_stat_reg = PSIHB_XSCOM_CR; + *out_stat_bit = PSIHB_CR_PSI_IRQ; + break; + case PSIHB_IRQ_FSP: + *out_xivr_reg = PSIHB_XSCOM_XIVR_PSI; + *out_stat_reg = PSIHB_XSCOM_CR; + *out_stat_bit = PSIHB_CR_FSP_IRQ; + break; + case PSIHB_IRQ_OCC: + *out_xivr_reg = PSIHB_XSCOM_XIVR_OCC; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_OCC; + break; + case PSIHB_IRQ_FSI: + *out_xivr_reg = PSIHB_XSCOM_XIVR_FSI; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_FSI; + break; + case PSIHB_IRQ_LPC_I2C: + *out_xivr_reg = PSIHB_XSCOM_XIVR_LPCI2C; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_LPCI2C; + break; + case PSIHB_IRQ_LOCAL_ERR: + *out_xivr_reg = PSIHB_XSCOM_XIVR_LOCERR; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_LOCERR; + break; + case PSIHB_IRQ_EXTERNAL: + *out_xivr_reg = PSIHB_XSCOM_XIVR_EXT; + *out_stat_reg = PSIHB_XSCOM_IRQ_STAT; + *out_stat_bit = PSIHB_IRQ_STAT_EXT; + break; + default: + return false; + } + return true; +} + +void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state) +{ + uint32_t xivr_reg; + uint32_t stat_reg; + uint64_t stat_bit; + uint32_t src; + bool masked; + + if (!pnv_psi_irq_bits(psi, irq, &xivr_reg, &stat_reg, &stat_bit)) { + /* XXX Generate an error ? */ + fprintf(stderr, "PSI: Unsupported irq %d\n", irq); + return; + } + src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK; + if (state) { + psi->regs[stat_reg] |= stat_bit; + /* XXX optimization: check mask here. That means re-evaluating + * when unmasking, thus TODO + */ + qemu_irq_raise(psi->ics->qirqs[src]); + } else { + psi->regs[stat_reg] &= ~stat_bit; + + /* FSP and PSI are muxed so don't lower if either still set */ + if (stat_reg != PSIHB_XSCOM_CR || + !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { + qemu_irq_lower(psi->ics->qirqs[src]); + } else { + state = true; + } + } + + /* XXX Note about the emulation of the pending bit: This isn't + * entirely correct. The pending bit should be cleared when the + * EOI has been received. However, we don't have callbacks on + * EOI (especially not under KVM) so no way to emulate that + * properly, so instead we just set that bit as the logical + * "output" of the XIVR (ie pending & !masked) + * XXX TODO: Also update it on set_xivr + */ + if (state && !masked) { + psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING; + } else { + psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING; + } +} + +static void pnv_psi_set_xivr(PnvPsiController *psi, uint32_t reg, uint64_t val) +{ + uint16_t server; + uint8_t prio; + uint8_t src; + + psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) | + (val & (PSIHB_XIVR_SERVER_MSK | + PSIHB_XIVR_PRIO_MSK | + PSIHB_XIVR_SRC_MSK)); + val = psi->regs[reg]; + server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH; + prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH; + src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; + if (src > PSIHB_IRQ_EXTERNAL) { + /* XXX Generate error ? */ + return; + } + /* Now because of source remapping, weird things can happen + * if you change the source number dynamically, our simple ICS + * doesn't deal with remapping. So we just poke a different + * ICS entry based on what source number was written. This will + * do for now but a more accurate implementation would instead + * use a fixed server/prio and a remapper of the generated irq. + */ + PSIDBG("IRQ %d server 0x%x prio %x\n", src, server, prio); + ics_simple_write_xive(psi->ics, src, server, prio, prio); +} + +static bool pnv_psi_reg_read(PnvPsiController *psi, uint32_t offset, + uint64_t *out_val, bool mmio) +{ + switch(offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_BAR: + case PSIHB_XSCOM_FSPBAR: + case PSIHB_XSCOM_CR: + case PSIHB_XSCOM_XIVR_PSI: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + case PSIHB_XSCOM_IRQ_STAT: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + case PSIHB_XSCOM_IRSN: + *out_val = psi->regs[offset]; + return true; + } + return false; +} + +static bool pnv_psi_reg_write(PnvPsiController *psi, uint32_t offset, + uint64_t val, bool mmio) +{ + switch(offset) { + case PSIHB_XSCOM_FIR_RW: + case PSIHB_XSCOM_FIRACT0: + case PSIHB_XSCOM_FIRACT1: + case PSIHB_XSCOM_SEMR: + case PSIHB_XSCOM_DMA_UPADD: + psi->regs[offset] = val; + return true; + case PSIHB_XSCOM_FIR_OR: + psi->regs[PSIHB_XSCOM_FIR_RW] |= val; + return true; + case PSIHB_XSCOM_FIR_AND: + psi->regs[PSIHB_XSCOM_FIR_RW] &= val; + return true; + case PSIHB_XSCOM_BAR: + /* Only XSCOM can write this one */ + if (!mmio) { + pnv_psi_set_bar(psi, val); + } + return true; + case PSIHB_XSCOM_FSPBAR: + psi->regs[PSIHB_XSCOM_BAR] = val & 0x0003ffff00000000; + pnv_psi_update_fsp_mr(psi); + return true; + case PSIHB_XSCOM_CR: + pnv_psi_set_cr(psi, val); + return true; + case PSIHB_XSCOM_SCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val); + return true; + case PSIHB_XSCOM_CCR: + pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val); + return true; + case PSIHB_XSCOM_XIVR_PSI: + case PSIHB_XSCOM_XIVR_OCC: + case PSIHB_XSCOM_XIVR_FSI: + case PSIHB_XSCOM_XIVR_LPCI2C: + case PSIHB_XSCOM_XIVR_LOCERR: + case PSIHB_XSCOM_XIVR_EXT: + pnv_psi_set_xivr(psi, offset, val); + return true; + case PSIHB_XSCOM_IRQ_STAT: + /* Read only, should we generate an error ? */ + return true; + case PSIHB_XSCOM_IRSN: + pnv_psi_set_irsn(psi, val); + return true; + } + return false; +} + +static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPsiController *psi = opaque; + uint32_t xscom_off; + uint64_t val; + + if (size != 8) { + goto fail; + } + + addr &= (PSIHB_BAR_SIZE - 1); + + PSIDBG("MMIO read 0x%x\n", (unsigned int)addr); + + if (addr >= PSIHB_MMIO_MAX) { + goto fail; + } + xscom_off = psi_mmio_to_xscom[addr/8]; + if (xscom_off == 0) { + goto fail; + } + if (pnv_psi_reg_read(psi, xscom_off, &val, true)) { + return val; + } + fail: + return 0xffffffffffffffffull; +} + +static void pnv_psi_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPsiController *psi = opaque; + uint32_t xscom_off; + + if (size != 8) { + return; + } + + addr &= (PSIHB_BAR_SIZE - 1); + + PSIDBG("MMIO write 0x%x val 0x%016llx\n", + (unsigned int)addr, (unsigned long long)val); + + if (addr >= PSIHB_MMIO_MAX) { + return; + } + xscom_off = psi_mmio_to_xscom[addr/8]; + if (xscom_off == 0) { + return; + } + pnv_psi_reg_write(psi, xscom_off, val, true); +} + +static const MemoryRegionOps psi_mmio_ops = { + .read = pnv_psi_mmio_read, + .write = pnv_psi_mmio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static bool pnv_psi_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvPsiController *psi = PNV_PSI_CONTROLLER(dev); + + PSIDBG("XSCOM read 0x%x\n", offset); + + return pnv_psi_reg_read(psi, offset, out_val, false); +} + +static bool pnv_psi_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvPsiController *psi = PNV_PSI_CONTROLLER(dev); + + PSIDBG("XSCOM write 0x%x val 0x%016llx\n", + offset, (unsigned long long)val); + + return pnv_psi_reg_write(psi, offset, val, false); +} + +static void pnv_psi_realize(DeviceState *dev, Error **errp) +{ + PnvPsiController *psi = PNV_PSI_CONTROLLER(dev); + Error *error = NULL; + unsigned int i; + + /* PSI XSCOM address is fixed */ + psi->xd.ranges[0].addr = 0x02010900; + psi->xd.ranges[0].size = 0x20; + + /* Initialize MMIO region */ + memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, + "psihb", PSIHB_BAR_SIZE); + + /* Default BAR. Use object properties ? */ + pnv_psi_set_bar(psi, 0x0003fffe80000001); + + /* Default sources in XIVR */ + psi->regs[PSIHB_XSCOM_XIVR_PSI] = PSIHB_XIVR_PRIO_MSK | + (0ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_OCC] = PSIHB_XIVR_PRIO_MSK | + (1ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_FSI] = PSIHB_XIVR_PRIO_MSK | + (2ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_LPCI2C] = PSIHB_XIVR_PRIO_MSK | + (3ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_LOCERR] = PSIHB_XIVR_PRIO_MSK | + (4ull << PSIHB_XIVR_SRC_SH); + psi->regs[PSIHB_XSCOM_XIVR_EXT] = PSIHB_XIVR_PRIO_MSK | + (5ull << PSIHB_XIVR_SRC_SH); + + /* Create ICS object */ + psi->ics = ICS(object_new(TYPE_ICS_SIMPLE)); + object_property_add_child(OBJECT(psi), "ics", OBJECT(psi->ics), NULL); + psi->ics->offset = 0; +#define PSI_NUM_INTERRUPTS 6 + psi->ics->nr_irqs = PSI_NUM_INTERRUPTS; + xics_add_ics(psi->xics, psi->ics); + object_property_set_bool(OBJECT(psi->ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + for (i = 0; i < PSI_NUM_INTERRUPTS; i++) + ics_simple_set_irq_type(psi->ics, i, true); + +} + +void pnv_psi_create(PnvChip *chip, XICSState *xics) +{ + struct DeviceState *dev; + PnvPsiController *psi; + + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_PSI_CONTROLLER); + psi = PNV_PSI_CONTROLLER(dev); + psi->xics = xics; + qdev_init_nofail(dev); + chip->psi = psi; +} + +static void pnv_psi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + static const char *compat[] = { "ibm,power8-psihb-x", + "ibm,psihb-x", NULL }; + + k->read = pnv_psi_xscom_read; + k->write = pnv_psi_xscom_write; + k->dt_name = "psihb"; + k->dt_compatible = compat; + + dc->realize = pnv_psi_realize; +} + +static const TypeInfo pnv_psi_info = { + .name = TYPE_PNV_PSI_CONTROLLER, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvPsiController), + .class_init = pnv_psi_class_init, +}; + +static void pnv_psi_register_types(void) +{ + type_register_static(&pnv_psi_info); +} + +type_init(pnv_psi_register_types) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 77b809a8dc233..73bbef9b1553f 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -23,6 +23,7 @@ typedef struct XScomBus XScomBus; typedef struct ISABus ISABus; typedef struct PnvLpcController PnvLpcController; +typedef struct PnvPsiController PnvPsiController; typedef struct XICSState XICSState; /* Should we turn that into a QOjb of some sort ? */ @@ -31,6 +32,7 @@ typedef struct PnvChip { XScomBus *xscom; PnvLpcController *lpc; ISABus *lpc_bus; + PnvPsiController *psi; } PnvChip; typedef struct PnvSystem { @@ -41,5 +43,19 @@ typedef struct PnvSystem { } PnvSystem; extern void pnv_lpc_create(PnvChip *chip, bool has_serirq); +extern void pnv_psi_create(PnvChip *chip, XICSState *xics); + +typedef enum PnvPsiIrq { + PSIHB_IRQ_PSI, /* internal use only */ + PSIHB_IRQ_FSP, /* internal use only */ + PSIHB_IRQ_OCC, + PSIHB_IRQ_FSI, + PSIHB_IRQ_LPC_I2C, + PSIHB_IRQ_LOCAL_ERR, + PSIHB_IRQ_EXTERNAL, +} PnvPsiIrq; + +extern void pnv_psi_irq_set(PnvPsiController *psi, PnvPsiIrq irq, bool state); + #endif /* _HW_PNV_LPC_H */ From 6176dcaea4a3397546e0e064a716d60c9ec9a6d7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 40/81] ppc/pnv: Add OCC model stub with interrupt support The OCC is an on-chip microcontroller based on a ppc405 core used for various power management tasks. It comes with a pile of additional hardware sitting on the PIB (aka XSCOM bus). At this point we don't emulate it (nor plan to do so). However there is one facility which is provided by the surrounding hardware that we do need, which is the interrupt generation facility. OPAL uses it to send itself interrupts under some circumstances and there are other uses around the corner. So this implement just enough to support this. Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/Makefile.objs | 2 +- hw/ppc/pnv.c | 3 ++ hw/ppc/pnv_occ.c | 126 +++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv.h | 3 ++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 hw/ppc/pnv_occ.c diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index a5b3ce63afd87..a795b1cb03138 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -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 pnv_xscom.o pnv_lpc.o pnv_psi.o +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_lpc.o pnv_psi.o pnv_occ.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index ded12709d176e..56bc40447dbb5 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -546,6 +546,9 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, chip->psi, 16)); } } + + /* Create the simplified OCC model */ + pnv_occ_create(chip); } static void ppc_powernv_init(MachineState *machine) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c new file mode 100644 index 0000000000000..d3cbb5fc181bc --- /dev/null +++ b/hw/ppc/pnv_occ.c @@ -0,0 +1,126 @@ +/* + * Emulation of a few OCC related registers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" + +struct PnvOCCState { + XScomDevice xd; + PnvPsiController *psi; + + /* OCC Misc interrupt */ + uint64_t occmisc; +}; + +#define TYPE_PNV_OCC "pnv-occ" +#define PNV_OCC(obj) OBJECT_CHECK(PnvOCCState, (obj), TYPE_PNV_OCC) + +static void pnv_occ_set_misc(PnvOCCState *occ, uint64_t val) +{ + bool irq_state; + + val &= 0xffff000000000000ull; + + occ->occmisc = val; + irq_state = !!(val >> 63); + pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state); +} + +static bool pnv_occ_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvOCCState *occ = PNV_OCC(dev); + uint32_t pcb_addr = dev->ranges[range].addr + offset; + + switch(pcb_addr) { + case 0x6a020: + *out_val = occ->occmisc; + return true; + } + return false; +} + +static bool pnv_occ_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvOCCState *occ = PNV_OCC(dev); + uint32_t pcb_addr = dev->ranges[range].addr + offset; + + switch(pcb_addr) { + default: + case 0x6a020: + pnv_occ_set_misc(occ, val); + return true; + case 0x6a021: + pnv_occ_set_misc(occ, occ->occmisc & val); + return true; + case 0x6a022: + pnv_occ_set_misc(occ, occ->occmisc | val); + return true; + } + return false; +} + +static void pnv_occ_realize(DeviceState *dev, Error **errp) +{ + PnvOCCState *occ = PNV_OCC(dev); + XScomDevice *xd = XSCOM_DEVICE(dev); + + xd->ranges[0].addr = 0x66000; + xd->ranges[0].size = 0x6000; + + occ->occmisc = 0; +} + +static void pnv_occ_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + + k->read = pnv_occ_xscom_read; + k->write = pnv_occ_xscom_write; + + dc->realize = pnv_occ_realize; +} + +static const TypeInfo pnv_occ_type_info = { + .name = TYPE_PNV_OCC, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvOCCState), + .class_init = pnv_occ_class_init, +}; + +static void pnv_occ_register_types(void) +{ + type_register_static(&pnv_occ_type_info); +} + +type_init(pnv_occ_register_types) + +void pnv_occ_create(PnvChip *chip) +{ + struct DeviceState *dev; + PnvOCCState *occ; + + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_OCC); + occ = PNV_OCC(dev); + occ->psi = chip->psi; + qdev_init_nofail(dev); + chip->occ = occ; +} diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 73bbef9b1553f..c488f123619cc 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -25,6 +25,7 @@ typedef struct ISABus ISABus; typedef struct PnvLpcController PnvLpcController; typedef struct PnvPsiController PnvPsiController; typedef struct XICSState XICSState; +typedef struct PnvOCCState PnvOCCState; /* Should we turn that into a QOjb of some sort ? */ typedef struct PnvChip { @@ -33,6 +34,7 @@ typedef struct PnvChip { PnvLpcController *lpc; ISABus *lpc_bus; PnvPsiController *psi; + PnvOCCState *occ; } PnvChip; typedef struct PnvSystem { @@ -44,6 +46,7 @@ typedef struct PnvSystem { extern void pnv_lpc_create(PnvChip *chip, bool has_serirq); extern void pnv_psi_create(PnvChip *chip, XICSState *xics); +extern void pnv_occ_create(PnvChip *chip); typedef enum PnvPsiIrq { PSIHB_IRQ_PSI, /* internal use only */ From be8c7cf1f6ca1a3f3bc9a3eaf819a73b096c873b Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 41/81] pci-bridge: Set a supported devfn_min for bridge Otherwise QEMU might add a device at slot 0 which isn't supported by the SHPC controller. Signed-off-by: Benjamin Herrenschmidt --- hw/pci-bridge/pci_bridge_dev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 7b582e96aca1d..9a11559f57ea7 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -57,6 +57,8 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) pci_bridge_initfn(dev, TYPE_PCI_BUS); if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) { + /* SHCP gets upset if we try to use slot 0 */ + br->sec_bus.devfn_min = PCI_FUNC_MAX; dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); From cc1f22fd1f032c3722f4e38688a4d586fcfc1e58 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 42/81] qdev: Add a hook for a bus to device if it can add devices This allows a bus class to tell whether a given bus has room for any new device. max_dev isn't sufficient as the rules can depend on some arguments or can differ between instances of a bus. This will be used by PCI in subsequent patches Signed-off-by: Benjamin Herrenschmidt --- include/hw/qdev-core.h | 1 + qdev-monitor.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 1ce02b20daf0e..c08d67f9069e8 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -200,6 +200,7 @@ struct BusClass { */ char *(*get_fw_dev_path)(DeviceState *dev); void (*reset)(BusState *bus); + bool (*can_add_device)(BusState *bus, QemuOpts *opts); BusRealize realize; BusUnrealize unrealize; diff --git a/qdev-monitor.c b/qdev-monitor.c index e19617fa8b33c..119eb78500aee 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -413,7 +413,7 @@ static inline bool qbus_is_full(BusState *bus) * Return the bus if found, else %NULL. */ static BusState *qbus_find_recursive(BusState *bus, const char *name, - const char *bus_typename) + const char *bus_typename, QemuOpts *opts) { BusChild *kid; BusState *pick, *child, *ret; @@ -427,7 +427,10 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, } if (match && !qbus_is_full(bus)) { - return bus; /* root matches and isn't full */ + BusClass *bc = BUS_GET_CLASS(bus); + if (!bc->can_add_device || bc->can_add_device(bus, opts)) { + return bus; /* root matches and isn't full */ + } } pick = match ? bus : NULL; @@ -435,7 +438,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, QTAILQ_FOREACH(kid, &bus->children, sibling) { DeviceState *dev = kid->child; QLIST_FOREACH(child, &dev->child_bus, sibling) { - ret = qbus_find_recursive(child, name, bus_typename); + ret = qbus_find_recursive(child, name, bus_typename, opts); if (ret && !qbus_is_full(ret)) { return ret; /* a descendant matches and isn't full */ } @@ -465,7 +468,7 @@ static BusState *qbus_find(const char *path, Error **errp) assert(!path[0]); elem[0] = len = 0; } - bus = qbus_find_recursive(sysbus_get_default(), elem, NULL); + bus = qbus_find_recursive(sysbus_get_default(), elem, NULL, NULL); if (!bus) { error_setg(errp, "Bus '%s' not found", elem); return NULL; @@ -571,7 +574,7 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp) return NULL; } } else if (dc->bus_type != NULL) { - bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); + bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type, opts); if (!bus || qbus_is_full(bus)) { error_setg(errp, "No '%s' bus found for device '%s'", dc->bus_type, driver); From 16e0db98bc548616fc0c59e30ad0298d0bab57b6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:52 +0200 Subject: [PATCH 43/81] pci: Use the new pci_can_add_device() to enforce devfn_min/max This adds a devfn_max field to PCIBus and adds a pci_can_add_device() function which, if no "addr" (aka devfn) is specified, will tell whether there is any slot free between devfn_min and devfn_max. When devfn_max is 0 (default), it will be ignored (no constraints). This will be used by some PCI root complex implementations that support only one direct child to avoid having qemu put dumb devices at different slot numbers. Signed-off-by: Benjamin Herrenschmidt --- hw/pci/pci.c | 22 ++++++++++++++++++++++ include/hw/pci/pci_bus.h | 1 + 2 files changed, 23 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index bb605efae0236..1194818197b9a 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -110,6 +110,27 @@ static uint16_t pcibus_numa_node(PCIBus *bus) return NUMA_NODE_UNASSIGNED; } +static bool pci_can_add_device(BusState *bus, QemuOpts *opts) +{ + unsigned int devfn, max; + PCIBus *pbus = PCI_BUS(bus); + + /* If address is specified, say yes and let it fail if that doesn't work */ + if (qemu_opt_get(opts, "addr") != NULL) { + return true; + } + max = ARRAY_SIZE(pbus->devices); + if (pbus->devfn_max && pbus->devfn_max < max) { + max = pbus->devfn_max; + } + for (devfn = pbus->devfn_min ; devfn < max; devfn += PCI_FUNC_MAX) { + if (!pbus->devices[devfn]) { + break; + } + } + return devfn < max; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -121,6 +142,7 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) k->realize = pci_bus_realize; k->unrealize = pci_bus_unrealize; k->reset = pcibus_reset; + k->can_add_device = pci_can_add_device; pbc->is_root = pcibus_is_root; pbc->bus_num = pcibus_num; diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 403fec6e58c0e..02055d4716308 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -23,6 +23,7 @@ struct PCIBus { PCIIOMMUFunc iommu_fn; void *iommu_opaque; uint8_t devfn_min; + uint8_t devfn_max; pci_set_irq_fn set_irq; pci_map_irq_fn map_irq; pci_route_irq_fn route_intx_to_irq; From d2dcb6b3ae49982e15dbe0ec413118017c44c7e2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 44/81] pci: Don't call pci_irq_handler() for a negative intx Under some circumstances, pci_intx() can return -1 (when the interrupt pin in the config space is 0 which normally means no interrupt). I have seen cases of pci_set_irq() being called on such devices, in turn causing pci_irq_handler() to be called with "-1" as an argument which doesn't seem like a terribly good idea. Signed-off-by: Benjamin Herrenschmidt --- hw/pci/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1194818197b9a..8e7033798d534 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1304,7 +1304,9 @@ qemu_irq pci_allocate_irq(PCIDevice *pci_dev) void pci_set_irq(PCIDevice *pci_dev, int level) { int intx = pci_intx(pci_dev); - pci_irq_handler(pci_dev, intx, level); + if (intx >= 0) { + pci_irq_handler(pci_dev, intx, level); + } } /* Special hooks used by device assignment */ From 7d6c4dabfe9f9a246ffe359354c92be2ed73bc49 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 45/81] ppc/pnv: Add model for Power8 PHB3 PCIe Host bridge This is a model of the PCIe host bridge found on Power8 chips, including IOMMU support, PCIe root complex etc... This implementation doesn't emulate the EEH error handling (and may never do). Signed-off-by: Benjamin Herrenschmidt --- hw/intc/xics.c | 2 +- hw/pci-host/Makefile.objs | 2 + hw/pci-host/pnv_phb3.c | 1085 +++++++++++++++++++++++++++ hw/pci-host/pnv_phb3_msi.c | 354 +++++++++ hw/pci-host/pnv_phb3_pbcq.c | 315 ++++++++ hw/pci-host/pnv_phb3_rc.c | 130 ++++ hw/ppc/pnv.c | 22 +- include/hw/pci-host/pnv_phb3.h | 145 ++++ include/hw/pci-host/pnv_phb3_regs.h | 505 +++++++++++++ include/hw/ppc/pnv.h | 3 + include/hw/ppc/xics.h | 1 + 11 files changed, 2560 insertions(+), 4 deletions(-) create mode 100644 hw/pci-host/pnv_phb3.c create mode 100644 hw/pci-host/pnv_phb3_msi.c create mode 100644 hw/pci-host/pnv_phb3_pbcq.c create mode 100644 hw/pci-host/pnv_phb3_rc.c create mode 100644 include/hw/pci-host/pnv_phb3.h create mode 100644 include/hw/pci-host/pnv_phb3_regs.h diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 7e1dcdf6c77b5..8130ba590a860 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -345,7 +345,7 @@ void icp_eoi(XICSState *xics, int server, uint32_t xirr) } } -static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) +void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) { XICSState *xics = ics->xics; ICPState *ss = xics->ss + server; diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs index 45f1f0ebabd64..961fe5b6e25e7 100644 --- a/hw/pci-host/Makefile.objs +++ b/hw/pci-host/Makefile.objs @@ -16,3 +16,5 @@ common-obj-$(CONFIG_FULONG) += bonito.o common-obj-$(CONFIG_PCI_PIIX) += piix.o common-obj-$(CONFIG_PCI_Q35) += q35.o common-obj-$(CONFIG_PCI_GENERIC) += gpex.o + +obj-$(CONFIG_POWERNV) += pnv_phb3.o pnv_phb3_pbcq.o pnv_phb3_rc.o pnv_phb3_msi.o diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c new file mode 100644 index 0000000000000..0dc31ec421f18 --- /dev/null +++ b/hw/pci-host/pnv_phb3.c @@ -0,0 +1,1085 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/ppc/xics.h" + +#define DBG_ERR(p, fmt, ...) do { \ + if (1) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +#define DBG_DMA(p, fmt, ...) do { \ + if (0) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +#define DBG_MAP(p, fmt, ...) do { \ + if (0) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +//#define DISPLAY_UNIMPLENTED_REG + +static PCIDevice *pnb_phb3_find_cfg_dev(PnvPhb3State *phb) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(phb); + uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3]; + uint8_t bus, devfn; + + if (!(addr >> 63)) { + return NULL; + } + bus = (addr >> 52) & 0xff; + devfn = (addr >> 44) & 0xff; + + return pci_find_device(pci->bus, bus, devfn); +} + +static void pnv_phb3_config_write(PnvPhb3State *phb, unsigned off, + unsigned size, uint64_t val) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + + pdev = pnb_phb3_find_cfg_dev(phb); + if (!pdev) { + return; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xfff; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return; + } + switch (size) { + case 1: + break; + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + default: + DBG_ERR(phb, "Unsupported config access size %d !", size); + return; + } + pci_host_config_write_common(pdev, cfg_addr, limit, val, size); +} + +static uint64_t pnv_phb3_config_read(PnvPhb3State *phb, unsigned off, + unsigned size) +{ + uint32_t cfg_addr, limit; + PCIDevice *pdev; + uint64_t val; + + pdev = pnb_phb3_find_cfg_dev(phb); + if (!pdev) { + return ~0ull; + } + cfg_addr = (phb->regs[PHB_CONFIG_ADDRESS >> 3] >> 32) & 0xffc; + cfg_addr |= off; + limit = pci_config_size(pdev); + if (limit <= cfg_addr) { + /* conventional pci device can be behind pcie-to-pci bridge. + 256 <= addr < 4K has no effects. */ + return ~0ull; + } + val = pci_host_config_read_common(pdev, cfg_addr, limit, size); + switch (size) { + case 1: + return val; + case 2: + return bswap16(val); + case 4: + return bswap32(val); + default: + DBG_ERR(phb, "Unsupported config access size %d !", size); + return ~0ull; + } +} + +static void pnv_phb3_check_m32(PnvPhb3State *phb) +{ + uint64_t base, start, size; + MemoryRegion *parent; + + if (phb->m32_mapped) { + // Should we destroy it in RCU friendly way... ? + memory_region_del_subregion(phb->mr_m32.container, &phb->mr_m32); + phb->m32_mapped = false; + } + + /* Disabled ? move on with life ... */ + if (!(phb->regs[PHB_PHB3_CONFIG >> 3] & PHB_PHB3C_M32_EN)) { + return; + } + + /* Grab geometry from registers */ + base = phb->regs[PHB_M32_BASE_ADDR >> 3]; + start = phb->regs[PHB_M32_START_ADDR >> 3]; + size = ~(phb->regs[PHB_M32_BASE_MASK >> 3] | 0xfffc000000000000ull) + 1; + + DBG_MAP(phb, "PHB3: M32 enabled, base=%016llx/%016llx", + (unsigned long long)base, (unsigned long long)size); + DBG_MAP(phb, "PHB3: MMIO0 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio0_base, + (unsigned long long)phb->pbcq->mmio0_size, phb->pbcq->mmio0_mapped); + DBG_MAP(phb, "PHB3: MMIO1 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio1_base, + (unsigned long long)phb->pbcq->mmio1_size, phb->pbcq->mmio1_mapped); + /* Check if it matches an enabled MMIO region in the PBCQ */ + if (phb->pbcq->mmio0_mapped && base >= phb->pbcq->mmio0_base && + (base + size) <= (phb->pbcq->mmio0_base + phb->pbcq->mmio0_size)) { + parent = &phb->pbcq->mmbar0; + base -= phb->pbcq->mmio0_base; + DBG_MAP(phb, "M32: Mapping under MMIO0"); + } else if (phb->pbcq->mmio1_mapped && base >= phb->pbcq->mmio1_base && + (base + size) <= (phb->pbcq->mmio1_base + phb->pbcq->mmio1_size)) { + parent = &phb->pbcq->mmbar1; + base -= phb->pbcq->mmio1_base; + DBG_MAP(phb, "M32: Mapping under MMIO1"); + } else { + DBG_MAP(phb, "M32 not matching either PBCQ MMIO region !"); + return; + } + + /* Create alias */ + memory_region_init_alias(&phb->mr_m32, OBJECT(phb), "phb3-m32", + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_m32); + phb->m32_mapped = true; +} + +static void pnv_phb3_check_m64(PnvPhb3State *phb, uint32_t index) +{ + uint64_t base, start, size, m64; + MemoryRegion *parent; + + if (phb->m64_mapped[index]) { + // Should we destroy it in RCU friendly way... ? + memory_region_del_subregion(phb->mr_m64[index].container, + &phb->mr_m64[index]); + phb->m64_mapped[index] = false; + } + + /* Get table entry */ + m64 = phb->ioda_M64BT[index]; + + /* Disabled ? move on with life ... */ + if (!(m64 & IODA2_M64BT_ENABLE)) { + return; + } + + /* Grab geometry from registers */ + base = GETFIELD(IODA2_M64BT_BASE, m64) << 20; + if (m64 & IODA2_M64BT_SINGLE_PE) { + base &= ~0x1ffffffull; + } + size = GETFIELD(IODA2_M64BT_MASK, m64) << 20; + size |= 0xfffc000000000000ull; + size = ~size + 1; + start = base | (phb->regs[PHB_M64_UPPER_BITS >> 3]); + + DBG_MAP(phb, "PHB3: M64[%d] enabled, base=%016llx/%016llx", index, + (unsigned long long)base, (unsigned long long)size); + DBG_MAP(phb, "PHB3: MMIO0 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio0_base, + (unsigned long long)phb->pbcq->mmio0_size, phb->pbcq->mmio0_mapped); + DBG_MAP(phb, "PHB3: MMIO1 =%016llx/%016llx [%d]", + (unsigned long long)phb->pbcq->mmio1_base, + (unsigned long long)phb->pbcq->mmio1_size, phb->pbcq->mmio1_mapped); + /* Check if it matches an enabled MMIO region in the PBCQ */ + if (phb->pbcq->mmio0_mapped && base >= phb->pbcq->mmio0_base && + (base + size) <= (phb->pbcq->mmio0_base + phb->pbcq->mmio0_size)) { + parent = &phb->pbcq->mmbar0; + base -= phb->pbcq->mmio0_base; + DBG_MAP(phb, "M64: Mapping under MMIO0"); + } else if (phb->pbcq->mmio1_mapped && base >= phb->pbcq->mmio1_base && + (base + size) <= (phb->pbcq->mmio1_base + phb->pbcq->mmio1_size)) { + parent = &phb->pbcq->mmbar1; + base -= phb->pbcq->mmio1_base; + DBG_MAP(phb, "M64: Mapping under MMIO1"); + } else { + DBG_MAP(phb, "M64 not matching either PBCQ MMIO region !"); + return; + } + + /* Create alias */ + memory_region_init_alias(&phb->mr_m64[index], OBJECT(phb), "phb3-m64", + &phb->pci_mmio, start, size); + memory_region_add_subregion(parent, base, &phb->mr_m64[index]); + phb->m64_mapped[index] = true; +} + +static void pnv_phb3_check_all_m64s(PnvPhb3State *phb) +{ + uint64_t i; + + for (i=0; iioda_LXIVT[idx] = val & (IODA2_LXIVT_SERVER_MASK | + IODA2_LXIVT_PRIORITY_MASK | + IODA2_LXIVT_NODE_ID_MASK); + server = GETFIELD(IODA2_LXIVT_SERVER, val); + prio = GETFIELD(IODA2_LXIVT_PRIORITY, val); + + ics_simple_write_xive(phb->lsi_ics, idx, server, prio, prio); +} + +static uint64_t *pnv_phb3_ioda_access(PnvPhb3State *phb, + unsigned *out_table, unsigned *out_idx) +{ + uint64_t adreg = phb->regs[PHB_IODA_ADDR >> 3]; + unsigned int index = GETFIELD(PHB_IODA_AD_TADR, adreg); + unsigned int table = GETFIELD(PHB_IODA_AD_TSEL, adreg); + unsigned int mask; + uint64_t *tptr = NULL; + + switch(table) { + case IODA2_TBL_LIST: + tptr = phb->ioda_LIST; + mask = 7; + break; + case IODA2_TBL_LXIVT: + tptr = phb->ioda_LXIVT; + mask = 7; + break; + case IODA2_TBL_IVC_CAM: + case IODA2_TBL_RBA: + mask = 31; + break; + case IODA2_TBL_RCAM: + mask = 63; + break; + case IODA2_TBL_MRT: + mask = 7; + break; + case IODA2_TBL_PESTA: + case IODA2_TBL_PESTB: + mask = 255; + break; + case IODA2_TBL_TVT: + tptr = phb->ioda_TVT; + mask = 255; + break; + case IODA2_TBL_TCAM: + case IODA2_TBL_TDR: + mask = 63; + break; + case IODA2_TBL_M64BT: + tptr = phb->ioda_M64BT; + mask = 15; + break; + case IODA2_TBL_M32DT: + tptr = phb->ioda_MDT; + mask = 255; + break; + case IODA2_TBL_PEEV: + tptr = phb->ioda_PEEV; + mask = 3; + break; + default: + DBG_ERR(phb, "Unknown IODA table idx %d", table); + return NULL; + } + index &= mask; + if (out_idx) { + *out_idx = index; + } + if (out_table) { + *out_table = table; + } + if (adreg & PHB_IODA_AD_AUTOINC) { + index = (index + 1) & mask; + adreg = SETFIELD(PHB_IODA_AD_TADR, adreg, index); + } + if (tptr) { + tptr += index; + } + phb->regs[PHB_IODA_ADDR >> 3] = adreg; + return tptr; +} + +static uint64_t pnv_phb3_ioda_read(PnvPhb3State *phb) +{ + unsigned table; + uint64_t *tptr; + + tptr = pnv_phb3_ioda_access(phb, &table, NULL); + if (!tptr) { + /* Return 0 on unsupported tables, not ff's */ + return 0; + } + return *tptr; +} + +static void pnv_phb3_ioda_write(PnvPhb3State *phb, uint64_t val) +{ + unsigned table, idx; + uint64_t *tptr; + + tptr = pnv_phb3_ioda_access(phb, &table, &idx); + if (!tptr) { + return; + } + + /* Handle side effects */ + switch(table) { + case IODA2_TBL_LXIVT: + pnv_phb3_lxivt_write(phb, idx, val); + break; + case IODA2_TBL_M64BT: + *tptr = val; + pnv_phb3_check_m64(phb, idx); + break; + default: + *tptr = val; + } +} + +/* This is called whenever the PHB LSI, MSI source ID register or + * the PBCQ irq filters are written. + */ +void pnv_phb3_remap_irqs(PnvPhb3State *phb) +{ + uint32_t local, global, count, mask, comp; + uint64_t baren; + + /* First check if we are enabled. Unlike real HW we don't separate TX and RX + * so we enable if both are set + */ + baren = phb->pbcq->nest_regs[PBCQ_NEST_BAR_EN]; + if (!(baren & PBCQ_NEST_BAR_EN_IRSN_RX) || + !(baren & PBCQ_NEST_BAR_EN_IRSN_TX)) { + phb->lsi_ics->offset = 0; + return; + } + + /* Grab local LSI source ID */ + local = GETFIELD(PHB_LSI_SRC_ID, phb->regs[PHB_LSI_SOURCE_ID >> 3]) << 3; + + /* Grab global one and compare */ + global = GETFIELD(PBCQ_NEST_LSI_SRC, + phb->pbcq->nest_regs[PBCQ_NEST_LSI_SRC_ID]) << 3; + if (global != local) { + /* This happens during initialization, let's come back when we + * are properly configured + */ + phb->lsi_ics->offset = 0; + return; + } + + /* Get the base on the powerbus */ + comp = GETFIELD(PBCQ_NEST_IRSN_COMP, + phb->pbcq->nest_regs[PBCQ_NEST_IRSN_COMPARE]); + mask = GETFIELD(PBCQ_NEST_IRSN_COMP, + phb->pbcq->nest_regs[PBCQ_NEST_IRSN_MASK]); + count = ((~mask) + 1) & 0x7ffff; + phb->total_irq = count; + + /* Sanity checks */ + if ((global + 8) > count) { + DBG_MAP(phb, "LSIs out of reach: LSI base=%d total irq=%d", + global, count); + } + if (count > 2048) { + DBG_MAP(phb, "More interrupts than supported: %d", count); + } + if ((comp & mask) != comp) { + DBG_MAP(phb, "IRQ compare bits not in mask: comp=0x%x mask=0x%x", + comp, mask); + comp &= mask; + } + /* Setup LSI offset */ + phb->lsi_ics->offset = comp + global; + + /* Setup MSI offset */ + pnv_phb3_msi_update_config(phb->msis, comp, count); + + DBG_MAP(phb, "Initialized for %d interrupts @0x%x, LSI off=%d", + count, comp, global); +} + +static void pnv_phb3_lsi_src_id_write(PnvPhb3State *phb, uint64_t val) +{ + /* Sanitize content */ + val &= PHB_LSI_SRC_ID_MASK; + phb->regs[PHB_LSI_SOURCE_ID >> 3] = val; + pnv_phb3_remap_irqs(phb); +} + +static void pnv_phb3_rtc_invalidate(PnvPhb3State *phb, uint64_t val) +{ + PnvPhb3DMASpace *ds; + + /* Always invalidate all for now ... */ + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + ds->pe_num = PHB_INVALID_PE; + } +} + + +static void pnv_phb3_update_msi_regions(PnvPhb3DMASpace *ds) +{ + uint64_t cfg = ds->phb->regs[PHB_PHB3_CONFIG >> 3]; + + if (cfg & PHB_PHB3C_32BIT_MSI_EN) { + if (!ds->msi32_mapped) { + memory_region_add_subregion(&ds->dma_mr, 0xffff0000, &ds->msi32_mr); + ds->msi32_mapped = true; + } + } else { + if (ds->msi32_mapped) { + memory_region_del_subregion(&ds->dma_mr, &ds->msi32_mr); + ds->msi32_mapped = false; + } + } + + if (cfg & PHB_PHB3C_64BIT_MSI_EN) { + if (!ds->msi64_mapped) { + memory_region_add_subregion(&ds->dma_mr, + (1ull << 60), &ds->msi64_mr); + ds->msi64_mapped = true; + } + } else { + if (ds->msi64_mapped) { + memory_region_del_subregion(&ds->dma_mr, &ds->msi64_mr); + ds->msi64_mapped = false; + } + } +} + +static void pnv_phb3_update_all_msi_regions(PnvPhb3State *phb) +{ + PnvPhb3DMASpace *ds; + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + pnv_phb3_update_msi_regions(ds); + } +} + +void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size) +{ + PnvPhb3State *phb = opaque; + bool changed; + + /* Special case configuration data */ + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + pnv_phb3_config_write(phb, off & 0x3, size, val); + return; + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + DBG_ERR(phb, "Invalid register access, offset: 0x%x size: %d", + (unsigned int)off, size); + return; + } + + /* Handle masking */ + switch(off) { + case PHB_M64_UPPER_BITS: + val &= 0xfffc000000000000ull; + break; + } + + /* Record whether it changed */ + changed = phb->regs[off >> 3] != val; + + /* Store in register cache first */ + phb->regs[off >> 3] = val; + + /* Handle side effects */ + switch(off) { + case PHB_PHB3_CONFIG: + if (changed) { + pnv_phb3_update_all_msi_regions(phb); + } + /* fall through */ + case PHB_M32_BASE_ADDR: + case PHB_M32_BASE_MASK: + case PHB_M32_START_ADDR: + if (changed) { + pnv_phb3_check_m32(phb); + } + break; + case PHB_M64_UPPER_BITS: + if (changed) { + pnv_phb3_check_all_m64s(phb); + } + break; + case PHB_LSI_SOURCE_ID: + if (changed) { + pnv_phb3_lsi_src_id_write(phb, val); + } + break; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + pnv_phb3_ioda_write(phb, val); + break; + + /* RTC invalidation */ + case PHB_RTC_INVALIDATE: + pnv_phb3_rtc_invalidate(phb, val); + break; + + /* FFI request */ + case PHB_FFI_REQUEST: + pnv_phb3_msi_ffi(phb->msis, val); + break; + + /* Silent simple writes */ + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_RBA_BAR: + case PHB_IVT_BAR: + case PHB_FFI_LOCK: + break; + +#ifdef DISPLAY_UNIMPLENTED_REG + /* Noise on anything else */ + default: + DBG_ERR(phb, "reg_write 0x%x=%016llx", + (unsigned int)off, (unsigned long long)val); +#endif + } +} + +uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size) +{ + PnvPhb3State *phb = opaque; + uint64_t val; + + if ((off & 0xfffc) == PHB_CONFIG_DATA) { + return pnv_phb3_config_read(phb, off & 0x3, size); + } + + /* Other registers are 64-bit only */ + if (size != 8 || off & 0x7) { + DBG_ERR(phb, "Invalid register access, offset: 0x%x size: %d", + (unsigned int)off, size); + return ~0ull; + } + + /* Default read from cache */ + val = phb->regs[off >> 3]; + + switch(off) { + /* Simulate venice DD2.0 */ + case PHB_VERSION: + return 0x000000a300000005ull; + + /* IODA table accesses */ + case PHB_IODA_DATA0: + return pnv_phb3_ioda_read(phb); + + /* Link training always appears trained */ + case PHB_PCIE_DLP_TRAIN_CTL: + return PHB_PCIE_DLP_INBAND_PRESENCE | PHB_PCIE_DLP_TC_DL_LINKACT; + + /* FFI Lock */ + case PHB_FFI_LOCK: + /* Set lock and return previous value */ + phb->regs[off >> 3] |= PHB_FFI_LOCK_STATE; + return val; + + /* Silent simple reads */ + case PHB_PHB3_CONFIG: + case PHB_M32_BASE_ADDR: + case PHB_M32_BASE_MASK: + case PHB_M32_START_ADDR: + case PHB_CONFIG_ADDRESS: + case PHB_IODA_ADDR: + case PHB_RTC_INVALIDATE: + case PHB_TCE_KILL: + case PHB_TCE_SPEC_CTL: + case PHB_PEST_BAR: + case PHB_PELTV_BAR: + case PHB_RTT_BAR: + case PHB_RBA_BAR: + case PHB_IVT_BAR: + case PHB_M64_UPPER_BITS: + break; + +#ifdef DISPLAY_UNIMPLENTED_REG + /* Noise on anything else */ + default: + DBG_ERR(phb, "reg_read 0x%x=%016llx", + (unsigned int)off, (unsigned long long)val); +#endif + } + return val; +} + +static const MemoryRegionOps pnv_phb3_reg_ops = { + .read = pnv_phb3_reg_read, + .write = pnv_phb3_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int pnv_phb3_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* Check that out properly ... */ + return irq_num & 3; +} + +static void pnv_phb3_set_irq(void *opaque, int irq_num, int level) +{ + PnvPhb3State *phb = opaque; + + /* LSI only ... */ + if (irq_num > 3) { + DBG_ERR(phb, "Unknown IRQ to set %d", irq_num); + } + qemu_set_irq(phb->lsi_ics->qirqs[irq_num], level); +} + +static bool pnv_phb3_resolve_pe(PnvPhb3DMASpace *ds) +{ + uint64_t rtt, addr; + uint16_t rte; + int bus_num; + + /* Already resolved ? */ + if (ds->pe_num != PHB_INVALID_PE) { + return true; + } + + /* We need to lookup the RTT */ + rtt = ds->phb->regs[PHB_RTT_BAR >> 3]; + if (!(rtt & PHB_RBA_BAR_ENABLE)) { + DBG_ERR(ds->phb, "DMA with RTT BAR disabled !"); + // Set error bits ? fence ? ... + return false; + } + + /* Read RTE */ + bus_num = pci_bus_num(ds->bus); + addr = rtt & PHB_RTT_BASE_ADDRESS_MASK; + addr += 2 * ((bus_num << 8) | ds->devfn); + if (dma_memory_read(&address_space_memory, addr, &rte, sizeof(rte))) { + DBG_ERR(ds->phb, "Failed to read RTT entry at %016llx", + (unsigned long long)addr); + // Set error bits ? fence ? ... + return false; + } + rte = be16_to_cpu(rte); + + /* Fail upon reading of invalid PE# */ + if (rte >= PHB_NUM_PE) { + DBG_ERR(ds->phb, "RTE for RID 0x%x invalid (%04x)", ds->devfn, rte); + // Set error bits ? fence ? ... + return false; + } + ds->pe_num = rte; + return true; +} + +static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, + bool is_write, uint64_t tve, + IOMMUTLBEntry *tlb) +{ + uint64_t tta = GETFIELD(IODA2_TVT_TABLE_ADDR, tve); + int32_t lev = GETFIELD(IODA2_TVT_NUM_LEVELS, tve); + uint32_t tts = GETFIELD(IODA2_TVT_TCE_TABLE_SIZE, tve); + uint32_t tps = GETFIELD(IODA2_TVT_IO_PSIZE, tve); + + DBG_DMA(ds->phb, "xlate %016llx:%c TVE=%016llx", + (unsigned long long)addr, is_write ? 'W' : 'R', + (unsigned long long)tve); + + DBG_DMA(ds->phb, " tta=%016llx lev=%d tts=%d tps=%d", + (unsigned long long)tta, lev, tts, tps); + + /* Invalid levels */ + if (lev > 4) { + DBG_ERR(ds->phb, "Invalid #levels in TVE %d", lev); + return; + } + + /* IO Page Size of 0 means untranslated, else use TCEs */ + if (tps == 0) { + /* We only support non-translate in top window + * XXX FIX THAT, Venice/Murano support it on bottom window + * above 4G and Naples suports it on everything + */ + if (!(tve & PPC_BIT(51))) { + DBG_ERR(ds->phb, "xlate for invalid non-translate TVE"); + return; + } + // XXX Handle boundaries */ + + /* XXX Use 4k pages like q35 ... for now */ + DBG_DMA(ds->phb, " non-translate ok"); + tlb->iova = addr & 0xfffffffffffff000ull; + tlb->translated_addr = addr & 0x0003fffffffff000ull; + tlb->addr_mask = 0xfffull; + tlb->perm = IOMMU_RW; + } else { + uint32_t tce_shift, tbl_shift, sh; + uint64_t base, taddr, tce, tce_mask; + + /* TVE disabled ? */ + if (tts == 0) { + DBG_ERR(ds->phb, "xlate for invalid translated TVE"); + return; + } + + /* Address bits per bottom level TCE entry */ + tce_shift = tps + 11; + + /* Address bits per table level */ + tbl_shift = tts + 8; + + /* Top level table base address */ + base = tta << 12; + + /* Total shift to first level */ + sh = tbl_shift * lev + tce_shift; + + DBG_DMA(ds->phb, " tce_shift %d tbl_shift %d", tce_shift, tbl_shift); + + // XXX Multi-level untested */ + while ((lev--) >= 0) { + /* Grab the TCE address */ + taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3); + if (dma_memory_read(&address_space_memory, taddr, &tce, sizeof(tce))) { + DBG_ERR(ds->phb, "Failed to read TCE at 0x%016llx", + (unsigned long long)taddr); + return; + } + tce = be64_to_cpu(tce); + DBG_DMA(ds->phb, " lev %d taddr 0x%016llx tce 0x%016llx", lev + 1, + (unsigned long long)taddr, (unsigned long long)tce); + + /* Check permission for indirect TCE */ + if ((lev >= 0) && !(tce & 3)) { + DBG_ERR(ds->phb, "Invalid indirect TCE at 0x%016llx", + (unsigned long long)taddr); + DBG_ERR(ds->phb, " xlate %016llx:%c TVE=%016llx", + (unsigned long long)addr, is_write ? 'W' : 'R', + (unsigned long long)tve); + DBG_ERR(ds->phb, " tta=%016llx lev=%d tts=%d tps=%d", + (unsigned long long)tta, lev, tts, tps); + return; + } + sh -= tbl_shift; + base = tce & ~0xfffull; + } + + /* We exit the loop with TCE being the final TCE */ + tce_mask = ~((1ull << tce_shift) - 1); + tlb->iova = addr & tce_mask; + tlb->translated_addr = tce & tce_mask; + tlb->addr_mask = ~tce_mask; + tlb->perm = tce & 3; + if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { + DBG_ERR(ds->phb, "TCE access fault at 0x%016llx", + (unsigned long long)taddr); + DBG_ERR(ds->phb, " xlate %016llx:%c TVE=%016llx", + (unsigned long long)addr, is_write ? 'W' : 'R', + (unsigned long long)tve); + DBG_ERR(ds->phb, " tta=%016llx lev=%d tts=%d tps=%d", + (unsigned long long)tta, lev, tts, tps); + } + } +} + +static IOMMUTLBEntry pnv_phb3_translate_iommu(MemoryRegion *iommu, hwaddr addr, + bool is_write) +{ + PnvPhb3DMASpace *ds = container_of(iommu, PnvPhb3DMASpace, dma_mr); + int tve_sel; + uint64_t tve, cfg; + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + /* Resolve PE# */ + if (!pnv_phb3_resolve_pe(ds)) { + DBG_ERR(ds->phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return ret; + } + + DBG_DMA(ds->phb, "xlate bus @%p (%d) devfn 0x%x PE %d ds @%p", + ds->bus, pci_bus_num(ds->bus),ds->devfn, ds->pe_num, ds); + + /* Check top bits */ + switch (addr >> 60) { + case 00: + /* DMA or 32-bit MSI ? */ + cfg = ds->phb->regs[PHB_PHB3_CONFIG >> 3]; + if ((cfg & PHB_PHB3C_32BIT_MSI_EN) && + ((addr & 0xffffffffffff0000ull) == 0xffff0000ull)) { + DBG_ERR(ds->phb, "xlate on 32-bit MSI region"); + return ret; + } + /* Choose TVE XXX Use PHB3 Control Register */ + tve_sel = (addr >> 59) & 1; + tve = ds->phb->ioda_TVT[ds->pe_num * 2 + tve_sel]; + DBG_DMA(ds->phb, " TVE_SEL=%d -> %d", + tve_sel, ds->pe_num * 2 + tve_sel); + pnv_phb3_translate_tve(ds, addr, is_write, tve, &ret); + break; + case 01: + DBG_ERR(ds->phb, "xlate on 64-bit MSI region"); + break; + default: + DBG_ERR(ds->phb, "xlate on unsupported address 0x%016llx", + (unsigned long long)addr); + } + return ret; +} + +static const MemoryRegionIOMMUOps pnv_phb3_iommu_ops = { + .translate = pnv_phb3_translate_iommu, +}; + +/* + * MSI/MSIX memory region implementation. + * The handler handles both MSI and MSIX. + */ +static void pnv_phb3_msi_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + PnvPhb3DMASpace *ds = opaque; + + /* Resolve PE# */ + if (!pnv_phb3_resolve_pe(ds)) { + DBG_ERR(ds->phb, "Failed to resolve PE# for bus @%p (%d) devfn 0x%x", + ds->bus, pci_bus_num(ds->bus), ds->devfn); + return; + } + + pnv_phb3_msi_send(ds->phb->msis, addr, data, ds->pe_num); +} + +static const MemoryRegionOps pnv_phb3_msi_ops = { + /* There is no .read as the read result is undefined by PCI spec */ + .read = NULL, + .write = pnv_phb3_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +static AddressSpace *pnv_phb3_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + PnvPhb3State *phb = opaque; + PnvPhb3DMASpace *ds; + + DBG_DMA(phb, "get IOMMU for bus @%p devfn 0x%x", bus, devfn); + + QLIST_FOREACH(ds, &phb->dma_spaces, list) { + if (ds->bus == bus && ds->devfn == devfn) { + DBG_DMA(phb, " found @%p", ds); + break; + } + } + if (ds == NULL) { + ds = g_malloc0(sizeof(PnvPhb3DMASpace)); + ds->bus = bus; + ds->devfn = devfn; + ds->pe_num = PHB_INVALID_PE; + ds->phb = phb; + memory_region_init_iommu(&ds->dma_mr, OBJECT(phb), + &pnv_phb3_iommu_ops, "phb3_iommu", UINT64_MAX); + address_space_init(&ds->dma_as, &ds->dma_mr, "phb3_iommu"); + memory_region_init_io(&ds->msi32_mr, OBJECT(phb), &pnv_phb3_msi_ops, + ds, "msi32", 0x10000); + memory_region_init_io(&ds->msi64_mr, OBJECT(phb), &pnv_phb3_msi_ops, + ds, "msi64", 0x100000); + pnv_phb3_update_msi_regions(ds); + + QLIST_INSERT_HEAD(&phb->dma_spaces, ds, list); + DBG_DMA(phb, " created @%p", ds); + } + return &ds->dma_as; +} + +static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->max_dev = 1; +} + +#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root-bus" + +static const TypeInfo pnv_phb3_root_bus_info = { + .name = TYPE_PNV_PHB3_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .class_init = pnv_phb3_root_bus_class_init, +}; + +static void pnv_phb3_initfn(Object *obj) +{ + PnvPhb3State *phb = PNV_PHB3(obj); + + phb->lsi_ics = ICS(object_new(TYPE_ICS_SIMPLE)); + object_property_add_child(OBJECT(phb), "ics", OBJECT(phb->lsi_ics), NULL); + /* Default init ... will be fixed by HW inits */ + phb->lsi_ics->offset = 0; + phb->lsi_ics->nr_irqs = PHB_NUM_LSI; + QLIST_INIT(&phb->dma_spaces); +} + +static void pnv_phb3_realize(DeviceState *dev, Error **errp) +{ + PnvPhb3State *phb = PNV_PHB3(dev); + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + Error *error = NULL; + int i; + + memory_region_init(&phb->pci_mmio, OBJECT(phb), "pci-mmio", + PCI_MMIO_TOTAL_SIZE); + + /* PHB3 doesn't support IO space. However, qemu gets very upset if + * we don't have an IO region to anchor IO BARs onto so we just + * initialize one which we never hook up to anything + */ + memory_region_init(&phb->pci_io, OBJECT(phb), "pci-io", 0x10000); + + memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb3_reg_ops, phb, + "phb3-regs", 0x1000); + + /* Realize LSI ICS */ + xics_add_ics(phb->xics, phb->lsi_ics); + object_property_set_bool(OBJECT(phb->lsi_ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } + for (i = 0; i < PHB_NUM_LSI; i++) + ics_simple_set_irq_type(phb->lsi_ics, i, true); + + pci->bus = pci_register_bus(dev, "phb3-root-bus", + pnv_phb3_set_irq, pnv_phb3_map_irq, phb, + &phb->pci_mmio, &phb->pci_io, + 0, 4, TYPE_PNV_PHB3_ROOT_BUS); + pci->bus->devfn_max = 1; + pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); +} + +void pnv_phb3_update_regions(PnvPhb3State *phb) +{ + /* Unmap first always */ + if (phb->regs_mapped) { + memory_region_del_subregion(&phb->pbcq->phbbar, &phb->mr_regs); + phb->regs_mapped = false; + } + + /* Map registers if enabled */ + if (phb->pbcq->phb_mapped) { + /* XXX We should use the PHB BAR 2 register but we don't ... */ + memory_region_add_subregion(&phb->pbcq->phbbar, 0, &phb->mr_regs); + phb->regs_mapped = true; + } + + /* Check/update m32 */ + if (phb->m32_mapped) { + pnv_phb3_check_m32(phb); + } +} + +static void pnv_phb3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_phb3_realize; +} + +static const TypeInfo pnv_phb3_type_info = { + .name = TYPE_PNV_PHB3, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PnvPhb3State), + .class_init = pnv_phb3_class_init, + .instance_init = pnv_phb3_initfn, +}; + +static void pnv_phb3_register_types(void) +{ + type_register_static(&pnv_phb3_type_info); + type_register_static(&pnv_phb3_root_bus_info); +} + +type_init(pnv_phb3_register_types) + +void pnv_phb3_create(PnvChip *chip, XICSState *xics, uint32_t idx) +{ + struct DeviceState *dev; + PnvPhb3State *phb; + PnvPBCQState *pbcq; + PCIHostState *pcih; + PCIDevice *pdev; + PCIBridge *bdev; + uint8_t chassis; + + chassis = chip->chip_id * 4 + idx; + + /* Create PBCQ */ + dev = qdev_create(&chip->xscom->bus, TYPE_PNV_PBCQ); + qdev_prop_set_uint32(dev, "phb_id", idx); + qdev_init_nofail(dev); + pbcq = PNV_PBCQ(dev); + + /* Create PHB3 */ + dev = qdev_create(NULL, TYPE_PNV_PHB3); + phb = PNV_PHB3(dev); + phb->pbcq = pbcq; + pbcq->phb = phb; + phb->xics = xics; + qdev_init_nofail(dev); + pcih = PCI_HOST_BRIDGE(phb); + + /* Create MSI source */ + phb->msis = pnv_phb3_msi_create(phb); + + /* Add root complex */ + pdev = pci_create_multifunction(pcih->bus, 0, false, TYPE_PNV_PHB3_RC); + qdev_prop_set_uint8(&pdev->qdev, "chassis", chassis); + qdev_prop_set_uint16(&pdev->qdev, "slot", 1); + qdev_init_nofail(&pdev->qdev); + bdev = PCI_BRIDGE(pdev); + + /* Setup bus for that chip */ + chip->phb[idx] = pci_bridge_get_sec_bus(bdev); +} diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c new file mode 100644 index 0000000000000..1cc289922109f --- /dev/null +++ b/hw/pci-host/pnv_phb3_msi.c @@ -0,0 +1,354 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/msi.h" +#include "hw/ppc/xics.h" + +#define PHB3_MAX_MSI 2048 + +//#define DEBUG_MSI 1 +#define DEBUG_MSI 0 + +typedef struct Phb3MsiState { + struct ICSState ics; + PnvPhb3State *phb; + uint64_t rba[PHB3_MAX_MSI/64]; + uint32_t rba_sum; +} Phb3MsiState; + +#define TYPE_PHB3_MSI "phb3-msi" +#define PHB3_MSI(obj) OBJECT_CHECK(Phb3MsiState, (obj), TYPE_PHB3_MSI) + +#define DBG_ERR(p, fmt, ...) do { \ + if (1) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + +#define DBG_MSI(p, fmt, ...) do { \ + if (DEBUG_MSI) fprintf(stderr, "PHB3(%s): " fmt "\n", __func__, ## __VA_ARGS__); \ + } while(0) + + +static void phb3_msi_initfn(Object *obj) +{ + Phb3MsiState *msis = PHB3_MSI(obj); + + /* Will be overriden later */ + msis->ics.offset = 0; + + /* Hard wire 2048, we ignore the fact that 8 of them can be + * taken over by LSIs at this point + */ + msis->ics.nr_irqs = PHB3_MAX_MSI; +} + +static uint64_t phb3_msi_ive_addr(Phb3MsiState *msis, int srcno) +{ + uint64_t ivtbar = msis->phb->regs[PHB_IVT_BAR >> 3]; + uint64_t phbctl = msis->phb->regs[PHB_CONTROL >> 3]; + + if (!(ivtbar & PHB_IVT_BAR_ENABLE)) { + DBG_ERR(msis->phb, "Failed access to disable IVT BAR !"); + return 0; + } + + if (srcno >= (ivtbar & PHB_IVT_LENGTH_MASK)) { + DBG_ERR(msis->phb, "MSI out of bounds (%d vs %ld)", + srcno, (long)(ivtbar & PHB_IVT_LENGTH_MASK)); + return 0; + } + + ivtbar &= PHB_IVT_BASE_ADDRESS_MASK; + + if (phbctl & PHB_CTRL_IVE_128_BYTES) { + return ivtbar + 128 * srcno; + } else { + return ivtbar + 16 * srcno; + } +} + +static bool phb3_msi_read_ive(Phb3MsiState *msis, int srcno, uint64_t *out_ive) +{ + uint64_t ive_addr, ive; + + ive_addr = phb3_msi_ive_addr(msis, srcno); + if (!ive_addr) { + return false; + } + if (dma_memory_read(&address_space_memory, ive_addr, &ive, sizeof(ive))) { + DBG_ERR(msis->phb, "Failed to read IVE at 0x%016llx", + (unsigned long long)ive_addr); + return false; + } + *out_ive = be64_to_cpu(ive); + + return true; +} + +static void phb3_msi_set_p(Phb3MsiState *msis, int srcno, uint8_t gen) +{ + uint64_t ive_addr; + uint8_t p = 0x01 | (gen << 1); + + ive_addr = phb3_msi_ive_addr(msis, srcno); + if (!ive_addr) { + return; + } + DBG_MSI(msis->phb, "MSI %d: setting P", srcno); + if (dma_memory_write(&address_space_memory, ive_addr + 4, &p, 1)) { + DBG_ERR(msis->phb, "Failed to write IVE (set P) at 0x%016llx", + (unsigned long long)ive_addr); + } +#ifdef DEBUG_MSI + { + uint64_t ive; + + if (dma_memory_read(&address_space_memory, ive_addr, &ive, 8)) { + DBG_ERR(ds->phb, "Failed to read IVE (set P) at 0x%016llx", + (unsigned long long)ive_addr); + } + DBG_MSI(msis->phb, " IVE readback: %016llx", + (unsigned long long)be64_to_cpu(ive)); + } +#endif +} + +static void phb3_msi_set_q(Phb3MsiState *msis, int srcno) +{ + uint64_t ive_addr; + uint8_t q = 0x01; + + ive_addr = phb3_msi_ive_addr(msis, srcno); + if (!ive_addr) { + return; + } + DBG_MSI(msis->phb, "MSI %d: setting Q", srcno); + if (dma_memory_write(&address_space_memory, ive_addr + 5, &q, 1)) { + DBG_ERR(ds->phb, "Failed to write IVE (set Q) at 0x%016llx", + (unsigned long long)ive_addr); + } +#ifdef DEBUG_MSI + { + uint64_t ive; + + if (dma_memory_read(&address_space_memory, ive_addr, &ive, 8)) { + DBG_ERR(ds->phb, "Failed to read IVE (set P) at 0x%016llx", + (unsigned long long)ive_addr); + } + DBG_MSI(msis->phb, " IVE readback: %016llx", + (unsigned long long)be64_to_cpu(ive)); + } +#endif +} + +static void phb3_msi_try_send(Phb3MsiState *msis, int srcno, bool ignore_p) +{ + uint64_t ive; + uint64_t server, prio, pq, gen; + + if (!phb3_msi_read_ive(msis, srcno, &ive)) { + return; + } + + server = GETFIELD(IODA2_IVT_SERVER, ive); + prio = GETFIELD(IODA2_IVT_PRIORITY, ive); + pq = GETFIELD(IODA2_IVT_Q, ive); + if (!ignore_p) { + pq |= GETFIELD(IODA2_IVT_P, ive) << 1; + } + gen = GETFIELD(IODA2_IVT_GEN, ive); + + DBG_MSI(msis->phb, "MSI %d: try_send, ive=0x%016llx eff pq=%d", srcno, + (unsigned long long)ive, (int)pq); + + switch(pq) { + case 0: /* 00 */ + if (prio == 0xff) { + /* Masked, set Q */ + phb3_msi_set_q(msis, srcno); + } else { + /* Enabled, set P and send */ + phb3_msi_set_p(msis, srcno, gen); + icp_irq(&msis->ics, server, srcno + msis->ics.offset, prio); + } + break; + case 2: /* 10 */ + /* Already pending, set Q */ + phb3_msi_set_q(msis, srcno); + break; + case 1: /* 01 */ + case 3: /* 11 */ + default: + /* Just drop stuff if Q already set */ + break; + } +} + +static void phb3_msi_set_irq(void *opaque, int srcno, int val) +{ + Phb3MsiState *msis = opaque; + + if (val) { + phb3_msi_try_send(msis, srcno, false); + } +} + + +void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, + int32_t dev_pe) +{ + uint64_t ive; + uint16_t pe; + uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f); + + if (src >= msis->ics.nr_irqs) { + DBG_ERR(msis->phb, "MSI %d out of bounds", src); + return; + } + if (dev_pe >= 0) { + if (!phb3_msi_read_ive(msis, src, &ive)) { + return; + } + pe = GETFIELD(IODA2_IVT_PE, ive); + if (pe != dev_pe) { + DBG_ERR(msis->phb, "MSI %d send by PE#%d but assigned to PE#%d", + src, dev_pe, pe); + return; + } + } + qemu_irq_pulse(msis->ics.qirqs[src]); + +} + +void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val) +{ + /* Emit interrupt */ + pnv_phb3_msi_send(msis, val, 0, -1); + + /* Clear FFI lock */ + msis->phb->regs[PHB_FFI_LOCK >> 3] = 0; +} + +static void phb3_msi_reject(ICSState *ics, uint32_t nr) +{ + Phb3MsiState *msis = PHB3_MSI(ics); + unsigned int srcno = nr - ics->offset; + unsigned int idx = srcno >> 6; + unsigned int bit = 1ull << (srcno & 0x3f); + + assert(srcno < PHB3_MAX_MSI); + + DBG_MSI(msis->phb, "MSI %d rejected", srcno); + + msis->rba[idx] |= bit; + msis->rba_sum |= (1u << idx); +} + +static void phb3_msi_resend(ICSState *ics) +{ + Phb3MsiState *msis = PHB3_MSI(ics); + unsigned int i,j; + + if (msis->rba_sum == 0) { + return; + } + + DBG_MSI(msis->phb, "MSI resend..."); + + for (i = 0; i < 32; i++) { + if ((msis->rba_sum & (1u << i)) == 0) { + continue; + } + msis->rba_sum &= ~(1u << i); + for (j = 0; j < 64; j++) { + if ((msis->rba[i] & (1ull << j)) == 0) { + continue; + } + msis->rba[i] &= ~(1u << j); + phb3_msi_try_send(msis, i * 64 + j, true); + } + } +} + +static void phb3_msi_reset(DeviceState *dev) +{ + Phb3MsiState *msis = PHB3_MSI(dev); + + memset(msis->rba, 0, sizeof(msis->rba)); + msis->rba_sum = 0; +} + +void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, + uint32_t count) +{ + if (count > PHB3_MAX_MSI) { + count = PHB3_MAX_MSI; + } + msis->ics.nr_irqs = count; + msis->ics.offset = base; +} + +static void phb3_msi_realize(DeviceState *dev, Error **errp) +{ + Phb3MsiState *msis = PHB3_MSI(dev); + + msis->ics.irqs = NULL; + msis->ics.qirqs = qemu_allocate_irqs(phb3_msi_set_irq, msis, PHB3_MAX_MSI); +} + +static void phb3_msi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *isc = ICS_CLASS(klass); + + dc->realize = phb3_msi_realize; + dc->reset = phb3_msi_reset; + isc->reject = phb3_msi_reject; + isc->resend = phb3_msi_resend; +} + +Phb3MsiState *pnv_phb3_msi_create(PnvPhb3State *phb) +{ + Phb3MsiState *msis; + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_PHB3_MSI); + msis = PHB3_MSI(dev); + msis->phb = phb; + phb->msis = msis; + xics_add_ics(phb->xics, &msis->ics); + qdev_init_nofail(dev); + + return msis; +} + +static const TypeInfo phb3_msi_info = { + .name = TYPE_PHB3_MSI, + .parent = TYPE_ICS, + .instance_size = sizeof(Phb3MsiState), + .class_init = phb3_msi_class_init, + .class_size = sizeof(ICSStateClass), + .instance_init = phb3_msi_initfn, +}; + +static void pnv_phb3_msi_register_types(void) +{ + type_register_static(&phb3_msi_info); +} + +type_init(pnv_phb3_msi_register_types) diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c new file mode 100644 index 0000000000000..591aa96ad2f5f --- /dev/null +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -0,0 +1,315 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" + +#include + +static bool pnv_pbcq_xscom_read(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t *out_val) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + + switch(range) { + case 0: + *out_val = pbcq->nest_regs[offset]; + break; + case 1: + *out_val = pbcq->pci_regs[offset]; + break; + case 2: + if (offset == PBCQ_SPCI_ASB_DATA) { + if (!pbcq->phb) { + *out_val = ~0ull; + break; + } + *out_val = pnv_phb3_reg_read(pbcq->phb, + pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], 8); + break; + } + *out_val = pbcq->spci_regs[offset]; + break; + default: + return false; + } + return true; +} + +static void pnv_pbcq_update_map(PnvPBCQState *pbcq) +{ + uint64_t bar_en = pbcq->nest_regs[PBCQ_NEST_BAR_EN]; + uint64_t bar, mask, size; + + /* + * NOTE: This will really not work well if those are remapped + * after the PHB has created its sub regions. We could do better + * if we had a way to resize regions but we don't really care + * that much in practice as the stuff below really only happens + * once early during boot + */ + + /* Handle unmaps */ + if (pbcq->mmio0_mapped && !(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { + memory_region_del_subregion(get_system_memory(), &pbcq->mmbar0); + pbcq->mmio0_mapped = false; + } + if (pbcq->mmio1_mapped && !(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { + memory_region_del_subregion(get_system_memory(), &pbcq->mmbar1); + pbcq->mmio1_mapped = false; + } + if (pbcq->phb_mapped && !(bar_en & PBCQ_NEST_BAR_EN_PHB)) { + memory_region_del_subregion(get_system_memory(), &pbcq->phbbar); + pbcq->phb_mapped = false; + } + + /* Update PHB if it exists */ + if (pbcq->phb) { + pnv_phb3_update_regions(pbcq->phb); + } + + /* Handle maps */ + if (!pbcq->mmio0_mapped && (bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { + bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] >> 14; + mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0]; + size = ((~mask) >> 14) + 1; + memory_region_init(&pbcq->mmbar0, OBJECT(pbcq), "pbcq-mmio0", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar0); + pbcq->mmio0_mapped = true; + pbcq->mmio0_base = bar; + pbcq->mmio0_size = size; + } + if (!pbcq->mmio1_mapped && (bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { + bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] >> 14; + mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1]; + size = ((~mask) >> 14) + 1; + memory_region_init(&pbcq->mmbar1, OBJECT(pbcq), "pbcq-mmio1", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar1); + pbcq->mmio1_mapped = true; + pbcq->mmio1_base = bar; + pbcq->mmio1_size = size; + } + if (!pbcq->phb_mapped && (bar_en & PBCQ_NEST_BAR_EN_PHB)) { + bar = pbcq->nest_regs[PBCQ_NEST_PHB_BAR] >> 14; + size = 0x1000; + memory_region_init(&pbcq->phbbar, OBJECT(pbcq), "pbcq-phb", size); + memory_region_add_subregion(get_system_memory(), bar, &pbcq->phbbar); + pbcq->phb_mapped = true; + } + + /* Update PHB if it exists */ + if (pbcq->phb) { + pnv_phb3_update_regions(pbcq->phb); + } +} + +static bool pnv_pbcq_xnest_write(PnvPBCQState *pbcq, uint32_t reg, uint64_t val) +{ + switch(reg) { + case PBCQ_NEST_MMIO_BAR0: + case PBCQ_NEST_MMIO_BAR1: + case PBCQ_NEST_MMIO_MASK0: + case PBCQ_NEST_MMIO_MASK1: + if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & + (PBCQ_NEST_BAR_EN_MMIO0 | + PBCQ_NEST_BAR_EN_MMIO1)) { + printf("WARNING: PH3: Changing enabled BAR unsupported\n"); + } + pbcq->nest_regs[reg] = val & 0xffffffffc0000000ull; + return true; + case PBCQ_NEST_PHB_BAR: + if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & PBCQ_NEST_BAR_EN_PHB) { + printf("WARNING: PH3: Changing enabled BAR unsupported\n"); + } + pbcq->nest_regs[reg] = val & 0xfffffffffc000000ull; + return true; + case PBCQ_NEST_BAR_EN: + pbcq->nest_regs[reg] = val & 0xf800000000000000ull; + pnv_pbcq_update_map(pbcq); + pnv_phb3_remap_irqs(pbcq->phb); + return true; + case PBCQ_NEST_IRSN_COMPARE: + case PBCQ_NEST_IRSN_MASK: + pbcq->nest_regs[reg] = val & PBCQ_NEST_IRSN_COMP_MASK; + pnv_phb3_remap_irqs(pbcq->phb); + return true; + case PBCQ_NEST_LSI_SRC_ID: + pbcq->nest_regs[reg] = val & PBCQ_NEST_LSI_SRC_MASK; + pnv_phb3_remap_irqs(pbcq->phb); + return true; + } + + /* XXX Don't error out on other regs for now ... */ + return true; +} + +static bool pnv_pbcq_xpci_write(PnvPBCQState *pbcq, uint32_t reg, uint64_t val) +{ + switch(reg) { + case PBCQ_PCI_BAR2: + pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull; + pnv_pbcq_update_map(pbcq); + break; + } + + /* XXX Don't error out on other regs for now ... */ + return true; +} + +static bool pnv_pbcq_xspci_write(PnvPBCQState *pbcq, uint32_t reg, uint64_t val) +{ + switch(reg) { + case PBCQ_SPCI_ASB_ADDR: + pbcq->spci_regs[reg] = val & 0xfff; + return true; + case PBCQ_SPCI_ASB_STATUS: + pbcq->spci_regs[reg] &= ~val; + return true; + case PBCQ_SPCI_ASB_DATA: + if (!pbcq->phb) { + return true; + } + pnv_phb3_reg_write(pbcq->phb, pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], val, 8); + return true; + // case PBCQ_SPCI_AIB_CAPP_EN: + // case PBCQ_SPCI_CAPP_SEC_TMR: + } + + /* XXX Don't error out on other regs for now ... */ + return true; +} + +static bool pnv_pbcq_xscom_write(XScomDevice *dev, uint32_t range, + uint32_t offset, uint64_t val) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + + switch(range) { + case 0: + return pnv_pbcq_xnest_write(pbcq, offset, val); + case 1: + return pnv_pbcq_xpci_write(pbcq, offset, val); + case 2: + return pnv_pbcq_xspci_write(pbcq, offset, val); + default: + return false; + } +} + +static void pnv_pbcq_default_bars(PnvPBCQState *pbcq) +{ + uint64_t mm0, mm1, reg; + + mm0 = 0x3d00000000000ull + + 0x4000000000ull * pbcq->chip_id + + 0x1000000000ull * pbcq->phb_id; + mm1 = 0x3ff8000000000ull + + 0x0200000000ull * pbcq->chip_id + + 0x0080000000ull * pbcq->phb_id; + reg = 0x3fffe40000000ull + + 0x0000400000ull * pbcq->chip_id + + 0x0000100000ull * pbcq->phb_id; + + pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] = mm0 << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] = mm1 << 14; + pbcq->nest_regs[PBCQ_NEST_PHB_BAR] = reg << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0] = 0x3fff000000000ull << 14; + pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1] = 0x3ffff80000000ull << 14; + pbcq->pci_regs[PBCQ_PCI_BAR2] = reg << 14; +} + +static void pnv_pbcq_realize(DeviceState *dev, Error **errp) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + XScomBus *xb = XSCOM_BUS(dev->parent_bus); + XScomDevice *xd = XSCOM_DEVICE(dev); + + assert(pbcq->phb_id < 4); + + /* Copy chip ID over for ease of access */ + pbcq->chip_id = xb->chip_id; + + /* Calculate XSCOM bases */ + pbcq->nest_xbase = 0x02012000 + 0x400 * pbcq->phb_id; + pbcq->pci_xbase = 0x09012000 + 0x400 * pbcq->phb_id; + pbcq->spci_xbase = 0x09013c00 + 0x040 * pbcq->phb_id; + xd->ranges[0].addr = pbcq->nest_xbase; + xd->ranges[0].size = PBCQ_NEST_REGS_COUNT; + xd->ranges[1].addr = pbcq->pci_xbase; + xd->ranges[1].size = PBCQ_PCI_REGS_COUNT; + xd->ranges[2].addr = pbcq->spci_xbase; + xd->ranges[2].size = PBCQ_SPCI_REGS_COUNT; + + /* XXX Fix OPAL to do that: establish default BAR values */ + pnv_pbcq_default_bars(pbcq); +} + +#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) + + +static int pnv_pbcq_devnode(XScomDevice *dev, void *fdt) +{ + PnvPBCQState *pbcq = PNV_PBCQ(dev); + + _FDT((fdt_property_cell(fdt, "ibm,phb-index", pbcq->phb_id))); + + return 0; +} + +static Property pnv_pbcq_properties[] = { + DEFINE_PROP_UINT32("phb_id", PnvPBCQState, phb_id, 0), + DEFINE_PROP_END_OF_LIST(), + +}; + +static void pnv_pbcq_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XScomDeviceClass *k = XSCOM_DEVICE_CLASS(klass); + static const char *compat[] = { "ibm,power8-pbcq", NULL }; + + k->devnode = pnv_pbcq_devnode; + k->read = pnv_pbcq_xscom_read; + k->write = pnv_pbcq_xscom_write; + k->dt_name = "pbcq"; + k->dt_compatible = compat; + + dc->realize = pnv_pbcq_realize; + dc->props = pnv_pbcq_properties; +} + +static const TypeInfo pnv_pbcq_type_info = { + .name = TYPE_PNV_PBCQ, + .parent = TYPE_XSCOM_DEVICE, + .instance_size = sizeof(PnvPBCQState), + .class_init = pnv_pbcq_class_init, +}; + +static void pnv_pbcq_register_types(void) +{ + type_register_static(&pnv_pbcq_type_info); +} + +type_init(pnv_pbcq_register_types) diff --git a/hw/pci-host/pnv_phb3_rc.c b/hw/pci-host/pnv_phb3_rc.c new file mode 100644 index 0000000000000..76641166889c6 --- /dev/null +++ b/hw/pci-host/pnv_phb3_rc.c @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "qemu/osdep.h" +#include "hw/pci-host/pnv_phb3.h" +#include "hw/pci/pcie_port.h" + +static void pnv_phb3_rc_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + uint32_t root_cmd = + pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); + + pci_bridge_write_config(d, address, val, len); + pcie_cap_slot_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); + pcie_aer_root_write_config(d, address, val, len, root_cmd); +} + +static void pnv_phb3_rc_reset(DeviceState *qdev) +{ + PCIDevice *d = PCI_DEVICE(qdev); + + pcie_cap_root_reset(d); + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_cap_arifwd_reset(d); + pcie_aer_root_reset(d); + pci_bridge_reset(qdev); + pci_bridge_disable_base_limit(d); +} + +static int pnv_phb3_rc_initfn(PCIDevice *d) +{ + PCIEPort *p = PCIE_PORT(d); + PCIESlot *s = PCIE_SLOT(d); + int rc; + + DEVICE(d)->id = "pcie"; + pci_bridge_initfn(d, TYPE_PCIE_BUS); + + /* XXX Make that a property ? Allow for only one device (8 functions) */ + pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devfn_max = 8; + + pcie_port_init_reg(d); + + rc = pcie_cap_init(d, 0x48, PCI_EXP_TYPE_ROOT_PORT, p->port); + if (rc < 0) { + printf("phb3-rc: pcie_cap_init() error %d !\n", rc); + goto err_bridge; + } + pcie_cap_arifwd_init(d); + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s->slot); + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + printf("phb3-rc: pcie_chassis_add_slot() error %d !\n", rc); + goto err_pcie_cap; + } + pcie_cap_root_init(d); + rc = pcie_aer_init(d, 0x100, PCI_ERR_SIZEOF); + if (rc < 0) { + printf("phb3-rc: pcie_aer_init() error %d !\n", rc); + goto err_slot; + } + pcie_aer_root_init(d); + return 0; + +err_slot: + pcie_chassis_del_slot(s); +err_pcie_cap: + pcie_cap_exit(d); +err_bridge: + pci_bridge_exitfn(d); + return rc; +} + +static void pnv_phb3_rc_exitfn(PCIDevice *d) +{ + PCIESlot *s = PCIE_SLOT(d); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + pci_bridge_exitfn(d); +} + +static void pnv_phb3_rc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->is_express = 1; + k->is_bridge = 1; + k->init = pnv_phb3_rc_initfn; + k->exit = pnv_phb3_rc_exitfn; + k->config_write = pnv_phb3_rc_write_config; + k->vendor_id = PCI_VENDOR_ID_IBM; + k->device_id = 0x03dc; + k->revision = 0; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->desc = "IBM PHB3 PCIE Root Port"; + dc->reset = pnv_phb3_rc_reset; +} + +static const TypeInfo pnv_phb3_rc_info = { + .name = TYPE_PNV_PHB3_RC, + .parent = TYPE_PCIE_SLOT, + .class_init = pnv_phb3_rc_class_init, +}; + +static void pnv_phb3_rc_register_types(void) +{ + type_register_static(&pnv_phb3_rc_info); +} + +type_init(pnv_phb3_rc_register_types) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 56bc40447dbb5..84dffab9f1c14 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -45,9 +45,15 @@ #include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/msi.h" #include "hw/isa/isa.h" #include "hw/char/serial.h" #include "hw/timer/mc146818rtc.h" +#include "hw/pci-host/pnv_phb3.h" + #include "exec/address-spaces.h" #include "qemu/config-file.h" #include "qapi/error.h" @@ -511,9 +517,11 @@ static void pnv_lpc_irq_handler_cpld(void *opaque, int n, int level) } static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, - bool has_lpc, bool has_lpc_irq) + bool has_lpc, bool has_lpc_irq, + unsigned int num_phbs) { PnvChip *chip = &sys->chips[chip_no]; + unsigned int i; if (chip_no >= PNV_MAX_CHIPS) { return; @@ -549,6 +557,11 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, /* Create the simplified OCC model */ pnv_occ_create(chip); + + /* Create a PCI, for now do one chip with 2 PHBs */ + for (i = 0; i < num_phbs; i++) { + pnv_phb3_create(chip, sys->xics, i); + } } static void ppc_powernv_init(MachineState *machine) @@ -572,6 +585,9 @@ static void ppc_powernv_init(MachineState *machine) void *fdt; int i; + /* MSIs are supported on this platform */ + msi_nonbroken = true; + /* Set up Interrupt Controller before we create the VCPUs */ xics = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads, XICS_IRQS_POWERNV); @@ -611,9 +627,9 @@ static void ppc_powernv_init(MachineState *machine) */ sys->num_chips = 1; - /* Create only one chip for now with an LPC bus + /* Create only one chip for now with an LPC bus and one PHB */ - pnv_create_chip(sys, 0, true, false); + pnv_create_chip(sys, 0, true, false, 1); /* Grab chip 0's ISA bus */ isa_bus = sys->chips[0].lpc_bus; diff --git a/include/hw/pci-host/pnv_phb3.h b/include/hw/pci-host/pnv_phb3.h new file mode 100644 index 0000000000000..d70bf8bd38c0b --- /dev/null +++ b/include/hw/pci-host/pnv_phb3.h @@ -0,0 +1,145 @@ +#ifndef _HW_PNV_PHB3_H +#define _HW_PNV_PHB3_H +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright IBM Corp. 2014 + */ +#include "hw/hw.h" +#include "hw/ppc/pnv.h" +#include "hw/pci/pci_host.h" +#include "exec/address-spaces.h" +#include "hw/pci-host/pnv_phb3_regs.h" +#include "sysemu/cpus.h" +#include "qom/cpu.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/xics.h" + +#define PHB_NUM_M64 16 +#define PHB_NUM_REGS (0x1000 >> 3) +#define PHB_NUM_LSI 8 +#define PHB_NUM_PE 256 + +#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60) + +#define IODA2_PCI_BUS_MAX 256 + +typedef struct PnvPBCQState PnvPBCQState; +typedef struct PnvPhb3State PnvPhb3State; +typedef struct PnvPhb3DMASpace PnvPhb3DMASpace; + +/* We don't want to include xics.h here */ +typedef struct XICSState XICSState; +typedef struct ICSState ICSState; + +/* Similarily with pnv_phb3_msi */ +typedef struct Phb3MsiState Phb3MsiState; + +/* We have one such address space wrapper per possible device + * under the PHB since they need to be assigned statically at + * qemu device creation time. The relationship to a PE is done + * later dynamically. This means we can potentially create a lot + * of these guys. Q35 stores them as some kind of radix tree but + * we never really need to do fast lookups so instead we simply + * keep a QLIST of them for now, we can add the radix if needed + * later on. + * + * We do cache the PE number to speed things up a bit though. + */ +struct PnvPhb3DMASpace { + PCIBus *bus; + uint8_t devfn; + int pe_num; /* Cached PE number */ +#define PHB_INVALID_PE (-1) + PnvPhb3State *phb; + AddressSpace dma_as; + MemoryRegion dma_mr; + MemoryRegion msi32_mr; + MemoryRegion msi64_mr; + bool msi32_mapped; + bool msi64_mapped; + QLIST_ENTRY(PnvPhb3DMASpace) list; +}; + +struct PnvPhb3State { + PCIHostState parent_obj; + MemoryRegion mr_m32; + MemoryRegion mr_m64[PHB_NUM_M64]; + MemoryRegion mr_regs; + bool regs_mapped; + bool m32_mapped; + bool m64_mapped[PHB_NUM_M64]; + MemoryRegion pci_mmio; + MemoryRegion pci_io; + uint64_t regs[PHB_NUM_REGS]; + PnvPBCQState *pbcq; + uint64_t ioda_LIST[8]; + uint64_t ioda_LXIVT[8]; + uint64_t ioda_TVT[512]; + uint64_t ioda_M64BT[16]; + uint64_t ioda_MDT[256]; + uint64_t ioda_PEEV[4]; + uint32_t total_irq; + XICSState *xics; + ICSState *lsi_ics; + Phb3MsiState *msis; + QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces; +}; + +struct PnvPBCQState { + XScomDevice xd; + uint32_t chip_id; + uint32_t phb_id; + uint32_t nest_xbase; + uint32_t spci_xbase; + uint32_t pci_xbase; + uint64_t nest_regs[PBCQ_NEST_REGS_COUNT]; + uint64_t spci_regs[PBCQ_SPCI_REGS_COUNT]; + uint64_t pci_regs[PBCQ_PCI_REGS_COUNT]; + MemoryRegion mmbar0; + MemoryRegion mmbar1; + MemoryRegion phbbar; + bool mmio0_mapped; + bool mmio1_mapped; + bool phb_mapped; + uint64_t mmio0_base; + uint64_t mmio0_size; + uint64_t mmio1_base; + uint64_t mmio1_size; + PnvPhb3State *phb; +}; + +#define TYPE_PNV_PBCQ "pnv-pbcq" +#define PNV_PBCQ(obj) \ + OBJECT_CHECK(PnvPBCQState, (obj), TYPE_PNV_PBCQ) + + +#define TYPE_PNV_PHB3 "pnv-phb3" +#define PNV_PHB3(obj) \ + OBJECT_CHECK(PnvPhb3State, (obj), TYPE_PNV_PHB3) + +#define TYPE_PNV_PHB3_RC "pnv-phb3-rc" + +uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size); +void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size); +void pnv_phb3_update_regions(PnvPhb3State *phb); +void pnv_phb3_remap_irqs(PnvPhb3State *phb); +void pnv_phb3_create(PnvChip *chip, XICSState *xics, uint32_t idx); +Phb3MsiState *pnv_phb3_msi_create(PnvPhb3State *phb); +void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, + uint32_t count); +void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, + int32_t dev_pe); +void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val); + +#endif /* _HW_PNV_PHB3_H */ diff --git a/include/hw/pci-host/pnv_phb3_regs.h b/include/hw/pci-host/pnv_phb3_regs.h new file mode 100644 index 0000000000000..daad8fcc77d39 --- /dev/null +++ b/include/hw/pci-host/pnv_phb3_regs.h @@ -0,0 +1,505 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PHB3_REGS_H +#define __PHB3_REGS_H + +#define PPC_BIT(bit) (0x8000000000000000UL >> (bit)) +#define PPC_BIT32(bit) (0x80000000UL >> (bit)) +#define PPC_BIT8(bit) (0x80UL >> (bit)) +#define PPC_BITMASK(bs,be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK32(bs,be) ((PPC_BIT32(bs) - PPC_BIT32(be))|PPC_BIT32(bs)) +#define PPC_BITLSHIFT(be) (63 - (be)) +#define PPC_BITLSHIFT32(be) (31 - (be)) + +/* Extract field fname from val */ +#define GETFIELD(fname, val) \ + (((val) & fname##_MASK) >> fname##_LSH) + +/* Set field fname of oval to fval + * NOTE: oval isn't modified, the combined result is returned + */ +#define SETFIELD(fname, oval, fval) \ + (((oval) & ~fname##_MASK) | \ + ((((typeof(oval))(fval)) << fname##_LSH) & fname##_MASK)) + +/* + * PBCQ XSCOM registers + */ + +#define PBCQ_NEST_IRSN_COMPARE 0x1a +#define PBCQ_NEST_IRSN_COMP_MASK PPC_BITMASK(0,18) +#define PBCQ_NEST_IRSN_COMP_LSH PPC_BITLSHIFT(18) +#define PBCQ_NEST_IRSN_MASK 0x1b +#define PBCQ_NEST_LSI_SRC_ID 0x1f +#define PBCQ_NEST_LSI_SRC_MASK PPC_BITMASK(0,7) +#define PBCQ_NEST_LSI_SRC_LSH PPC_BITLSHIFT(7) +#define PBCQ_NEST_REGS_COUNT 0x46 +#define PBCQ_NEST_MMIO_BAR0 0x40 +#define PBCQ_NEST_MMIO_BAR1 0x41 +#define PBCQ_NEST_PHB_BAR 0x42 +#define PBCQ_NEST_MMIO_MASK0 0x43 +#define PBCQ_NEST_MMIO_MASK1 0x44 +#define PBCQ_NEST_BAR_EN 0x45 +#define PBCQ_NEST_BAR_EN_MMIO0 PPC_BIT(0) +#define PBCQ_NEST_BAR_EN_MMIO1 PPC_BIT(1) +#define PBCQ_NEST_BAR_EN_PHB PPC_BIT(2) +#define PBCQ_NEST_BAR_EN_IRSN_RX PPC_BIT(3) +#define PBCQ_NEST_BAR_EN_IRSN_TX PPC_BIT(4) + +#define PBCQ_PCI_REGS_COUNT 0x15 +#define PBCQ_PCI_BAR2 0x0b + +#define PBCQ_SPCI_REGS_COUNT 0x5 +#define PBCQ_SPCI_ASB_ADDR 0x0 +#define PBCQ_SPCI_ASB_STATUS 0x1 +#define PBCQ_SPCI_ASB_DATA 0x2 +#define PBCQ_SPCI_AIB_CAPP_EN 0x3 +#define PBCQ_SPCI_CAPP_SEC_TMR 0x4 + +/* + * PHB MMIO registers + */ + +/* PHB Fundamental register set A */ +#define PHB_LSI_SOURCE_ID 0x100 +#define PHB_LSI_SRC_ID_MASK PPC_BITMASK(5,12) +#define PHB_LSI_SRC_ID_LSH PPC_BITLSHIFT(12) +#define PHB_DMA_CHAN_STATUS 0x110 +#define PHB_DMA_CHAN_ANY_ERR PPC_BIT(27) +#define PHB_DMA_CHAN_ANY_ERR1 PPC_BIT(28) +#define PHB_DMA_CHAN_ANY_FREEZE PPC_BIT(29) +#define PHB_CPU_LOADSTORE_STATUS 0x120 +#define PHB_CPU_LS_ANY_ERR PPC_BIT(27) +#define PHB_CPU_LS_ANY_ERR1 PPC_BIT(28) +#define PHB_CPU_LS_ANY_FREEZE PPC_BIT(29) +#define PHB_DMA_MSI_NODE_ID 0x128 +#define PHB_DMAMSI_NID_FIXED PPC_BIT(0) +#define PHB_DMAMSI_NID_MASK PPC_BITMASK(24,31) +#define PHB_DMAMSI_NID_LSH PPC_BITLSHIFT(31) +#define PHB_CONFIG_DATA 0x130 +#define PHB_LOCK0 0x138 +#define PHB_CONFIG_ADDRESS 0x140 +#define PHB_CA_ENABLE PPC_BIT(0) +#define PHB_CA_BUS_MASK PPC_BITMASK(4,11) +#define PHB_CA_BUS_LSH PPC_BITLSHIFT(11) +#define PHB_CA_DEV_MASK PPC_BITMASK(12,16) +#define PHB_CA_DEV_LSH PPC_BITLSHIFT(16) +#define PHB_CA_FUNC_MASK PPC_BITMASK(17,19) +#define PHB_CA_FUNC_LSH PPC_BITLSHIFT(19) +#define PHB_CA_REG_MASK PPC_BITMASK(20,31) +#define PHB_CA_REG_LSH PPC_BITLSHIFT(31) +#define PHB_CA_PE_MASK PPC_BITMASK(40,47) +#define PHB_CA_PE_LSH PPC_BITLSHIFT(47) +#define PHB_LOCK1 0x148 +#define PHB_IVT_BAR 0x150 +#define PHB_IVT_BAR_ENABLE PPC_BIT(0) +#define PHB_IVT_BASE_ADDRESS_MASK PPC_BITMASK(14,48) +#define PHB_IVT_BASE_ADDRESS_LSH PPC_BITLSHIFT(48) +#define PHB_IVT_LENGTH_MASK PPC_BITMASK(52,63) +#define PHB_IVT_LENGTH_ADDRESS_LSH PPC_BITLSHIFT(63) +#define PHB_RBA_BAR 0x158 +#define PHB_RBA_BAR_ENABLE PPC_BIT(0) +#define PHB_RBA_BASE_ADDRESS_MASK PPC_BITMASK(14,55) +#define PHB_RBA_BASE_ADDRESS_LSH PPC_BITLSHIFT(55) +#define PHB_PHB3_CONFIG 0x160 +#define PHB_PHB3C_64B_TCE_EN PPC_BIT(2) +#define PHB_PHB3C_32BIT_MSI_EN PPC_BIT(8) +#define PHB_PHB3C_64BIT_MSI_EN PPC_BIT(14) +#define PHB_PHB3C_M32_EN PPC_BIT(16) +#define PHB_RTT_BAR 0x168 +#define PHB_RTT_BAR_ENABLE PPC_BIT(0) +#define PHB_RTT_BASE_ADDRESS_MASK PPC_BITMASK(14,46) +#define PHB_RTT_BASE_ADDRESS_LSH PPC_BITLSHIFT(46) +#define PHB_PELTV_BAR 0x188 +#define PHB_PELTV_BAR_ENABLE PPC_BIT(0) +#define PHB_PELTV_BASE_ADDRESS_MASK PPC_BITMASK(14,50) +#define PHB_PELTV_BASE_ADDRESS_LSH PPC_BITLSHIFT(50) +#define PHB_M32_BASE_ADDR 0x190 +#define PHB_M32_BASE_MASK 0x198 +#define PHB_M32_START_ADDR 0x1a0 +#define PHB_PEST_BAR 0x1a8 +#define PHB_PEST_BAR_ENABLE PPC_BIT(0) +#define PHB_PEST_BASE_ADDRESS_MASK PPC_BITMASK(14,51) +#define PHB_PEST_BASE_ADDRESS_LSH PPC_BITLSHIFT(51) +#define PHB_M64_UPPER_BITS 0x1f0 +#define PHB_INTREP_TIMER 0x1f8 +#define PHB_DMARD_SYNC 0x200 +#define PHB_RTC_INVALIDATE 0x208 +#define PHB_RTC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_RTC_INVALIDATE_RID_MASK PPC_BITMASK(16,31) +#define PHB_RTC_INVALIDATE_RID_LSH PPC_BITLSHIFT(31) +#define PHB_TCE_KILL 0x210 +#define PHB_TCE_KILL_ALL PPC_BIT(0) +#define PHB_TCE_SPEC_CTL 0x218 +#define PHB_IODA_ADDR 0x220 +#define PHB_IODA_AD_AUTOINC PPC_BIT(0) +#define PHB_IODA_AD_TSEL_MASK PPC_BITMASK(11,15) +#define PHB_IODA_AD_TSEL_LSH PPC_BITLSHIFT(15) +#define PHB_IODA_AD_TADR_MASK PPC_BITMASK(55,63) +#define PHB_IODA_AD_TADR_LSH PPC_BITLSHIFT(63) +#define PHB_IODA_DATA0 0x228 +#define PHB_FFI_REQUEST 0x238 +#define PHB_FFI_LOCK_CLEAR PPC_BIT(3) +#define PHB_FFI_REQUEST_ISN_MASK PPC_BITMASK(49,59) +#define PHB_FFI_REQUEST_ISN_LSH PPC_BITLSHIFT(59) +#define PHB_FFI_LOCK 0x240 +#define PHB_FFI_LOCK_STATE PPC_BIT(0) +#define PHB_XIVE_UPDATE 0x248 /* Broken in DD1 */ +#define PHB_PHB3_GEN_CAP 0x250 +#define PHB_PHB3_TCE_CAP 0x258 +#define PHB_PHB3_IRQ_CAP 0x260 +#define PHB_PHB3_EEH_CAP 0x268 +#define PHB_IVC_INVALIDATE 0x2a0 +#define PHB_IVC_INVALIDATE_ALL PPC_BIT(0) +#define PHB_IVC_INVALIDATE_SID_MASK PPC_BITMASK(16,31) +#define PHB_IVC_INVALIDATE_SID_LSH PPC_BITLSHIFT(31) +#define PHB_IVC_UPDATE 0x2a8 +#define PHB_IVC_UPDATE_ENABLE_P PPC_BIT(0) +#define PHB_IVC_UPDATE_ENABLE_Q PPC_BIT(1) +#define PHB_IVC_UPDATE_ENABLE_SERVER PPC_BIT(2) +#define PHB_IVC_UPDATE_ENABLE_PRI PPC_BIT(3) +#define PHB_IVC_UPDATE_ENABLE_GEN PPC_BIT(4) +#define PHB_IVC_UPDATE_ENABLE_CON PPC_BIT(5) +#define PHB_IVC_UPDATE_GEN_MATCH_MASK PPC_BITMASK(6, 7) +#define PHB_IVC_UPDATE_GEN_MATCH_LSH PPC_BITLSHIFT(7) +#define PHB_IVC_UPDATE_SERVER_MASK PPC_BITMASK(8, 23) +#define PHB_IVC_UPDATE_SERVER_LSH PPC_BITLSHIFT(23) +#define PHB_IVC_UPDATE_PRI_MASK PPC_BITMASK(24, 31) +#define PHB_IVC_UPDATE_PRI_LSH PPC_BITLSHIFT(31) +#define PHB_IVC_UPDATE_GEN_MASK PPC_BITMASK(32,33) +#define PHB_IVC_UPDATE_GEN_LSH PPC_BITLSHIFT(33) +#define PHB_IVC_UPDATE_P_MASK PPC_BITMASK(34,34) +#define PHB_IVC_UPDATE_P_LSH PPC_BITLSHIFT(34) +#define PHB_IVC_UPDATE_Q_MASK PPC_BITMASK(35,35) +#define PHB_IVC_UPDATE_Q_LSH PPC_BITLSHIFT(35) +#define PHB_IVC_UPDATE_SID_MASK PPC_BITMASK(48,63) +#define PHB_IVC_UPDATE_SID_LSH PPC_BITLSHIFT(63) +#define PHB_PAPR_ERR_INJ_CTL 0x2b0 +#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0) +#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1) +#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2) +#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3) +#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4) +#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5) +#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6) +#define PHB_PAPR_ERR_INJ_ADDR 0x2b8 +#define PHB_PAPR_ERR_INJ_ADDR_MMIO_MASK PPC_BITMASK(16,63) +#define PHB_PAPR_ERR_INJ_ADDR_MMIO_LSH PPC_BITLSHIFT(63) +#define PHB_PAPR_ERR_INJ_MASK 0x2c0 +#define PHB_PAPR_ERR_INJ_MASK_CFG_MASK PPC_BITMASK(4,11) +#define PHB_PAPR_ERR_INJ_MASK_CFG_LSH PPC_BITLSHIFT(11) +#define PHB_PAPR_ERR_INJ_MASK_MMIO_MASK PPC_BITMASK(16,63) +#define PHB_PAPR_ERR_INJ_MASK_MMIO_LSH PPC_BITLSHIFT(63) +#define PHB_ETU_ERR_SUMMARY 0x2c8 + +/* UTL registers */ +#define UTL_SYS_BUS_CONTROL 0x400 +#define UTL_STATUS 0x408 +#define UTL_SYS_BUS_AGENT_STATUS 0x410 +#define UTL_SYS_BUS_AGENT_ERR_SEVERITY 0x418 +#define UTL_SYS_BUS_AGENT_IRQ_EN 0x420 +#define UTL_SYS_BUS_BURST_SZ_CONF 0x440 +#define UTL_REVISION_ID 0x448 +#define UTL_BCLK_DOMAIN_DBG1 0x460 +#define UTL_BCLK_DOMAIN_DBG2 0x468 +#define UTL_BCLK_DOMAIN_DBG3 0x470 +#define UTL_BCLK_DOMAIN_DBG4 0x478 +#define UTL_BCLK_DOMAIN_DBG5 0x480 +#define UTL_BCLK_DOMAIN_DBG6 0x488 +#define UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 +#define UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 +#define UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 +#define UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 +#define UTL_OUT_NP_BUF_ALLOC 0x500 +#define UTL_IN_NP_BUF_ALLOC 0x510 +#define UTL_PCIE_TAGS_ALLOC 0x520 +#define UTL_GBIF_READ_TAGS_ALLOC 0x530 +#define UTL_PCIE_PORT_CONTROL 0x540 +#define UTL_PCIE_PORT_STATUS 0x548 +#define UTL_PCIE_PORT_ERROR_SEV 0x550 +#define UTL_PCIE_PORT_IRQ_EN 0x558 +#define UTL_RC_STATUS 0x560 +#define UTL_RC_ERR_SEVERITY 0x568 +#define UTL_RC_IRQ_EN 0x570 +#define UTL_EP_STATUS 0x578 +#define UTL_EP_ERR_SEVERITY 0x580 +#define UTL_EP_ERR_IRQ_EN 0x588 +#define UTL_PCI_PM_CTRL1 0x590 +#define UTL_PCI_PM_CTRL2 0x598 +#define UTL_GP_CTL1 0x5a0 +#define UTL_GP_CTL2 0x5a8 +#define UTL_PCLK_DOMAIN_DBG1 0x5b0 +#define UTL_PCLK_DOMAIN_DBG2 0x5b8 +#define UTL_PCLK_DOMAIN_DBG3 0x5c0 +#define UTL_PCLK_DOMAIN_DBG4 0x5c8 + +/* PCI-E Stack registers */ +#define PHB_PCIE_SYSTEM_CONFIG 0x600 +#define PHB_PCIE_BUS_NUMBER 0x608 +#define PHB_PCIE_SYSTEM_TEST 0x618 +#define PHB_PCIE_LINK_MANAGEMENT 0x630 +#define PHB_PCIE_LM_LINK_ACTIVE PPC_BIT(8) +#define PHB_PCIE_DLP_TRAIN_CTL 0x640 +#define PHB_PCIE_DLP_TCTX_DISABLE PPC_BIT(1) +#define PHB_PCIE_DLP_TCRX_DISABLED PPC_BIT(16) +#define PHB_PCIE_DLP_INBAND_PRESENCE PPC_BIT(19) +#define PHB_PCIE_DLP_TC_DL_LINKUP PPC_BIT(21) +#define PHB_PCIE_DLP_TC_DL_PGRESET PPC_BIT(22) +#define PHB_PCIE_DLP_TC_DL_LINKACT PPC_BIT(23) +#define PHB_PCIE_SLOP_LOOPBACK_STATUS 0x648 +#define PHB_PCIE_SYS_LINK_INIT 0x668 +#define PHB_PCIE_UTL_CONFIG 0x670 +#define PHB_PCIE_DLP_CONTROL 0x678 +#define PHB_PCIE_UTL_ERRLOG1 0x680 +#define PHB_PCIE_UTL_ERRLOG2 0x688 +#define PHB_PCIE_UTL_ERRLOG3 0x690 +#define PHB_PCIE_UTL_ERRLOG4 0x698 +#define PHB_PCIE_DLP_ERRLOG1 0x6a0 +#define PHB_PCIE_DLP_ERRLOG2 0x6a8 +#define PHB_PCIE_DLP_ERR_STATUS 0x6b0 +#define PHB_PCIE_DLP_ERR_COUNTERS 0x6b8 +#define PHB_PCIE_UTL_ERR_INJECT 0x6c0 +#define PHB_PCIE_TLDLP_ERR_INJECT 0x6c8 +#define PHB_PCIE_LANE_EQ_CNTL0 0x6d0 +#define PHB_PCIE_LANE_EQ_CNTL1 0x6d8 +#define PHB_PCIE_LANE_EQ_CNTL2 0x6e0 +#define PHB_PCIE_LANE_EQ_CNTL3 0x6e8 +#define PHB_PCIE_STRAPPING 0x700 + +/* Fundamental register set B */ +#define PHB_VERSION 0x800 +#define PHB_RESET 0x808 +#define PHB_CONTROL 0x810 +#define PHB_CTRL_IVE_128_BYTES PPC_BIT(24) +#define PHB_AIB_RX_CRED_INIT_TIMER 0x818 +#define PHB_AIB_RX_CMD_CRED 0x820 +#define PHB_AIB_RX_DATA_CRED 0x828 +#define PHB_AIB_TX_CMD_CRED 0x830 +#define PHB_AIB_TX_DATA_CRED 0x838 +#define PHB_AIB_TX_CHAN_MAPPING 0x840 +#define PHB_AIB_TAG_ENABLE 0x858 +#define PHB_AIB_FENCE_CTRL 0x860 +#define PHB_TCE_TAG_ENABLE 0x868 +#define PHB_TCE_WATERMARK 0x870 +#define PHB_TIMEOUT_CTRL1 0x878 +#define PHB_TIMEOUT_CTRL2 0x880 +#define PHB_QUIESCE_DMA_G 0x888 +#define PHB_AIB_TAG_STATUS 0x900 +#define PHB_TCE_TAG_STATUS 0x908 + +/* FIR & Error registers */ +#define PHB_LEM_FIR_ACCUM 0xc00 +#define PHB_LEM_FIR_AND_MASK 0xc08 +#define PHB_LEM_FIR_OR_MASK 0xc10 +#define PHB_LEM_ERROR_MASK 0xc18 +#define PHB_LEM_ERROR_AND_MASK 0xc20 +#define PHB_LEM_ERROR_OR_MASK 0xc28 +#define PHB_LEM_ACTION0 0xc30 +#define PHB_LEM_ACTION1 0xc38 +#define PHB_LEM_WOF 0xc40 +#define PHB_ERR_STATUS 0xc80 +#define PHB_ERR1_STATUS 0xc88 +#define PHB_ERR_INJECT 0xc90 +#define PHB_ERR_LEM_ENABLE 0xc98 +#define PHB_ERR_IRQ_ENABLE 0xca0 +#define PHB_ERR_FREEZE_ENABLE 0xca8 +#define PHB_ERR_AIB_FENCE_ENABLE 0xcb0 +#define PHB_ERR_LOG_0 0xcc0 +#define PHB_ERR_LOG_1 0xcc8 +#define PHB_ERR_STATUS_MASK 0xcd0 +#define PHB_ERR1_STATUS_MASK 0xcd8 + +#define PHB_OUT_ERR_STATUS 0xd00 +#define PHB_OUT_ERR1_STATUS 0xd08 +#define PHB_OUT_ERR_INJECT 0xd10 +#define PHB_OUT_ERR_LEM_ENABLE 0xd18 +#define PHB_OUT_ERR_IRQ_ENABLE 0xd20 +#define PHB_OUT_ERR_FREEZE_ENABLE 0xd28 +#define PHB_OUT_ERR_AIB_FENCE_ENABLE 0xd30 +#define PHB_OUT_ERR_LOG_0 0xd40 +#define PHB_OUT_ERR_LOG_1 0xd48 +#define PHB_OUT_ERR_STATUS_MASK 0xd50 +#define PHB_OUT_ERR1_STATUS_MASK 0xd58 + +#define PHB_INA_ERR_STATUS 0xd80 +#define PHB_INA_ERR1_STATUS 0xd88 +#define PHB_INA_ERR_INJECT 0xd90 +#define PHB_INA_ERR_LEM_ENABLE 0xd98 +#define PHB_INA_ERR_IRQ_ENABLE 0xda0 +#define PHB_INA_ERR_FREEZE_ENABLE 0xda8 +#define PHB_INA_ERR_AIB_FENCE_ENABLE 0xdb0 +#define PHB_INA_ERR_LOG_0 0xdc0 +#define PHB_INA_ERR_LOG_1 0xdc8 +#define PHB_INA_ERR_STATUS_MASK 0xdd0 +#define PHB_INA_ERR1_STATUS_MASK 0xdd8 + +#define PHB_INB_ERR_STATUS 0xe00 +#define PHB_INB_ERR1_STATUS 0xe08 +#define PHB_INB_ERR_INJECT 0xe10 +#define PHB_INB_ERR_LEM_ENABLE 0xe18 +#define PHB_INB_ERR_IRQ_ENABLE 0xe20 +#define PHB_INB_ERR_FREEZE_ENABLE 0xe28 +#define PHB_INB_ERR_AIB_FENCE_ENABLE 0xe30 +#define PHB_INB_ERR_LOG_0 0xe40 +#define PHB_INB_ERR_LOG_1 0xe48 +#define PHB_INB_ERR_STATUS_MASK 0xe50 +#define PHB_INB_ERR1_STATUS_MASK 0xe58 + +/* Performance monitor & Debug registers */ +#define PHB_TRACE_CONTROL 0xf80 +#define PHB_PERFMON_CONFIG 0xf88 +#define PHB_PERFMON_CTR0 0xf90 +#define PHB_PERFMON_CTR1 0xf98 +#define PHB_PERFMON_CTR2 0xfa0 +#define PHB_PERFMON_CTR3 0xfa8 +#define PHB_HOTPLUG_OVERRIDE 0xfb0 +#define PHB_HPOVR_FORCE_RESAMPLE PPC_BIT(9) +#define PHB_HPOVR_PRESENCE_A PPC_BIT(10) +#define PHB_HPOVR_PRESENCE_B PPC_BIT(11) +#define PHB_HPOVR_LINK_ACTIVE PPC_BIT(12) +#define PHB_HPOVR_LINK_BIFURCATED PPC_BIT(13) +#define PHB_HPOVR_LINK_LANE_SWAPPED PPC_BIT(14) + +/* + * IODA2 on-chip tables + */ + +#define IODA2_TBL_LIST 1 +#define IODA2_TBL_LXIVT 2 +#define IODA2_TBL_IVC_CAM 3 +#define IODA2_TBL_RBA 4 +#define IODA2_TBL_RCAM 5 +#define IODA2_TBL_MRT 6 +#define IODA2_TBL_PESTA 7 +#define IODA2_TBL_PESTB 8 +#define IODA2_TBL_TVT 9 +#define IODA2_TBL_TCAM 10 +#define IODA2_TBL_TDR 11 +#define IODA2_TBL_M64BT 16 +#define IODA2_TBL_M32DT 17 +#define IODA2_TBL_PEEV 20 + +/* LXIVT */ +#define IODA2_LXIVT_SERVER_MASK PPC_BITMASK(8,23) +#define IODA2_LXIVT_SERVER_LSH PPC_BITLSHIFT(23) +#define IODA2_LXIVT_PRIORITY_MASK PPC_BITMASK(24,31) +#define IODA2_LXIVT_PRIORITY_LSH PPC_BITLSHIFT(31) +#define IODA2_LXIVT_NODE_ID_MASK PPC_BITMASK(56,63) +#define IODA2_LXIVT_NODE_ID_LSH PPC_BITLSHIFT(63) + +/* IVT */ +#define IODA2_IVT_SERVER_MASK PPC_BITMASK(0,23) +#define IODA2_IVT_SERVER_LSH PPC_BITLSHIFT(23) +#define IODA2_IVT_PRIORITY_MASK PPC_BITMASK(24,31) +#define IODA2_IVT_PRIORITY_LSH PPC_BITLSHIFT(31) +#define IODA2_IVT_GEN_MASK PPC_BITMASK(37,38) +#define IODA2_IVT_GEN_LSH PPC_BITLSHIFT(38) +#define IODA2_IVT_P_MASK PPC_BITMASK(39,39) +#define IODA2_IVT_P_LSH PPC_BITLSHIFT(39) +#define IODA2_IVT_Q_MASK PPC_BITMASK(47,47) +#define IODA2_IVT_Q_LSH PPC_BITLSHIFT(47) +#define IODA2_IVT_PE_MASK PPC_BITMASK(48,63) +#define IODA2_IVT_PE_LSH PPC_BITLSHIFT(63) + +/* TVT */ +#define IODA2_TVT_TABLE_ADDR_MASK PPC_BITMASK(0,47) +#define IODA2_TVT_TABLE_ADDR_LSH PPC_BITLSHIFT(47) +#define IODA2_TVT_NUM_LEVELS_MASK PPC_BITMASK(48,50) +#define IODA2_TVT_NUM_LEVELS_LSH PPC_BITLSHIFT(50) +#define IODA2_TVE_1_LEVEL 0 +#define IODA2_TVE_2_LEVELS 1 +#define IODA2_TVE_3_LEVELS 2 +#define IODA2_TVE_4_LEVELS 3 +#define IODA2_TVE_5_LEVELS 4 +#define IODA2_TVT_TCE_TABLE_SIZE_MASK PPC_BITMASK(51,55) +#define IODA2_TVT_TCE_TABLE_SIZE_LSH PPC_BITLSHIFT(55) +#define IODA2_TVT_IO_PSIZE_MASK PPC_BITMASK(59,63) +#define IODA2_TVT_IO_PSIZE_LSH PPC_BITLSHIFT(63) + +/* PESTA */ +#define IODA2_PESTA_MMIO_FROZEN PPC_BIT(0) + +/* PESTB */ +#define IODA2_PESTB_DMA_STOPPED PPC_BIT(0) + +/* M32DT */ +#define IODA2_M32DT_PE_MASK PPC_BITMASK(8,15) +#define IODA2_M32DT_PE_LSH PPC_BITLSHIFT(15) + +/* M64BT */ +#define IODA2_M64BT_ENABLE PPC_BIT(0) +#define IODA2_M64BT_SINGLE_PE PPC_BIT(1) +#define IODA2_M64BT_BASE_MASK PPC_BITMASK(2,31) +#define IODA2_M64BT_BASE_LSH PPC_BITLSHIFT(31) +#define IODA2_M64BT_MASK_MASK PPC_BITMASK(34,63) +#define IODA2_M64BT_MASK_LSH PPC_BITLSHIFT(63) +#define IODA2_M64BT_SINGLE_BASE_MASK PPC_BITMASK(2,26) +#define IODA2_M64BT_SINGLE_BASE_LSH PPC_BITLSHIFT(26) +#define IODA2_M64BT_PE_HI_MASK PPC_BITMASK(27,31) +#define IODA2_M64BT_PE_HI_LSH PPC_BITLSHIFT(31) +#define IODA2_M64BT_SINGLE_MASK_MASK PPC_BITMASK(34,58) +#define IODA2_M64BT_SINGLE_MASK_LSH PPC_BITLSHIFT(58) +#define IODA2_M64BT_PE_LOW_MASK PPC_BITMASK(59,63) +#define IODA2_M64BT_PE_LOW_LSH PPC_BITLSHIFT(63) + +/* + * IODA2 in-memory tables + */ + +/* PEST + * + * 2x8 bytes entries, PEST0 and PEST1 + */ + +#define IODA2_PEST0_MMIO_CAUSE PPC_BIT(2) +#define IODA2_PEST0_CFG_READ PPC_BIT(3) +#define IODA2_PEST0_CFG_WRITE PPC_BIT(4) +#define IODA2_PEST0_TTYPE_MASK PPC_BITMASK(5,7) +#define IODA2_PEST0_TTYPE_LSH PPC_BITLSHIFT(7) +#define PEST_TTYPE_DMA_WRITE 0 +#define PEST_TTYPE_MSI 1 +#define PEST_TTYPE_DMA_READ 2 +#define PEST_TTYPE_DMA_READ_RESP 3 +#define PEST_TTYPE_MMIO_LOAD 4 +#define PEST_TTYPE_MMIO_STORE 5 +#define PEST_TTYPE_OTHER 7 +#define IODA2_PEST0_CA_RETURN PPC_BIT(8) +#define IODA2_PEST0_UTL_RTOS_TIMEOUT PPC_BIT(8) /* Same bit as CA return */ +#define IODA2_PEST0_UR_RETURN PPC_BIT(9) +#define IODA2_PEST0_UTL_NONFATAL PPC_BIT(10) +#define IODA2_PEST0_UTL_FATAL PPC_BIT(11) +#define IODA2_PEST0_PARITY_UE PPC_BIT(13) +#define IODA2_PEST0_UTL_CORRECTABLE PPC_BIT(14) +#define IODA2_PEST0_UTL_INTERRUPT PPC_BIT(15) +#define IODA2_PEST0_MMIO_XLATE PPC_BIT(16) +#define IODA2_PEST0_IODA2_ERROR PPC_BIT(16) /* Same bit as MMIO xlate */ +#define IODA2_PEST0_TCE_PAGE_FAULT PPC_BIT(18) +#define IODA2_PEST0_TCE_ACCESS_FAULT PPC_BIT(19) +#define IODA2_PEST0_DMA_RESP_TIMEOUT PPC_BIT(20) +#define IODA2_PEST0_AIB_SIZE_INVALID PPC_BIT(21) +#define IODA2_PEST0_LEM_BIT_MASK PPC_BITMASK(26,31) +#define IODA2_PEST0_LEM_BIT_LSH PPC_BITLSHIFT(31) +#define IODA2_PEST0_RID_MASK PPC_BITMASK(32,47) +#define IODA2_PEST0_RID_LSH PPC_BITLSHIFT(47) +#define IODA2_PEST0_MSI_DATA_MASK PPC_BITMASK(48,63) +#define IODA2_PEST0_MSI_DATA_LSH PPC_BITLSHIFT(63) + +#define IODA2_PEST1_FAIL_ADDR_MASK PPC_BITMASK(3,63) +#define IODA2_PEST1_FAIL_ADDR_LSH PPC_BITLSHIFT(63) + + +#endif /* __PHB3_REGS_H */ diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index c488f123619cc..98d75b0661510 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -26,6 +26,7 @@ typedef struct PnvLpcController PnvLpcController; typedef struct PnvPsiController PnvPsiController; typedef struct XICSState XICSState; typedef struct PnvOCCState PnvOCCState; +typedef struct PCIBus PCIBus; /* Should we turn that into a QOjb of some sort ? */ typedef struct PnvChip { @@ -35,6 +36,8 @@ typedef struct PnvChip { ISABus *lpc_bus; PnvPsiController *psi; PnvOCCState *occ; +#define PNV_MAX_CHIP_PHB 4 + PCIBus *phb[PNV_MAX_CHIP_PHB]; } PnvChip; typedef struct PnvSystem { diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index f3f6b472fb6b0..e5d98f591064b 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -206,6 +206,7 @@ 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 icp_irq(ICSState *ics, int server, int nr, uint8_t priority); void ics_simple_write_xive(ICSState *ics, int nr, int server, uint8_t priority, uint8_t saved_priority); From 69942186262f938c29863cdc7eb5f32295281cea Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 46/81] ppc/pnv: Create a default PCI layout This creates a legacy PCIe->PCI bridge under the PHB by default to which a bunch of standard devices are attached. Currently: - VGA (as specified by -vga) - USB (with keyboard and mouse if graphcis is enabled) - AHCI - e1000 This gives us something close to a standard OpenPower platform. Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/pnv.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 84dffab9f1c14..c709c66f62e1f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -53,6 +53,9 @@ #include "hw/char/serial.h" #include "hw/timer/mc146818rtc.h" #include "hw/pci-host/pnv_phb3.h" +#include "hw/usb.h" +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" #include "exec/address-spaces.h" #include "qemu/config-file.h" @@ -86,6 +89,9 @@ typedef struct sPowerNVMachineState sPowerNVMachineState; /** * sPowerNVMachineState: */ + +#define MAX_SATA_PORTS 6 + struct sPowerNVMachineState { /*< private >*/ MachineState parent_obj; @@ -496,6 +502,71 @@ static const VMStateDescription vmstate_powernv = { .minimum_version_id = 1, }; +/* Returns whether we want to use VGA or not */ +static int pnv_vga_init(PCIBus *pci_bus) +{ + switch (vga_interface_type) { + case VGA_NONE: + return false; + case VGA_DEVICE: + return true; + case VGA_STD: + case VGA_VIRTIO: + return pci_vga_init(pci_bus) != NULL; + default: + fprintf(stderr, "This vga model is not supported," + "currently it only supports -vga std\n"); + exit(0); + } +} + +static void pnv_nic_init(PCIBus *pci_bus) +{ + int i; + + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + DeviceState *dev; + PCIDevice *pdev; + Error *err = NULL; + + pdev = pci_create(pci_bus, -1, "e1000"); + dev = &pdev->qdev; + qdev_set_nic_properties(dev, nd); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_report_err(err); + object_unparent(OBJECT(dev)); + exit(1); + } + } +} + +static void pnv_storage_init(PCIBus *pci_bus) +{ + DriveInfo *hd[MAX_SATA_PORTS]; + PCIDevice *ahci; + + /* Add an AHCI device. We use an ICH9 since that's all we have + * at hand for PCI AHCI but it shouldn't really matter + */ + ahci = pci_create_simple(pci_bus, -1, "ich9-ahci"); + g_assert(MAX_SATA_PORTS == ICH_AHCI(ahci)->ahci.ports); + ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); + ahci_ide_create_devs(ahci, hd); +} + +static PCIBus *pnv_create_pci_legacy_bridge(PCIBus *parent, uint8_t chassis_nr) +{ + PCIDevice *dev; + + dev = pci_create_multifunction(parent, 0, false, "pci-bridge"); + qdev_prop_set_uint8(&dev->qdev, "chassis_nr", chassis_nr); + dev->qdev.id = "pci"; + qdev_init_nofail(&dev->qdev); + return pci_bridge_get_sec_bus(PCI_BRIDGE(dev)); +} + static void pnv_lpc_irq_handler_cpld(void *opaque, int n, int level) { #define MAX_ISA_IRQ 16 @@ -579,7 +650,9 @@ static void ppc_powernv_init(MachineState *machine) sPowerNVMachineState *pnv_machine = POWERNV_MACHINE(machine); PnvSystem *sys = &pnv_machine->sys; XICSState *xics; + PCIBus *pbus; ISABus *isa_bus; + bool has_gfx = false; long fw_size; char *filename; void *fdt; @@ -640,6 +713,30 @@ static void ppc_powernv_init(MachineState *machine) /* Create an RTC ISA device too */ rtc_init(isa_bus, 2000, NULL); + /* Add a PCI switch */ + pbus = pnv_create_pci_legacy_bridge(sys->chips[0].phb[0], 128); + + /* Graphics */ + if (pnv_vga_init(pbus)) { + has_gfx = true; + machine->usb |= defaults_enabled() && !machine->usb_disabled; + } + if (machine->usb) { + pci_create_simple(pbus, -1, "nec-usb-xhci"); + if (has_gfx) { + USBBus *usb_bus = usb_bus_find(-1); + + usb_create_simple(usb_bus, "usb-kbd"); + usb_create_simple(usb_bus, "usb-mouse"); + } + } + + /* Add NIC */ + pnv_nic_init(pbus); + + /* Add storage */ + pnv_storage_init(pbus); + if (bios_name == NULL) { bios_name = FW_FILE_NAME; } @@ -714,7 +811,7 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data) NMIClass *nc = NMI_CLASS(oc); mc->init = ppc_powernv_init; - mc->block_default_type = IF_SCSI; + mc->block_default_type = IF_IDE; mc->max_cpus = MAX_CPUS; mc->no_parallel = 1; mc->default_boot_order = NULL; From db8945b8db24cc3622b8eda996d98df3083fd0e2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 47/81] ppc: Update LPCR definitions Includes all the bits up to ISA 2.07 Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index a480e324e5604..09e9894edda33 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -499,12 +499,16 @@ struct ppc_slb_t { #define LPCR_VPM1 (1ull << (63 - 1)) #define LPCR_ISL (1ull << (63 - 2)) #define LPCR_KBV (1ull << (63 - 3)) +#define LPCR_DPFD_SHIFT (63-11) +#define LPCR_DPFD (0x3ull << LPCR_DPFD_SHIFT) +#define LPCR_VRMASD_SHIFT (63-16) +#define LPCR_VRMASD (0x1full << LPCR_VRMASD_SHIFT) +#define LPCR_RMLS_SHIFT (63-37) +#define LPCR_RMLS (0xfull << LPCR_RMLS_SHIFT) #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 LPCR_ONL (1ull << (63-45)) #define LPCR_P7_PECE0 (1ull << (63-49)) #define LPCR_P7_PECE1 (1ull << (63-50)) #define LPCR_P7_PECE2 (1ull << (63-51)) @@ -513,6 +517,12 @@ struct ppc_slb_t { #define LPCR_P8_PECE2 (1ull << (63-49)) #define LPCR_P8_PECE3 (1ull << (63-50)) #define LPCR_P8_PECE4 (1ull << (63-51)) +#define LPCR_MER (1ull << (63-52)) +#define LPCR_TC (1ull << (63-54)) +#define LPCR_LPES0 (1ull << (63-60)) +#define LPCR_LPES1 (1ull << (63-61)) +#define LPCR_RMI (1ull << (63-62)) +#define LPCR_HDICE (1ull << (63-63)) #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) From 5042785ecdd5b503ddbd5886d3c16536d338b2a4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 48/81] ppc: Use a helper to filter writes to LPCR This handles filtering bits based on what is implemented by a given architecture version. We also use it to copy to LPCR some of the relevant 970 HID4 bits. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/helper.h | 1 + target-ppc/mmu-hash64.c | 57 +++++++++++++++++++++++++++ target-ppc/translate_init.c | 78 +++++++++++++++++++++++-------------- 3 files changed, 106 insertions(+), 30 deletions(-) diff --git a/target-ppc/helper.h b/target-ppc/helper.h index b88e47246091f..7a834dd58a4fe 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -16,6 +16,7 @@ DEF_HELPER_1(rfmci, void, env) DEF_HELPER_2(pminsn, void, env, i32) DEF_HELPER_1(rfid, void, env) DEF_HELPER_1(hrfid, void, env) +DEF_HELPER_2(store_lpcr, void, env, tl) #endif DEF_HELPER_1(check_tlb_flush, void, env) #endif diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 9a23cbc51a24b..013b4652b2dd1 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -819,3 +819,60 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, */ tlb_flush(CPU(cpu), 1); } + +void helper_store_lpcr(CPUPPCState *env, target_ulong val) +{ + uint64_t lpcr = 0; + + /* Filter out bits */ + switch(env->mmu_model) { + case POWERPC_MMU_64B: /* 970 */ + if (val & 0x40) { + lpcr |= LPCR_LPES0; + } + if (val & 0x8000000000000000ull) { + lpcr |= LPCR_LPES1; + } + if (val & 0x20) { + lpcr |= (0x4ull << LPCR_RMLS_SHIFT); + } + if (val & 0x4000000000000000ull) { + lpcr |= (0x2ull << LPCR_RMLS_SHIFT); + } + if (val & 0x2000000000000000ull) { + lpcr |= (0x1ull << LPCR_RMLS_SHIFT); + } + env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26; + + /* XXX We could also write LPID from HID4 here + * but since we don't tag any translation on it + * it doesn't actually matter + */ + /* XXX For proper emulation of 970 we also need + * to dig HRMOR out of HID5 + */ + break; + case POWERPC_MMU_2_03: /* P5p */ + lpcr = val & (LPCR_RMLS | LPCR_ILE | + LPCR_LPES0 | LPCR_LPES1 | + LPCR_RMI | LPCR_HDICE); + break; + case POWERPC_MMU_2_06: /* P7 */ + lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | + LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | + LPCR_MER | LPCR_TC | + LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); + break; + case POWERPC_MMU_2_07: /* P8 */ + lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | + LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | + LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | + LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); + break; + default: + ; + } + env->spr[SPR_LPCR] = lpcr; +} diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index b39210f88563f..e9a4d88e17432 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7532,16 +7532,6 @@ static void gen_spr_970_hior(CPUPPCState *env) 0x00000000); } -static void gen_spr_970_lpar(CPUPPCState *env) -{ - /* Logical partitionning */ - /* PPC970: HID4 is effectively the LPCR */ - spr_register(env, SPR_970_HID4, "HID4", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); -} - static void gen_spr_book3s_common(CPUPPCState *env) { spr_register(env, SPR_CTRL, "SPR_CTRL", @@ -7794,15 +7784,6 @@ static void gen_spr_power5p_ear(CPUPPCState *env) 0x00000000); } -static void gen_spr_power5p_lpar(CPUPPCState *env) -{ - /* Logical partitionning */ - spr_register_kvm(env, SPR_LPCR, "LPCR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1); -} - #if !defined(CONFIG_USER_ONLY) static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) { @@ -7814,7 +7795,44 @@ static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) spr_store_dump_spr(sprn); tcg_temp_free(hmer); } + +static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn) +{ +#if defined (TARGET_PPC64) + spr_write_generic(ctx, sprn, gprn); + gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); +#endif +} + +#endif /* !defined(CONFIG_USER_ONLY) */ + +static void gen_spr_970_lpar(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Logical partitionning */ + /* PPC970: HID4 is effectively the LPCR */ + spr_register(env, SPR_970_HID4, "HID4", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_970_hid4, + 0x00000000); +#endif +} + +static void gen_spr_power5p_lpar(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Logical partitionning */ + spr_register_kvm(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_lpcr, + KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1); #endif +} static void gen_spr_book3s_ids(CPUPPCState *env) { @@ -8123,6 +8141,17 @@ static void gen_spr_power8_pspb(CPUPPCState *env) KVM_REG_PPC_PSPB, 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 gen_spr_power8_ic(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -8153,17 +8182,6 @@ static void gen_spr_power8_book4(CPUPPCState *env) #endif } -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); From 4f65144e8259186fed6f903b0a26e4ac726b6ff7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 49/81] ppc: Cosmetic, align some comments Signed-off-by: Benjamin Herrenschmidt --- target-ppc/mmu-hash32.c | 4 ++-- target-ppc/mmu_helper.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 39abb2fd39c49..4b4ad2c9d5fc5 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -36,8 +36,8 @@ struct mmu_ctx_hash32 { hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ - int key; /* Access key */ + int prot; /* Protection bits */ + int key; /* Access key */ }; static int ppc_hash32_pp_prot(int key, int pp, int nx) diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 930e9d31cfde3..8b46f493adfda 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -60,11 +60,11 @@ typedef struct mmu_ctx_t mmu_ctx_t; struct mmu_ctx_t { hwaddr raddr; /* Real address */ hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ + int prot; /* Protection bits */ hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - int nx; /* Non-execute area */ + target_ulong ptem; /* Virtual segment ID | API */ + int key; /* Access key */ + int nx; /* Non-execute area */ }; /* Common routines used by software and hardware TLBs emulation */ From 1fa19bf2b071c5bd3812bd865ea7bf844535e3be Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:53 +0200 Subject: [PATCH 50/81] ppc: Add proper real mode translation support This adds proper support for translating real mode addresses based on the combination of HV and LPCR bits. This handles HRMOR offset for hypervisor real mode, and both RMA and VRMA modes for guest real mode. PAPR mode adjusts the offsets appropriately to match the RMA used in TCG, but we need to limit to the max supported by the implementation (16G). Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/spapr.c | 7 ++ target-ppc/mmu-hash64.c | 146 +++++++++++++++++++++++++++++++----- target-ppc/mmu-hash64.h | 1 + target-ppc/translate_init.c | 10 ++- 4 files changed, 144 insertions(+), 20 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 4f48eacaaed0c..6f654e816c507 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1759,6 +1759,13 @@ static void ppc_spapr_init(MachineState *machine) spapr->vrma_adjust = 1; spapr->rma_size = MIN(spapr->rma_size, 0x10000000); } + + /* Actually we don't support unbounded RMA anymore since we + * added proper emulation of HV mode. The max we can get is + * 16G which also happens to be what we configure for PAPR + * mode so make sure we don't do anything bigger than that + */ + spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); } if (spapr->rma_size > node0_size) { diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 013b4652b2dd1..b7ef8e9f0998a 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -622,12 +622,41 @@ static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, uin env->error_code = 0; } +static int64_t ppc_hash64_get_rmls(CPUPPCState *env) +{ + uint64_t lpcr = env->spr[SPR_LPCR]; + + /* + * This is the full 4 bits encoding of POWER8. Previous + * CPUs only support a subset of these but the filtering + * is done when writing LPCR + */ + switch((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { + case 0x8: /* 32MB */ + return 0x2000000ull; + case 0x3: /* 64MB */ + return 0x4000000ull; + case 0x7: /* 128MB */ + return 0x8000000ull; + case 0x4: /* 256MB */ + return 0x10000000ull; + case 0x2: /* 1GB */ + return 0x40000000ull; + case 0x1: /* 16GB */ + return 0x400000000ull; + default: + /* What to do here ??? */ + return 0; + } +} + int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - ppc_slb_t *slb; + ppc_slb_t *slb_ptr; + ppc_slb_t slb; unsigned apshift; hwaddr pte_offset; ppc_hash_pte64_t pte; @@ -638,11 +667,52 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + /* Note on LPCR usage: 970 uses HID4, but our special variant + * of store_spr copies relevant fields into env->spr[SPR_LPCR]. + * Similarily we filter unimplemented bits when storing into + * LPCR depending on the MMU version. This code can thus just + * use the LPCR "as-is". + */ + /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { - /* Translation is off */ - /* In real mode the top 4 effective address bits are ignored */ + /* Translation is supposedly "off" */ + /* In real mode the top 4 effective address bits are (mostly) ignored */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv) { + if (!(eaddr >> 63)) { + raddr |= env->spr[SPR_HRMOR]; + } + } else { + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + uint32_t vrmasd; + /* VRMA, we make up an SLB entry */ + slb.vsid = SLB_VSID_VRMA; + vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); + slb.esid = SLB_ESID_V; + goto skip_slb; + } + /* RMA. Check bounds in RMLS */ + if (raddr < ppc_hash64_get_rmls(env)) { + raddr |= env->spr[SPR_RMOR]; + } else { + /* The access failed, generate the approriate interrupt */ + if (rwx == 2) { + ppc_hash64_set_isi(cs, env, 0x08000000); + } else { + dsisr = 0x08000000; + if (rwx == 1) { + dsisr |= 0x02000000; + } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + } + return 1; + } + } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE); @@ -650,9 +720,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, } /* 2. Translation is on, so look up the SLB */ - slb = slb_lookup(cpu, eaddr); - - if (!slb) { + slb_ptr = slb_lookup(cpu, eaddr); + if (!slb_ptr) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISEG; env->error_code = 0; @@ -664,14 +733,29 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, return 1; } + /* We grab a local copy because we can modify it (or get a + * pre-cooked one from the VRMA code + */ + slb = *slb_ptr; + + /* 2.5 Clamp L||LP in ISL mode */ + if (env->spr[SPR_LPCR] & LPCR_ISL) { + slb.vsid &= ~SLB_VSID_LLP_MASK; + } + /* 3. Check for segment level no-execute violation */ - if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { + if ((rwx == 2) && (slb.vsid & SLB_VSID_N)) { ppc_hash64_set_isi(cs, env, 0x10000000); return 1; } + /* We go straight here for VRMA translations as none of the + * above applies in that case + */ + skip_slb: + /* 4. Locate the PTE in the hash table */ - pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte); + pte_offset = ppc_hash64_htab_lookup(cpu, &slb, eaddr, &pte); if (pte_offset == -1) { dsisr = 0x40000000; if (rwx == 2) { @@ -688,7 +772,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* Validate page size encoding */ - apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); + apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1); if (!apshift) { error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64 " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset); @@ -701,7 +785,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, /* 5. Check access permissions */ - pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); + pp_prot = ppc_hash64_pte_prot(cpu, &slb, pte); amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = pp_prot & amr_prot; @@ -757,27 +841,51 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr, hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { CPUPPCState *env = &cpu->env; - ppc_slb_t *slb; - hwaddr pte_offset; + ppc_slb_t slb; + ppc_slb_t *slb_ptr; + hwaddr pte_offset, raddr; ppc_hash_pte64_t pte; unsigned apshift; + /* Handle real mode */ if (msr_dr == 0) { - /* In real mode the top 4 effective address bits are ignored */ - return addr & 0x0FFFFFFFFFFFFFFFULL; - } + raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - slb = slb_lookup(cpu, addr); - if (!slb) { + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv & !(addr >> 63)) { + return raddr | env->spr[SPR_HRMOR]; + } + + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + uint32_t vrmasd; + + /* VRMA, we make up an SLB entry */ + slb.vsid = SLB_VSID_VRMA; + vrmasd = (env->spr[SPR_LPCR] & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + slb.vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); + slb.esid = SLB_ESID_V; + goto skip_slb; + } + /* RMA. Check bounds in RMLS */ + if (raddr < ppc_hash64_get_rmls(env)) { + return raddr | env->spr[SPR_RMOR]; + } return -1; } - pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte); + slb_ptr = slb_lookup(cpu, addr); + if (!slb_ptr) { + return -1; + } + slb = *slb_ptr; + skip_slb: + pte_offset = ppc_hash64_htab_lookup(cpu, &slb, addr, &pte); if (pte_offset == -1) { return -1; } - apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); + apshift = hpte_page_shift(slb.sps, pte.pte0, pte.pte1); if (!apshift) { return -1; } diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 9bf8b9b2679ae..17255651ce7d0 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -37,6 +37,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, #define SLB_VSID_B_256M 0x0000000000000000ULL #define SLB_VSID_B_1T 0x4000000000000000ULL #define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL +#define SLB_VSID_VRMA (0x0001FFFFFF000000ULL | SLB_VSID_B_1T) #define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) #define SLB_VSID_KS 0x0000000000000800ULL #define SLB_VSID_KP 0x0000000000000400ULL diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index e9a4d88e17432..f800028e046e5 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8750,11 +8750,19 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu) /* 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 + * a real hypervisor. + * + * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for + * real mode accesses, which thankfully defaults to 0 and isn't + * accessible in guest mode. */ lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1; + /* Set RMLS to the max (ie, 16G) */ + lpcr->default_value &= ~LPCR_RMLS; + lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; + /* 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 From 269a50248b6b4ba2431ada9d6e0d2ece67e4387e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 51/81] ppc: Fix 64K pages support in full emulation We were always advertising only 4K & 16M. Additionally the code wasn't properly matching the page size with the PTE content, which meant we could potentially hit an incorrect PTE if the guest used multiple sizes. Finally, honor the CPU capabilities when decoding the size from the SLB so we don't try to use 64K pages on 970. This still doesn't add support for MPSS (Multiple Page Sizes per Segment) Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 3 +++ target-ppc/mmu-hash64.c | 39 +++++++++++++++++++++++++++++++++---- target-ppc/translate_init.c | 22 ++++++++++++++++++--- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 09e9894edda33..049ff5fe83355 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -114,18 +114,21 @@ enum powerpc_mmu_t { #define POWERPC_MMU_64 0x00010000 #define POWERPC_MMU_1TSEG 0x00020000 #define POWERPC_MMU_AMR 0x00040000 +#define POWERPC_MMU_64K 0x00080000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* Architecture 2.03 and later (has LPCR) */ POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_64K | POWERPC_MMU_AMR | 0x00000003, /* Architecture 2.06 "degraded" (no 1T segments) */ POWERPC_MMU_2_06a = POWERPC_MMU_64 | POWERPC_MMU_AMR | 0x00000003, /* Architecture 2.07 variant */ POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_64K | POWERPC_MMU_AMR | 0x00000004, /* Architecture 2.07 "degraded" (no 1T segments) */ POWERPC_MMU_2_07a = POWERPC_MMU_64 | POWERPC_MMU_AMR diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index b7ef8e9f0998a..11e82f307265f 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -421,9 +421,31 @@ void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token) } } +/* Returns the effective page shift or 0. MPSS isn't supported yet so + * this will always be the slb_pshift or 0 + */ +static uint32_t ppc_hash64_pte_size_decode(uint64_t pte1, uint32_t slb_pshift) +{ + switch(slb_pshift) { + case 12: + return 12; + case 16: + if ((pte1 & 0xf000) == 0x1000) { + return 16; + } + return 0; + case 24: + if ((pte1 & 0xff000) == 0) { + return 24; + } + return 0; + } + return 0; +} + static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, - bool secondary, target_ulong ptem, - ppc_hash_pte64_t *pte) + uint32_t slb_pshift, bool secondary, + target_ulong ptem, ppc_hash_pte64_t *pte) { CPUPPCState *env = &cpu->env; int i; @@ -443,6 +465,13 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, if ((pte0 & HPTE64_V_VALID) && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) && HPTE64_V_COMPARE(pte0, ptem)) { + uint32_t pshift = ppc_hash64_pte_size_decode(pte1, slb_pshift); + if (pshift == 0) { + continue; + } + /* We don't do anything with pshift yet as qemu TLB only deals + * with 4K pages anyway + */ pte->pte0 = pte0; pte->pte1 = pte1; ppc_hash64_stop_access(cpu, token); @@ -496,7 +525,8 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); - pte_offset = ppc_hash64_pteg_search(cpu, hash, 0, ptem, pte); + pte_offset = ppc_hash64_pteg_search(cpu, hash, slb->sps->page_shift, 0, ptem, + pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ @@ -506,7 +536,8 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); - pte_offset = ppc_hash64_pteg_search(cpu, ~hash, 1, ptem, pte); + pte_offset = ppc_hash64_pteg_search(cpu, ~hash, slb->sps->page_shift, 1, + ptem, pte); } return pte_offset; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index f800028e046e5..28d6316776cbc 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -10248,8 +10248,8 @@ static void ppc_cpu_initfn(Object *obj) if (pcc->sps) { env->sps = *pcc->sps; } else if (env->mmu_model & POWERPC_MMU_64) { - /* Use default sets of page sizes */ - static const struct ppc_segment_page_sizes defsps = { + /* Use default sets of page sizes. We don't support MPSS */ + static const struct ppc_segment_page_sizes defsps_4k = { .sps = { { .page_shift = 12, /* 4K */ .slb_enc = 0, @@ -10261,7 +10261,23 @@ static void ppc_cpu_initfn(Object *obj) }, }, }; - env->sps = defsps; + static const struct ppc_segment_page_sizes defsps_64k = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 16, /* 64K */ + .slb_enc = 0x110, + .enc = { { .page_shift = 16, .pte_enc = 1 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, + }; + env->sps = (env->mmu_model & POWERPC_MMU_64K) ? defsps_64k : defsps_4k; } #endif /* defined(TARGET_PPC64) */ From a120903f8685fe123754486fe46731be9b7b4175 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 52/81] ppc/pnv+spapr: Add "ibm,pa-features" property to the device-tree This is currently missing on both PowerNV and PAPR FIXME: Split patch & fix tabs Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/pnv.c | 8 ++++++++ target-ppc/translate_init.c | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index c709c66f62e1f..facc4db834ea8 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -276,6 +276,11 @@ static void powernv_create_cpu_node(void *fdt, CPUState *cs, int smt_threads) uint32_t page_sizes_prop[64]; size_t page_sizes_prop_size; char *nodename; + const uint8_t pa_features[] = { 24, 0, + 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; if ((index % smt_threads) != 0) { return; @@ -350,6 +355,9 @@ static void powernv_create_cpu_node(void *fdt, CPUState *cs, int smt_threads) page_sizes_prop, page_sizes_prop_size))); } + _FDT((fdt_property(fdt, "ibm,pa-features", + pa_features, sizeof(pa_features)))); + /* XXX Just a hack for now */ _FDT((fdt_property_cell(fdt, "ibm,chip-id", 0))); diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 28d6316776cbc..cc7f93fa2f9d8 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -10278,6 +10278,7 @@ static void ppc_cpu_initfn(Object *obj) }, }; env->sps = (env->mmu_model & POWERPC_MMU_64K) ? defsps_64k : defsps_4k; + env->ci_large_pages = env->mmu_model >= POWERPC_MMU_2_06; } #endif /* defined(TARGET_PPC64) */ From 9360dcb7d244be912ee1a866e3de3c3416327cfd Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 53/81] ppc: Fix conditions for delivering external interrupts to a guest External interrupts can bypass the MSR_EE test if they occur in guest mode and LPES0 is clear. In that case they are directed to the hypervisor Signed-off-by: Benjamin Herrenschmidt --- target-ppc/excp_helper.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 3e94abd0c79be..29f5052f2df5d 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -794,6 +794,14 @@ static void ppc_hw_interrupt(CPUPPCState *env) return; } } + /* Extermal interrupt can ignore MSR:EE under some circumstances */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { + bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) { + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); + return; + } + } if (msr_ce != 0) { /* External critical interrupt */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { @@ -839,17 +847,6 @@ static void ppc_hw_interrupt(CPUPPCState *env) powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR); return; } - /* External interrupt */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { - /* Taking an external interrupt does not clear the external - * interrupt status - */ -#if 0 - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); -#endif - powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); - return; - } if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI); From 8d96fe503096a552e8d914262a0ac9bb5a3edd8b Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 54/81] ppc: Enforce setting MSR:EE,IR and DR when MSR:PR is set The architecture specifies that any instruction that sets MSR:PR will also set MSR:EE, IR and DR. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/helper_regs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index 12af61cbf19be..09bc450374a51 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -136,6 +136,10 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, /* Change the exception prefix on PowerPC 601 */ env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000; } + /* If PR=1 then EE, IR and DR must be 1 */ + if ((value >> MSR_PR) & 1) { + value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR); + } #endif env->msr = value; hreg_compute_hflags(env); From 6fd03cba483a244c3543ca141b52d30fdc13f177 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 55/81] ppc: Initial HDEC support The current behaviour isn't completely right, as for the DEC, we don't properly re-arm when wrapping around, but I will fix this in a separate patch. Signed-off-by: Benjamin Herrenschmidt --- hw/ppc/ppc.c | 17 ++++++++++++----- target-ppc/excp_helper.c | 22 ++++++++++++---------- target-ppc/helper.h | 2 ++ target-ppc/timebase_helper.c | 10 ++++++++++ target-ppc/translate_init.c | 30 ++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index c57fdf24d494f..3212480d68183 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -704,9 +704,18 @@ static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu) static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; + /* Raise it */ - LOG_TB("raise decrementer exception\n"); - ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); + LOG_TB("raise hv decrementer exception\n"); + + /* The architecture specifies that we don't deliver HDEC + * interrupts in a PM state. Not only they don't cause a + * wakeup but they also get effectively discarded. + */ + if (!env->in_pm_state) { + ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); + } } static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu) @@ -933,9 +942,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) } /* Create new timer */ tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu); - if (0) { - /* XXX: find a suitable condition to enable the hypervisor decrementer - */ + if (env->has_hv_mode) { tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb, cpu); } else { diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 29f5052f2df5d..1f43b56c39562 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -752,7 +752,6 @@ void ppc_cpu_do_interrupt(CPUState *cs) static void ppc_hw_interrupt(CPUPPCState *env) { PowerPCCPU *cpu = ppc_env_get_cpu(env); - int hdice; #if 0 CPUState *cs = CPU(cpu); @@ -781,15 +780,13 @@ static void ppc_hw_interrupt(CPUPPCState *env) return; } #endif - if (0) { - /* XXX: find a suitable condition to enable the hypervisor mode */ - hdice = env->spr[SPR_LPCR] & 1; - } else { - hdice = 0; - } - if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) { - /* Hypervisor decrementer exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { + /* LPCR will be clear when not supported so this will work */ + bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + if ((msr_ee != 0 || msr_hv == 0) && hdice) { + /* HDEC clears on delivery */ + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR); return; } @@ -941,6 +938,11 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) cs->halted = 1; env->in_pm_state = true; + /* The architecture specifies that HDEC interrupts are + * discarded in PM states + */ + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); + /* 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 diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 7a834dd58a4fe..5e798541eaf0c 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -599,6 +599,8 @@ DEF_HELPER_2(store_601_rtcl, void, env, tl) DEF_HELPER_2(store_601_rtcu, void, env, tl) DEF_HELPER_1(load_decr, tl, env) DEF_HELPER_2(store_decr, void, env, tl) +DEF_HELPER_1(load_hdecr, tl, env) +DEF_HELPER_2(store_hdecr, void, env, tl) DEF_HELPER_2(store_hid0_601, void, env, tl) DEF_HELPER_3(store_403_pbr, void, env, i32, tl) DEF_HELPER_1(load_40x_pit, tl, env) diff --git a/target-ppc/timebase_helper.c b/target-ppc/timebase_helper.c index 3b340d70d1934..aede3e64c85be 100644 --- a/target-ppc/timebase_helper.c +++ b/target-ppc/timebase_helper.c @@ -101,6 +101,16 @@ void helper_store_decr(CPUPPCState *env, target_ulong val) cpu_ppc_store_decr(env, val); } +target_ulong helper_load_hdecr(CPUPPCState *env) +{ + return cpu_ppc_load_hdecr(env); +} + +void helper_store_hdecr(CPUPPCState *env, target_ulong val) +{ + cpu_ppc_store_hdecr(env, val); +} + target_ulong helper_load_40x_pit(CPUPPCState *env) { return load_40x_pit(env); diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index cc7f93fa2f9d8..44e49e606613b 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -293,6 +293,32 @@ static void spr_read_purr (DisasContext *ctx, int gprn, int sprn) { gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } + +/* HDECR */ +static void spr_read_hdecr (DisasContext *ctx, int gprn, int sprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + +static void spr_write_hdecr (DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); + if (ctx->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_stop_exception(ctx); + } +} + #endif #endif @@ -7831,6 +7857,10 @@ static void gen_spr_power5p_lpar(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_lpcr, KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1); + spr_register_hv(env, SPR_HDEC, "HDEC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_hdecr, &spr_write_hdecr, 0); #endif } From 4c80d0e9abeb70c2c2a0421018711bb830ea1b98 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 56/81] ppc: Add placeholder SPRs for DPDES and DHDES on P8 We still need to eventually implement doorbells but at least this makes us not crash when the SPRs are accessed. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 2 ++ target-ppc/translate_init.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 049ff5fe83355..dd6d4a091a97d 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1445,6 +1445,8 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) #define SPR_PSPB (0x09F) +#define SPR_DHDES (0x0B1) +#define SPR_DPDES (0x0B0) #define SPR_DAWR (0x0B4) #define SPR_RPR (0x0BA) #define SPR_CIABR (0x0BB) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 44e49e606613b..7b5a533102a64 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8182,6 +8182,22 @@ static void gen_spr_power8_rpr(CPUPPCState *env) #endif } +static void gen_spr_power8_dbell(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_DPDES, "DPDES", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + spr_register_hv(env, SPR_DHDES, "DHDES", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); +#endif +} + static void gen_spr_power8_ic(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -8267,6 +8283,7 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) gen_spr_power8_ic(env); gen_spr_power8_book4(env); gen_spr_power8_rpr(env); + gen_spr_power8_dbell(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); From f23b7e6b572529f2b8728885de7799ecb1d3b104 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 57/81] ppc: LPCR is a HV resource Don't allow access in guest mode Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate_init.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 7b5a533102a64..12fb2fb328f25 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7853,10 +7853,11 @@ static void gen_spr_power5p_lpar(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* Logical partitionning */ - spr_register_kvm(env, SPR_LPCR, "LPCR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_lpcr, - KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1); + spr_register_kvm_hv(env, SPR_LPCR, "LPCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_lpcr, + KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1); spr_register_hv(env, SPR_HDEC, "HDEC", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, From d5933406029ff6ed89fcae8c00d618791bdbe5c6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:54 +0200 Subject: [PATCH 58/81] ppc: SPURR & PURR are HV writeable and privileged Those are HV writeable, so we provide a dummy write. We eventually need to provide a better emulation but for now this will get us going. We also make them non-user readable as per the architecture. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate_init.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 12fb2fb328f25..446e1aac5bd8b 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -294,6 +294,12 @@ static void spr_read_purr (DisasContext *ctx, int gprn, int sprn) gen_helper_load_purr(cpu_gpr[gprn], cpu_env); } +__attribute__ (( unused )) +static void spr_write_purr (DisasContext *ctx, int gprn, int sprn) +{ + // Temporary placeholder +} + /* HDECR */ static void spr_read_hdecr (DisasContext *ctx, int gprn, int sprn) { @@ -7975,14 +7981,16 @@ static void gen_spr_book3s_purr(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ - spr_register_kvm(env, SPR_PURR, "PURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - KVM_REG_PPC_PURR, 0x00000000); - spr_register_kvm(env, SPR_SPURR, "SPURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - KVM_REG_PPC_SPURR, 0x00000000); + spr_register_kvm_hv(env, SPR_PURR, "PURR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, &spr_write_purr, + KVM_REG_PPC_PURR, 0x00000000); + spr_register_kvm_hv(env, SPR_SPURR, "SPURR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, &spr_write_purr, + KVM_REG_PPC_SPURR, 0x00000000); #endif } From 10561ca7c6168cf35ed48ec0ddccd186e1b951ef Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:55 +0200 Subject: [PATCH 59/81] ppc: Add dummy write to VTB The Hypervisor can write it. We don't handle that properly yet but at least let's not blow up when it is written. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate_init.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 446e1aac5bd8b..39d86847195e1 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -300,6 +300,12 @@ static void spr_write_purr (DisasContext *ctx, int gprn, int sprn) // Temporary placeholder } +__attribute__ (( unused )) +static void spr_write_vtb (DisasContext *ctx, int gprn, int sprn) +{ + // Temporary placeholder +} + /* HDECR */ static void spr_read_hdecr (DisasContext *ctx, int gprn, int sprn) { @@ -8153,10 +8159,11 @@ static void gen_spr_power8_ebb(CPUPPCState *env) /* Virtual Time Base */ static void gen_spr_vtb(CPUPPCState *env) { - spr_register(env, SPR_VTB, "VTB", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_tbl, SPR_NOACCESS, - 0x00000000); + spr_register_hv(env, SPR_VTB, "VTB", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, spr_write_vtb, + 0x00000000); } static void gen_spr_power8_fscr(CPUPPCState *env) From 99ea73619a11552d125355a026932c6de9be4c80 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:55 +0200 Subject: [PATCH 60/81] ppc: Add dummy POWER8 MPPR register Controls the micropartition prefetch, this is pretty much meaningless in full emulation (used for priming the caches on real HW). Signed-off-by: Benjamin Herrenschmidt --- target-ppc/cpu.h | 1 + target-ppc/translate_init.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index dd6d4a091a97d..13b78654c2b82 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1448,6 +1448,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_DHDES (0x0B1) #define SPR_DPDES (0x0B0) #define SPR_DAWR (0x0B4) +#define SPR_MPPR (0x0B8) #define SPR_RPR (0x0BA) #define SPR_CIABR (0x0BB) #define SPR_DAWRX (0x0BC) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 39d86847195e1..7db311be10f34 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8229,6 +8229,11 @@ static void gen_spr_power8_book4(CPUPPCState *env) { /* Add a number of P8 book4 registers */ #if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_MPPR, "MPPR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); spr_register_kvm(env, SPR_ACOP, "ACOP", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, From 2b8c7fcffa2e8d42b7aa6c14358a2120e8188ce7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:55 +0200 Subject: [PATCH 61/81] ppc: Print HSRR0/HSRR1 in "info registers" They are generally useful when debugging HV mode stuff Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 9cc37c4ea297b..162eea9e5e988 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -11352,6 +11352,13 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->spr[SPR_SPRG4], env->spr[SPR_SPRG5], env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]); +#if defined(TARGET_PPC64) + if (env->excp_model == POWERPC_EXCP_POWER7 || + env->excp_model == POWERPC_EXCP_POWER8) { + cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); + } +#endif if (env->excp_model == POWERPC_EXCP_BOOKE) { cpu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx " MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n", From 7397252616c7c846ea3db4ccae1b52cab4020500 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:55 +0200 Subject: [PATCH 62/81] ppc: Add dummy logmpp instruction It's used by KVM for micropartition prefetch Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 162eea9e5e988..baba1855dc3a2 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -3640,6 +3640,16 @@ static void gen_rvwinkle(DisasContext *ctx) gen_stop_exception(ctx); #endif /* defined(CONFIG_USER_ONLY) */ } + +static void gen_logmpp(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + CHK_HV; + /* This doesn't do anything in emulation */ +#endif /* defined(CONFIG_USER_ONLY) */ +} #endif /* #if defined(TARGET_PPC64) */ /*** Floating-point load ***/ @@ -9924,6 +9934,14 @@ 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), + +/* This should be P8 Book4, not ISA207S, but I don't want to add a bit for that + * one dummy instruction. Note also that there's a discrepancy between the + * P8 Book4 which documents it as using RA while KVM implementation uses RB, + * so for now mark both fields as valid + */ +//GEN_HANDLER_E(logmpp, 0x1f, 0x12, 0x1f, 0x03E0F800, PPC_NONE, PPC2_ISA207S), +GEN_HANDLER_E(logmpp, 0x1f, 0x12, 0x1f, 0x03E00000, PPC_NONE, PPC2_ISA207S), #endif GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW), GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW), From 0ba13593006a6c2b16cb506495bcd1f97c012982 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:55 +0200 Subject: [PATCH 63/81] ppc: Add slbfee. instruction Used to lookup SLB entries by address, for some reason it was missing. Signed-off-by: Benjamin Herrenschmidt --- target-ppc/helper.h | 1 + target-ppc/mmu-hash64.c | 25 +++++++++++++++++++++++++ target-ppc/translate.c | 25 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 5e798541eaf0c..bad6edd0234d1 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -552,6 +552,7 @@ DEF_HELPER_FLAGS_2(tlbiva, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_2(load_slb_esid, tl, env, tl) DEF_HELPER_2(load_slb_vsid, tl, env, tl) +DEF_HELPER_2(find_slb_esid, tl, env, tl) DEF_HELPER_FLAGS_1(slbia, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(slbie, TCG_CALL_NO_RWG, void, env, tl) #endif diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 11e82f307265f..b678d7af94767 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -218,6 +218,20 @@ static int ppc_load_slb_vsid(PowerPCCPU *cpu, target_ulong rb, return 0; } +static int ppc_find_slb_esid(CPUPPCState *env, target_ulong rb, + target_ulong *rt) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + ppc_slb_t *slb = slb_lookup(cpu, rb); + + if (!slb) { + return -1; + } + + *rt = slb->vsid; + return 0; +} + void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) { PowerPCCPU *cpu = ppc_env_get_cpu(env); @@ -252,6 +266,17 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) return rt; } +target_ulong helper_find_slb_esid(CPUPPCState *env, target_ulong rb) +{ + target_ulong rt = 0; + + if (ppc_find_slb_esid(env, rb, &rt) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } + return rt; +} + /* * 64-bit hash table MMU handling */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index baba1855dc3a2..7b88f546e094f 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4978,6 +4978,30 @@ static void gen_slbmfev(DisasContext *ctx) cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } + +static void gen_slbfee_(DisasContext *ctx) +{ +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGLabel *l1; + TCGLabel *l2; + + CHK_SV; + + gen_helper_find_slb_esid(cpu_gpr[rS(ctx->opcode)], cpu_env, + cpu_gpr[rB(ctx->opcode)]); + l1 = gen_new_label(); + l2 = gen_new_label(); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rS(ctx->opcode)], -1, l1); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rS(ctx->opcode)], 0); + gen_set_label(l2); +#endif /* defined(CONFIG_USER_ONLY) */ +} #endif /* defined(TARGET_PPC64) */ /*** Lookaside buffer management ***/ @@ -9987,6 +10011,7 @@ GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001, GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x001F0001, PPC_SEGMENT_64B), GEN_HANDLER2(slbmfee, "slbmfee", 0x1F, 0x13, 0x1C, 0x001F0001, PPC_SEGMENT_64B), GEN_HANDLER2(slbmfev, "slbmfev", 0x1F, 0x13, 0x1A, 0x001F0001, PPC_SEGMENT_64B), +GEN_HANDLER2(slbfee_, "slbfee.", 0x1F, 0x13, 0x1E, 0x001F0000, PPC_SEGMENT_64B), #endif GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA), /* XXX Those instructions will need to be handled differently for From 35a238dddb36d9cdf9ff73a7ba3888929365bd65 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 12 May 2016 11:32:55 +0200 Subject: [PATCH 64/81] ppc: Fix CFAR updates We need to remove 4 from the NIP since it's already been moved by 4 by the dispatcher before the helper is called Signed-off-by: Benjamin Herrenschmidt --- target-ppc/translate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 7b88f546e094f..538acf6361c20 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4048,7 +4048,7 @@ static void gen_b(DisasContext *ctx) if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->nip); } - gen_update_cfar(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip - 4); gen_goto_tb(ctx, 0, target); } @@ -4113,7 +4113,7 @@ static inline void gen_bcond(DisasContext *ctx, int type) } tcg_temp_free_i32(temp); } - gen_update_cfar(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip - 4); if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -4231,7 +4231,7 @@ static void gen_rfi(DisasContext *ctx) } /* Restore CPU state */ CHK_SV; - gen_update_cfar(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip - 4); gen_helper_rfi(cpu_env); gen_sync_exception(ctx); #endif @@ -4245,7 +4245,7 @@ static void gen_rfid(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV; - gen_update_cfar(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip - 4); gen_helper_rfid(cpu_env); gen_sync_exception(ctx); #endif From 93f0121aba93a9b347b5f3bc43ac859a08b38d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 65/81] ppc: add IPMI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open PowerNV systems use a BT device to communicate with the BMC. Provide support for it. Signed-off-by: Cédric Le Goater --- default-configs/ppc64-softmmu.mak | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index d30176ed4ce98..e80aa476aadf6 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -7,6 +7,10 @@ CONFIG_VIRTIO_VGA=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y +CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y From 34a3f436a8f6a2025080e114f1221a259ac5bcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 66/81] ipmi: use a file to load SDRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IPMI BMC simulator populates the sdr/sensor tables with a minimal set of entries (Watchdog). But some qemu platforms might want to use extra entries for their custom needs. This patch modifies slighty the initializing routine to take into account a larger set read from a file. The name of the file to use is defined through a new 'sdr' property of the simulator device. Signed-off-by: Cédric Le Goater Acked-by: Corey Minyard Reviewed-by: Marcel Apfelbaum --- hw/ipmi/ipmi_bmc_sim.c | 23 +++++++++++++++++++++-- qemu-options.hx | 11 ++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index dc9c14cd29317..5e6db52a6a1cf 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -27,6 +27,7 @@ #include "qemu/timer.h" #include "hw/ipmi/ipmi.h" #include "qemu/error-report.h" +#include "hw/loader.h" #define IPMI_NETFN_CHASSIS 0x00 @@ -213,6 +214,7 @@ struct IPMIBmcSim { IPMISel sel; IPMISdr sdr; IPMISensor sensors[MAX_SENSORS]; + char *sdr_filename; /* Odd netfns are for responses, so we only need the even ones. */ const IPMINetfn *netfns[MAX_NETFNS / 2]; @@ -1701,22 +1703,33 @@ static void ipmi_sdr_init(IPMIBmcSim *ibs) sdrs_size = sizeof(init_sdrs); sdrs = init_sdrs; + if (ibs->sdr_filename && + !g_file_get_contents(ibs->sdr_filename, (gchar **) &sdrs, &sdrs_size, + NULL)) { + error_report("failed to load sdr file '%s'", ibs->sdr_filename); + sdrs_size = sizeof(init_sdrs); + sdrs = init_sdrs; + } for (i = 0; i < sdrs_size; i += len) { struct ipmi_sdr_header *sdrh; if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) { error_report("Problem with recid 0x%4.4x", i); - return; + break; } sdrh = (struct ipmi_sdr_header *) &sdrs[i]; len = ipmi_sdr_length(sdrh); if (i + len > sdrs_size) { error_report("Problem with recid 0x%4.4x", i); - return; + break; } sdr_add_entry(ibs, sdrh, len, NULL); } + + if (sdrs != init_sdrs) { + g_free(sdrs); + } } static const VMStateDescription vmstate_ipmi_sim = { @@ -1786,12 +1799,18 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } +static Property ipmi_sim_properties[] = { + DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), + DEFINE_PROP_END_OF_LIST(), +}; + static void ipmi_sim_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); dc->realize = ipmi_sim_realize; + dc->props = ipmi_sim_properties; bk->handle_command = ipmi_sim_handle_command; } diff --git a/qemu-options.hx b/qemu-options.hx index 6106520c56e01..7c73e38dcd52d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -388,7 +388,7 @@ possible drivers and properties, use @code{-device help} and @code{-device @var{driver},help}. Some drivers are: -@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}] +@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}] Add an IPMI BMC. This is a simulation of a hardware management interface processor that normally sits on a system. It provides @@ -400,6 +400,15 @@ This address is the BMC's address on the I2C network of management controllers. If you don't know what this means, it is safe to ignore it. +@table @option +@item bmc=@var{id} +The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. +@item slave_addr=@var{val} +Define slave address to use for the BMC. The default is 0x20. +@item sdrfile=@var{file} +file containing raw Sensor Data Records (SDR) data. The default is none. +@end table + @item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] Add a connection to an external IPMI BMC simulator. Instead of From 3dbe9a2ef1cf38d6d14e75a19784a637ab92bf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 67/81] ipmi: provide support for FRUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch provides a simple FRU support for the BMC simulator. FRUs are loaded from a file which name is specified in the object properties, each entry having a fixed size, also specified in the properties. If the file is unknown or not accessible for some reason, a unique entry of 1024 bytes is created as a default. Just enough to start some simulation. These commands complies with the IPMI spec : "34. FRU Inventory Device Commands". Signed-off-by: Cédric Le Goater Acked-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 128 +++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 8 ++- 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 5e6db52a6a1cf..4b887784249b4 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -80,6 +80,9 @@ #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B #define IPMI_CMD_RUN_INIT_AGENT 0x2C +#define IPMI_CMD_GET_FRU_AREA_INFO 0x10 +#define IPMI_CMD_READ_FRU_DATA 0x11 +#define IPMI_CMD_WRITE_FRU_DATA 0x12 #define IPMI_CMD_GET_SEL_INFO 0x40 #define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 #define IPMI_CMD_RESERVE_SEL 0x42 @@ -122,6 +125,13 @@ typedef struct IPMISdr { uint8_t overflow; } IPMISdr; +typedef struct IPMIFru { + char *filename; + unsigned int nentries; + uint16_t areasize; + uint8_t *data; +} IPMIFru; + typedef struct IPMISensor { uint8_t status; uint8_t reading; @@ -213,6 +223,7 @@ struct IPMIBmcSim { IPMISel sel; IPMISdr sdr; + IPMIFru fru; IPMISensor sensors[MAX_SENSORS]; char *sdr_filename; @@ -1322,6 +1333,91 @@ static void get_sel_info(IPMIBmcSim *ibs, rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02); } +static void get_fru_area_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t fru_entry_size; + + fruid = cmd[2]; + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry_size = ibs->fru.areasize; + + rsp_buffer_push(rsp, fru_entry_size & 0xff); + rsp_buffer_push(rsp, fru_entry_size >> 8 & 0xff); + rsp_buffer_push(rsp, 0x0); +} + +static void read_fru_data(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t offset; + int i; + uint8_t *fru_entry; + unsigned int count; + + fruid = cmd[2]; + offset = (cmd[3] | cmd[4] << 8); + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (offset >= ibs->fru.areasize - 1) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; + + count = MIN(cmd[5], ibs->fru.areasize - offset); + + rsp_buffer_push(rsp, count & 0xff); + for (i = 0; i < count; i++) { + rsp_buffer_push(rsp, fru_entry[offset + i]); + } +} + +static void write_fru_data(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + uint8_t fruid; + uint16_t offset; + uint8_t *fru_entry; + unsigned int count; + + fruid = cmd[2]; + offset = (cmd[3] | cmd[4] << 8); + + if (fruid >= ibs->fru.nentries) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (offset >= ibs->fru.areasize - 1) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; + + count = MIN(cmd_len - 5, ibs->fru.areasize - offset); + + memcpy(fru_entry + offset, cmd + 5, count); + + rsp_buffer_push(rsp, count & 0xff); +} + static void reserve_sel(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) @@ -1658,6 +1754,9 @@ static const IPMINetfn app_netfn = { }; static const IPMICmdHandler storage_cmds[] = { + [IPMI_CMD_GET_FRU_AREA_INFO] = { get_fru_area_info, 3 }, + [IPMI_CMD_READ_FRU_DATA] = { read_fru_data, 5 }, + [IPMI_CMD_WRITE_FRU_DATA] = { write_fru_data, 5 }, [IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info }, [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep }, [IPMI_CMD_GET_SDR] = { get_sdr, 8 }, @@ -1760,6 +1859,31 @@ static const VMStateDescription vmstate_ipmi_sim = { } }; +static void ipmi_fru_init(IPMIFru *fru) +{ + int fsize; + int size = 0; + + fsize = get_image_size(fru->filename); + if (fsize > 0) { + size = QEMU_ALIGN_UP(fsize, fru->areasize); + fru->data = g_malloc0(size); + if (load_image_size(fru->filename, fru->data, fsize) != fsize) { + error_report("Could not load file '%s'", fru->filename); + g_free(fru->data); + fru->data = NULL; + } + } + + if (!fru->data) { + /* give one default FRU */ + size = fru->areasize; + fru->data = g_malloc0(size); + } + + fru->nentries = size / fru->areasize; +} + static void ipmi_sim_realize(DeviceState *dev, Error **errp) { IPMIBmc *b = IPMI_BMC(dev); @@ -1782,6 +1906,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) ipmi_sdr_init(ibs); + ipmi_fru_init(&ibs->fru); + ibs->acpi_power_state[0] = 0; ibs->acpi_power_state[1] = 0; @@ -1800,6 +1926,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) } static Property ipmi_sim_properties[] = { + DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), + DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), DEFINE_PROP_END_OF_LIST(), }; diff --git a/qemu-options.hx b/qemu-options.hx index 7c73e38dcd52d..de6f301d23710 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -388,7 +388,7 @@ possible drivers and properties, use @code{-device help} and @code{-device @var{driver},help}. Some drivers are: -@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}] +@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}] Add an IPMI BMC. This is a simulation of a hardware management interface processor that normally sits on a system. It provides @@ -406,7 +406,11 @@ The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. @item slave_addr=@var{val} Define slave address to use for the BMC. The default is 0x20. @item sdrfile=@var{file} -file containing raw Sensor Data Records (SDR) data. The default is none. +file containing raw Sensor Data Records (SDR) data. The default is none. +@item fruareasize=@var{val} +size of a Field Replaceable Unit (FRU) area. The default is 1024. +@item frudatafile=@var{file} +file containing raw Field Replaceable Unit (FRU) inventory data. The default is none. @end table @item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] From 8b3afbaef727b3db32b19dc41f7c20ddcf242af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 68/81] ipmi: introduce an ipmi_bmc_sdr_find() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch exposes a new IPMI routine to query a sdr entry from the sdr table maintained by the IPMI BMC simulator. The API is very similar to the internal sdr_find_entry() routine and should be used the same way to query one or all sdrs. A typical use would be to loop on the sdrs to build nodes of a device tree. Signed-off-by: Cédric Le Goater Acked-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 16 ++++++++++++++++ include/hw/ipmi/ipmi.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 4b887784249b4..27cbe190fc178 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -417,6 +417,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, return 1; } +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec) + +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + unsigned int pos; + + pos = 0; + if (sdr_find_entry(&ibs->sdr, recid, &pos, nextrec)) { + return -1; + } + + *sdr = (const struct ipmi_sdr_compact *) &ibs->sdr.sdr[pos]; + return 0; +} + static void sel_inc_reservation(IPMISel *sel) { sel->reservation++; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 74a2b5af96131..e41321db174d8 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -255,4 +255,6 @@ struct ipmi_sdr_compact { typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; +int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, + const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); #endif From 00788e1af62420c3a0cf8cbf7f63700b4ec91ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 69/81] ipmi: introduce an ipmi_bmc_gen_event() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used to fill the message buffer with custom events expected by some systems. Typically, an Open PowerNV platform guest is notified with an OEM SEL message before a shutdown or a reboot. Signed-off-by: Cédric Le Goater Acked-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 24 ++++++++++++++++++++++++ include/hw/ipmi/ipmi.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 27cbe190fc178..6316755ae6f50 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -474,6 +474,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs) IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); } +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { + return; + } + + if (log && IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { + sel_add_event(ibs, evt); + } + + if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { + goto out; + } + + memcpy(ibs->evtbuf, evt, 16); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + out: + return; +} static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) { diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index e41321db174d8..178e72d72b400 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -257,4 +257,6 @@ typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); +void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); + #endif From fe4ccc9571a7f48de6ce3bec896a180cd1159512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 70/81] ipmi: add SET_SENSOR_READING command (tentative try) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SET_SENSOR_READING is a complex IPMI command (IPMI spec : "35.17 Set Sensor Reading And Event Status Command"). Here is a very minimum framework fitting the Open PowerNV platform needs. This command is used on this platform to set the "System Firmware Progress" sensor and the "Boot Count" sensor. Signed-off-by: Cédric Le Goater --- hw/ipmi/ipmi_bmc_sim.c | 133 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 6316755ae6f50..dcafa3fa0ac0c 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -45,6 +45,7 @@ #define IPMI_CMD_GET_SENSOR_READING 0x2d #define IPMI_CMD_SET_SENSOR_TYPE 0x2e #define IPMI_CMD_GET_SENSOR_TYPE 0x2f +#define IPMI_CMD_SET_SENSOR_READING 0x30 /* #define IPMI_NETFN_APP 0x06 In ipmi.h */ @@ -1744,6 +1745,137 @@ static void get_sensor_type(IPMIBmcSim *ibs, rsp_buffer_push(rsp, sens->evt_reading_type_code); } +static void set_sensor_reading(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMISensor *sens; + uint8_t evd1; + uint8_t evd2; + uint8_t evd3; + + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT); + return; + } + + sens = ibs->sensors + cmd[2]; + + /* Sensor Reading operation */ + switch ((cmd[3]) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* write given value to sensor reading byte */ + sens->reading = cmd[4]; + break; + case 2: + case 3: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + /* Deassertion bits operation */ + switch ((cmd[3] >> 2) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* write given value */ + if (cmd_len > 7) { + sens->deassert_states = cmd[7]; + } + if (cmd_len > 8) { + sens->deassert_states = cmd[8] << 8; + } + + case 2: /* mask on */ + if (cmd_len > 7) { + sens->deassert_states |= cmd[7]; + } + if (cmd_len > 8) { + sens->deassert_states |= cmd[8] << 8; + } + break; + case 3: /* mask off */ + if (cmd_len > 7) { + sens->deassert_states &= cmd[7]; + } + if (cmd_len > 8) { + sens->deassert_states &= (cmd[8] << 8); + } + break; + } + + /* Assertion bits operation */ + switch ((cmd[3] >> 4) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* write given value */ + if (cmd_len > 5) { + sens->assert_states = cmd[5]; + } + if (cmd_len > 6) { + sens->assert_states = cmd[6] << 8; + } + + case 2: /* mask on */ + if (cmd_len > 5) { + sens->assert_states |= cmd[5]; + } + if (cmd_len > 6) { + sens->assert_states |= cmd[6] << 8; + } + break; + case 3: /* mask off */ + if (cmd_len > 5) { + sens->assert_states &= cmd[5]; + } + if (cmd_len > 6) { + sens->assert_states &= (cmd[6] << 8); + } + break; + } + + evd1 = evd2 = evd3 = 0x0; + if (cmd_len > 9) { + evd1 = cmd[9]; + } + if (cmd_len > 10) { + evd2 = cmd[10]; + } + if (cmd_len > 11) { + evd3 = cmd[11]; + } + + /* Event Data Bytes operation */ + switch ((cmd[3] >> 6) & 0x3) { + case 0: /* Do not use the event data in message */ + evd1 = evd2 = evd3 = 0x0; + break; + case 1: /* Write given values to event data bytes excluding bits + * [3:0] Event Data 1. */ + evd1 &= 0xf0; + break; + case 2: /* Write given values to event data bytes including bits + * [3:0] Event Data 1. */ + break; + case 3: + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (IPMI_SENSOR_IS_DISCRETE(sens)) { + unsigned int bit = evd1 & 0xf; + uint16_t mask = (1 << bit); + + if (sens->assert_states & mask & sens->assert_enable) { + gen_event(ibs, cmd[2], 0, evd1, evd2, evd3); + } + + if (sens->deassert_states & mask & sens->deassert_enable) { + gen_event(ibs, cmd[2], 1, evd1, evd2, evd3); + } + } +} static const IPMICmdHandler chassis_cmds[] = { [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities }, @@ -1764,6 +1896,7 @@ static const IPMICmdHandler sensor_event_cmds[] = { [IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 }, [IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 }, [IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 }, + [IPMI_CMD_SET_SENSOR_READING] = { set_sensor_reading, 5 }, }; static const IPMINetfn sensor_event_netfn = { .cmd_nums = ARRAY_SIZE(sensor_event_cmds), From d5cc6c73180bc99240bef0b1c9771c058d52aeaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:56 +0200 Subject: [PATCH 71/81] ppc/pnv: Add reset operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is very similar to what is done on the sPAPR platform. It will allow the device tree to be populated with devices specified on the command line. Next patches will add devices that are currently populated by the firmware (skiboot), rtc and serial devices. Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index facc4db834ea8..b7d0ce4b5ce52 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -96,6 +96,8 @@ struct sPowerNVMachineState { /*< private >*/ MachineState parent_obj; PnvSystem sys; + hwaddr fdt_addr; + void *fdt_skel; }; static XICSState *try_create_xics(const char *type, int nr_servers, @@ -643,6 +645,22 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, } } +static void ppc_powernv_reset(void) +{ + sPowerNVMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); + void *fdt; + + qemu_devices_reset(); + + fdt = g_malloc(FDT_MAX_SIZE); + + _FDT((fdt_open_into(pnv->fdt_skel, fdt, FDT_MAX_SIZE))); + + cpu_physical_memory_write(pnv->fdt_addr, fdt, fdt_totalsize(fdt)); + + g_free(fdt); +} + static void ppc_powernv_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; @@ -787,7 +805,8 @@ static void ppc_powernv_init(MachineState *machine) } fdt = powernv_create_fdt(sys, machine->kernel_cmdline, initrd_base, initrd_size); - cpu_physical_memory_write(FDT_ADDR, fdt, fdt_totalsize(fdt)); + pnv_machine->fdt_skel = fdt; + pnv_machine->fdt_addr = FDT_ADDR; } static int powernv_kvm_type(const char *vm_type) @@ -819,6 +838,7 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data) NMIClass *nc = NMI_CLASS(oc); mc->init = ppc_powernv_init; + mc->reset = ppc_powernv_reset; mc->block_default_type = IF_IDE; mc->max_cpus = MAX_CPUS; mc->no_parallel = 1; From c037a57709f9fe508b40601c75d6559cb2d90196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 72/81] ppc/pnv: scan ISA bus at reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just the basic loop to make sure the correct foreach API is being used. object_child_foreach() also looks promising but I am not sure it fits this need. Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index b7d0ce4b5ce52..678a622ccc300 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -645,10 +645,16 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, } } +static int walk_isa_device(DeviceState *dev, void *fdt) +{ + return 0; +} + static void ppc_powernv_reset(void) { sPowerNVMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); void *fdt; + Object *obj; qemu_devices_reset(); @@ -656,6 +662,14 @@ static void ppc_powernv_reset(void) _FDT((fdt_open_into(pnv->fdt_skel, fdt, FDT_MAX_SIZE))); + obj = object_resolve_path_type("", TYPE_ISA_BUS, NULL); + if (!obj) { + fprintf(stderr, "no isa bus ?!\n"); + return; + } + + qbus_walk_children(BUS(obj), walk_isa_device, NULL, NULL, NULL, fdt); + cpu_physical_memory_write(pnv->fdt_addr, fdt, fdt_totalsize(fdt)); g_free(fdt); From fd17d40cc5dd5389109aa4fbb48fd306a81ee8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 73/81] ppc/pnv: populate device tree for rtc devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the properties of a rtc device when found under the isa bus. The code could be common to any ISA device but we are missing the IO length. Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 678a622ccc300..e34841b532e1a 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -645,8 +645,47 @@ static void pnv_create_chip(PnvSystem *sys, unsigned int chip_no, } } +static int powernv_populate_rtc(ISADevice *d, void *fdt, int lpc_off) +{ + uint32_t io_base = d->ioport_id; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(2) + }; + char *name; + int node; + int ret; + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + g_free(name); + if (node <= 0) { + return node; + } + ret = fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + ret |= fdt_setprop_string(fdt, node, "compatible", "pnpPNP,b00"); + return ret; +} + static int walk_isa_device(DeviceState *dev, void *fdt) { + ISADevice *d = ISA_DEVICE(dev); + Object *obj = OBJECT(dev); + int lpc_off; + + lpc_off = fdt_node_offset_by_compatible(fdt, -1, "ibm,power8-lpc"); + if (lpc_off < 0) { + return lpc_off; + } + + if (object_dynamic_cast(obj, TYPE_MC146818_RTC)) { + powernv_populate_rtc(d, fdt, lpc_off); + } else { + fprintf(stderr, "unknown isa device %s@i%x\n", qdev_fw_name(dev), + d->ioport_id); + } + return 0; } From 5df2dfbda491fac8a1f63feab51cd939f1b5f948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 74/81] ppc/pnv: populate device tree for serial devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This basically does the same as previous patch but for the uart now. Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index e34841b532e1a..b9ee80606cd8f 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -668,6 +668,39 @@ static int powernv_populate_rtc(ISADevice *d, void *fdt, int lpc_off) return ret; } +static int powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off) +{ + const char compatible[] = "ns16550\0pnpPNP,501"; + uint32_t io_base = d->ioport_id; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(8) + }; + char *name; + int node; + int ret; + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + g_free(name); + if (node <= 0) { + return node; + } + ret = fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + ret |= fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); + + ret |= fdt_setprop_cell(fdt, node, "clock-frequency", 1843200); + ret |= fdt_setprop_cell(fdt, node, "current-speed", 115200); + ret |= fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0]); + ret |= fdt_setprop_cell(fdt, node, "interrupt-parent", + fdt_get_phandle(fdt, lpc_off)); + + /* This is needed by Linux */ + ret |= fdt_setprop_string(fdt, node, "device_type", "serial"); + return ret; +} + static int walk_isa_device(DeviceState *dev, void *fdt) { ISADevice *d = ISA_DEVICE(dev); @@ -681,6 +714,8 @@ static int walk_isa_device(DeviceState *dev, void *fdt) if (object_dynamic_cast(obj, TYPE_MC146818_RTC)) { powernv_populate_rtc(d, fdt, lpc_off); + } else if (object_dynamic_cast(obj, TYPE_ISA_SERIAL)) { + powernv_populate_serial(d, fdt, lpc_off); } else { fprintf(stderr, "unknown isa device %s@i%x\n", qdev_fw_name(dev), d->ioport_id); From be14a41479fc65082c648722456b8c3811f3f2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 75/81] ppc/pnv: populate device tree for ipmi bt devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch looks for ipmi-bt devices [1] on the ISA bus and adds the device tree entries depending on the object properties. When the IPMI patchset is merged, qemu will expose such devices on the command line with : -device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10 [1] https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg03168.html Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index b9ee80606cd8f..faedf6fd83847 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -701,6 +701,68 @@ static int powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off) return ret; } +static int powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) +{ + const char compatible[] = "bt\0ipmi-bt"; + uint32_t io_base = 0x0; + uint32_t io_regs[] = { + cpu_to_be32(1), + cpu_to_be32(io_base), + cpu_to_be32(3) + }; + uint32_t irq; + char *name; + int node; + int ret; + Error *err = NULL; + + io_base = object_property_get_int(OBJECT(d), "ioport", &err); + if (err) { + return -1; + } + io_regs[1] = cpu_to_be32(io_base); + + irq = object_property_get_int(OBJECT(d), "irq", &err); + if (err) { + return -1; + } + + name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); + node = fdt_add_subnode(fdt, lpc_off, name); + g_free(name); + if (node <= 0) { + return node; + } + ret = fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); + ret |= fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); + + /* Mark it as reserved to avoid Linux trying to claim it */ + ret |= fdt_setprop_string(fdt, node, "status", "reserved"); + ret |= fdt_setprop_cell(fdt, node, "interrupts", irq); + ret |= fdt_setprop_cell(fdt, node, "interrupt-parent", + fdt_get_phandle(fdt, lpc_off)); + return ret; +} +#if 0 +static DeviceState *ipmi_bt_create(BusState *bus, const char *bmcname, + Error **errp) +{ + Error *err = NULL; + DeviceState *dev; + + dev = qdev_create(bus, "isa-ipmi-bt"); + qdev_prop_set_int32(dev, "irq", 10); + qdev_prop_set_string(dev, "bmc", bmcname); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + object_unparent(OBJECT(dev)); + return NULL; + } + + return dev; +} +#endif static int walk_isa_device(DeviceState *dev, void *fdt) { ISADevice *d = ISA_DEVICE(dev); @@ -716,6 +778,8 @@ static int walk_isa_device(DeviceState *dev, void *fdt) powernv_populate_rtc(d, fdt, lpc_off); } else if (object_dynamic_cast(obj, TYPE_ISA_SERIAL)) { powernv_populate_serial(d, fdt, lpc_off); + } else if (object_dynamic_cast(obj, "isa-ipmi-bt")) { + powernv_populate_ipmi_bt(d, fdt, lpc_off); } else { fprintf(stderr, "unknown isa device %s@i%x\n", qdev_fw_name(dev), d->ioport_id); From 20e74351206f065815e14811d4cdba49765ca207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 76/81] ppc/pnv: add initial IPMI sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds basic IPMI sensors used by the Open PowerNV platform and populates the device tree as expected by the guest. The array 'pnv_init_sdrs' and 'struct sdr_compact' could probably be merged in the IPMI BMC simulator. Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index faedf6fd83847..e3612a4b8be10 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -34,6 +34,7 @@ #include "sysemu/cpus.h" #include "sysemu/kvm.h" #include "sysemu/numa.h" +#include "sysemu/device_tree.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "qom/cpu.h" @@ -64,6 +65,7 @@ #include "hw/nmi.h" #include "hw/compat.h" +#include "hw/ipmi/ipmi.h" #include @@ -701,6 +703,55 @@ static int powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off) return ret; } +static int powernv_populate_ipmi_sensor(Object *objbmc, void *fdt) +{ + int node; + int ret; + int i; + const struct ipmi_sdr_compact *sdr; + + node = qemu_fdt_add_subnode(fdt, "/bmc"); + if (node <= 0) { + return -1; + } + + ret = fdt_setprop_string(fdt, node, "name", "bmc"); + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + node = fdt_add_subnode(fdt, node, "sensors"); + if (node <= 0) { + return -1; + } + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + for (i = 0; !ipmi_bmc_sdr_find(IPMI_BMC(objbmc), i, &sdr, NULL); i++) { + int snode; + char sensor_name[32]; + + sprintf(sensor_name, "sensor@%x", sdr->sensor_owner_number); + snode = fdt_add_subnode(fdt, node, sensor_name); + if (snode <= 0) { + return -1; + } + + ret |= fdt_setprop_cell(fdt, snode, "reg", sdr->sensor_owner_number); + ret |= fdt_setprop_string(fdt, snode, "name", "sensor"); + ret |= fdt_setprop_string(fdt, snode, "compatible", "ibm,ipmi-sensor"); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-sensor-reading-type", + sdr->reading_type); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-entity-id", + sdr->entity_id); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-entity-instance", + sdr->entity_instance); + ret |= fdt_setprop_cell(fdt, snode, "ipmi-sensor-type", + sdr->sensor_type); + } + + return ret; +} + static int powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) { const char compatible[] = "bt\0ipmi-bt"; @@ -715,6 +766,7 @@ static int powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) int node; int ret; Error *err = NULL; + Object *obj; io_base = object_property_get_int(OBJECT(d), "ioport", &err); if (err) { @@ -741,6 +793,17 @@ static int powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) ret |= fdt_setprop_cell(fdt, node, "interrupts", irq); ret |= fdt_setprop_cell(fdt, node, "interrupt-parent", fdt_get_phandle(fdt, lpc_off)); + + /* a ipmi bt device necessarily comes with a bmc : + * -device ipmi-bmc-sim,id=bmc0 + */ + obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); + if (obj) { + ret = powernv_populate_ipmi_sensor(obj, fdt); + } else { + fprintf(stderr, "bmc simulator is not running !?"); + } + return ret; } #if 0 From 2d476769640f50ea54bcf0416f033d8994b3fc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 77/81] ppc/pnv: generate an OEM SEL event on shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open PowerNV systems expect to be notified with such an event before a shutdown or a reboot. An OEM SEL message is sent with specific identifiers and a user data containing the request : OFF or REBOOT. We need to find a home for 'struct oem_sel' Signed-off-by: Cédric Le Goater --- hw/ppc/pnv.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index e3612a4b8be10..420cf24024b35 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -100,6 +100,7 @@ struct sPowerNVMachineState { PnvSystem sys; hwaddr fdt_addr; void *fdt_skel; + Notifier powerdown_notifier; }; static XICSState *try_create_xics(const char *type, int nr_servers, @@ -851,6 +852,55 @@ static int walk_isa_device(DeviceState *dev, void *fdt) return 0; } +/* + * OEM SEL Event data packet sent by BMC in response of a Read Event + * Message Buffer command + */ +struct oem_sel { + /* SEL header */ + uint8_t id[2]; + uint8_t type; + uint8_t timestamp[4]; + uint8_t manuf_id[3]; + /* OEM SEL data (6 bytes) follows */ + uint8_t netfun; + uint8_t cmd; + uint8_t data[4]; +}; + +#define SOFT_OFF 0x00 +#define SOFT_REBOOT 0x01 + +static void pnv_gen_oem_sel(uint8_t reboot) +{ + Object *obj; + uint8_t evt[16]; + struct oem_sel sel = { + .id = { 0x55 , 0x55 }, + .type = 0xC0, /* OEM */ + .manuf_id = { 0x0, 0x0, 0x0 }, + .timestamp = { 0x0, 0x0, 0x0, 0x0 }, + .netfun = 0x3a, /* IBM */ + .cmd = 0x04, /* AMI OEM SEL Power Notification */ + .data = { reboot, 0xFF, 0xFF, 0xFF }, + }; + + obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); + if (!obj) { + fprintf(stderr, "bmc simulator is not running\n"); + return; + } + + memcpy(evt, &sel, 16); + ipmi_bmc_gen_event(IPMI_BMC(obj), evt, 0 /* do not log the event */); +} + +static void pnv_powerdown_notify(Notifier *n, void *opaque) +{ + pnv_gen_oem_sel(SOFT_OFF); +} + + static void ppc_powernv_reset(void) { sPowerNVMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); @@ -1022,6 +1072,9 @@ static void ppc_powernv_init(MachineState *machine) initrd_base, initrd_size); pnv_machine->fdt_skel = fdt; pnv_machine->fdt_addr = FDT_ADDR; + + pnv_machine->powerdown_notifier.notify = pnv_powerdown_notify; + qemu_register_powerdown_notifier(&pnv_machine->powerdown_notifier); } static int powernv_kvm_type(const char *vm_type) From bd5a6558dd003d4084b371933fb66fbfcc392bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 12 May 2016 11:32:57 +0200 Subject: [PATCH 78/81] ppc/pnv: add some XSCOM addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These addresses are used by skiboot when the CAPP partition is preloaded. Just return 0 to pretend success on the write. Signed-off-by: Cédric Le Goater --- hw/ppc/pnv_xscom.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index 9e9a8bee272e5..49a5d18064452 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -205,6 +205,11 @@ static void xscom_write(void *opaque, hwaddr addr, uint64_t val, case 0x2020009: /* ADU stuff */ case 0x202000f: /* ADU stuff */ break; + case 0x2013028: /* CAPP stuff */ + case 0x201302a: /* CAPP stuff */ + case 0x2013801: /* CAPP stuff */ + case 0x2013802: /* CAPP stuff */ + break; default: if (!xscom_dispatch_write(s, pcba, val)) { xscom_complete(HMER_XSCOM_FAIL | HMER_XSCOM_DONE); From e218fd3ba945bb0f483f5f12bedbb74d897cd5b9 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Mon, 26 Oct 2015 03:53:45 -0400 Subject: [PATCH 79/81] PPC: fix HISI/HDSI generation. These can only be generated if the thread is not in HV mode, even with VPM. 5.7.2 of PowerISA_v2.07: "If the thread is not in hypervisor state, and either address translation is enabled and VPM1=1, or address translation is disabled and VPM0=1, conditions that would have caused a Data Storage or an Instruction Storage interrupt if the affected memory were not virtu- alized instead cause a Hypervisor Data Storage or a Hypervisor Instruction Storage interrupt respectively." 6.5.16: "A Hypervisor Data Storage interrupt occurs when no higher priority exception exists, the thread is not in hypervisor state, and ..." Signed-off-by: Andrei Warkentin --- target-ppc/mmu-hash64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index b678d7af94767..99ac6084f3382 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -666,7 +666,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, uin } else { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); } - if (vpm && msr_hv) { + if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HDSI; env->spr[SPR_HDAR] = dar; env->spr[SPR_HDSISR] = dsisr; From 67ad03a939de5c98e35d1934fa4cb4d349313438 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Wed, 11 May 2016 01:12:37 -0400 Subject: [PATCH 80/81] PPC: don't spam about invalid/priviledged SPR accesses Don't log if invalid/priviledged SPR accesses are done with MSR.PR if MSR.HV is set, as that would indicate a hypervisor running a guest in PR mode for full virtualization. IMO I'd just drop the logs period if MSR.PR is set... Signed-off-by: Andrei Warkentin --- target-ppc/translate.c | 57 ++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 538acf6361c20..20b0bd5fdb419 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4458,8 +4458,11 @@ static inline void gen_op_mfspr(DisasContext *ctx) /* This is a hack to avoid warnings when running Linux: * this OS breaks the PowerPC virtualisation model, * allowing userland application to read the PVR + * + * Also avoid spamming if we're obviously running a HV + * that does full guest virtualization via PR. */ - if (sprn != SPR_PVR) { + if (sprn != SPR_PVR && !ctx->hv) { fprintf(stderr, "Trying to read privileged spr %d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); if (qemu_log_separate()) { @@ -4476,12 +4479,18 @@ static inline void gen_op_mfspr(DisasContext *ctx) /* This is a nop */ return; } - /* Not defined */ - fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - if (qemu_log_separate()) { - qemu_log("Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + + /* + * Not defined - don't spam if we're in PR and HV mode, + * which would indicate that a HV is running a guest. + */ + if (!ctx->hv || !ctx->pr) { + fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } } /* The behaviour depends on MSR:PR and SPR# bit 0x10, @@ -4622,12 +4631,17 @@ static void gen_mtspr(DisasContext *ctx) if (likely(write_cb != SPR_NOACCESS)) { (*write_cb)(ctx, sprn, rS(ctx->opcode)); } else { - /* Privilege exception */ - fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); - if (qemu_log_separate()) { - qemu_log("Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + /* + * Privilege exception - don't spam if we're in PR and HV mode, + * which would indicate that a HV is running a guest. + */ + if (!ctx->hv) { + fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + if (qemu_log_separate()) { + qemu_log("Trying to write privileged spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } } gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } @@ -4639,13 +4653,18 @@ static void gen_mtspr(DisasContext *ctx) return; } - /* Not defined */ - if (qemu_log_separate()) { - qemu_log("Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + /* + * Not defined - don't spam if we're in PR and HV mode, + * which would indicate that a HV is running a guest. + */ + if (!ctx->hv || !ctx->pr) { + if (qemu_log_separate()) { + qemu_log("Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); + } + fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); } - fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->nip - 4); /* The behaviour depends on MSR:PR and SPR# bit 0x10, * it can generate a priv, a hv emu or a no-op From 8a4db7afe9434bc90e356b421456d875455393b7 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Wed, 8 Jun 2016 22:17:18 -0400 Subject: [PATCH 81/81] pnv: allow initrd up to 2GB Allow attaching whole ISOs. Signed-off-by: Andrei Warkentin --- hw/ppc/pnv.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 420cf24024b35..7d7c4ed4eed0e 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1053,12 +1053,9 @@ static void ppc_powernv_init(MachineState *machine) /* 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 + 0x80000000); // 2GB max if (initrd_size < 0) { fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", initrd_filename);