From a4e044c30e0044947fd0d69b1888d916f96bba6b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 14 Mar 2013 17:59:29 +0000 Subject: [PATCH 01/58] pseries: Fix breakage in CPU QOM conversion Commit 259186a7d2f7184efc96ae99bc5658e6159f53ad "cpu: Move halted and interrupt_request fields to CPUState" broke the pseries machine. That's because it uses CPU() instead of ENV_GET_CPU() to convert from the global first_cpu pointer (still a CPUArchState) to a CPUState. This patch fixes the breakage. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f355a9bb84..d45098d096 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -629,7 +629,7 @@ static void ppc_spapr_reset(void) spapr->rtas_size); /* Set up the entry state */ - first_cpu_cpu = CPU(first_cpu); + first_cpu_cpu = ENV_GET_CPU(first_cpu); first_cpu->gpr[3] = spapr->fdt_addr; first_cpu->gpr[5] = 0; first_cpu_cpu->halted = 0; From 89dfd6e1b3c0b31ef700203808be2a9a71947d1d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Mar 2013 15:53:25 +0000 Subject: [PATCH 02/58] pseries: Remove "busname" property for PCI host bridge Currently the "spapr-pci-host-bridge" device has a "busname" property which can be used to override the default assignment of qbus names for the bus subordinate to the PHB. We use that for the default primary PCI bus, to make libvirt happy, which expects there to be a bus named simply "pci". The default qdev core logic would name the bus "pci.0", and the pseries code would otherwise name it "pci@800000020000000" which is the name it is given in the device tree based on its BUID. The "busname" property is rather clunky though, so this patch simplifies things by just using a special case hack for the default PHB, setting busname to "pci" when index=0. Signed-off-by: Alexey Kardashevskiy Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 2 +- hw/spapr_pci.c | 30 ++++++++++++++++++++++-------- hw/spapr_pci.h | 4 +--- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d45098d096..74ae83bb77 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -856,7 +856,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Set up PCI */ spapr_pci_rtas_init(); - phb = spapr_create_phb(spapr, 0, "pci"); + phb = spapr_create_phb(spapr, 0); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 36adbc5592..42c8b61c74 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -518,6 +518,7 @@ static int spapr_phb_init(SysBusDevice *s) { sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); PCIHostState *phb = PCI_HOST_BRIDGE(s); + const char *busname; char *namebuf; int i; PCIBus *bus; @@ -575,9 +576,6 @@ static int spapr_phb_init(SysBusDevice *s) } sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); - if (!sphb->busname) { - sphb->busname = sphb->dtbusname; - } namebuf = alloca(strlen(sphb->dtbusname) + 32); @@ -621,7 +619,26 @@ static int spapr_phb_init(SysBusDevice *s) &sphb->msiwindow); } - bus = pci_register_bus(DEVICE(s), sphb->busname, + /* + * Selecting a busname is more complex than you'd think, due to + * interacting constraints. If the user has specified an id + * explicitly for the phb , then we want to use the qdev default + * of naming the bus based on the bridge device (so the user can + * then assign devices to it in the way they expect). For the + * first / default PCI bus (index=0) we want to use just "pci" + * because libvirt expects there to be a bus called, simply, + * "pci". Otherwise, we use the same name as in the device tree, + * since it's unique by construction, and makes the guest visible + * BUID clear. + */ + if (s->qdev.id) { + busname = NULL; + } else if (sphb->index == 0) { + busname = "pci"; + } else { + busname = sphb->dtbusname; + } + bus = pci_register_bus(DEVICE(s), busname, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS); @@ -663,7 +680,6 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_STRING("busname", sPAPRPHBState, busname), DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), @@ -694,14 +710,12 @@ static const TypeInfo spapr_phb_info = { .class_init = spapr_phb_class_init, }; -PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, - const char *busname) +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) { DeviceState *dev; dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_string(dev, "busname", busname); qdev_init_nofail(dev); return PCI_HOST_BRIDGE(dev); diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 8bb3c62c3d..8bd8a663c5 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -39,7 +39,6 @@ typedef struct sPAPRPHBState { int32_t index; uint64_t buid; - char *busname; char *dtbusname; MemoryRegion memspace, iospace; @@ -82,8 +81,7 @@ static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); } -PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, - const char *busname); +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index); int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, From c6304a4a6822f0e3e45c94b89d4e328057355683 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Mar 2013 15:53:27 +0000 Subject: [PATCH 03/58] target-ppc: Remove CONFIG_PSERIES dependency in kvm.c target-ppc/kvm.c has an #ifdef on CONFIG_PSERIES, for the handling of KVM exits due to a PAPR hypercall from the guest. However, since commit e4c8b28cde12d01ada8fe869567dc5717a2dfcb7 "ppc: express FDT dependency of pSeries and e500 boards via default-configs/", this hasn't worked properly. That patch altered the configuration setup so that although CONFIG_PSERIES is visible from the Makefiles, it is not visible from C files. This broke the pseries machine when KVM is in use. This patch makes a quick and dirty fix, by removing the CONFIG_PSERIES dependency, replacing it with TARGET_PPC64 (since removing it entirely leads to type mismatch errors). Technically this breaks the build when configured with --disable-fdt, since that disables CONFIG_PSERIES on TARGET_PPC64. However, it turns out the build was already broken in that case, so this fixes pseries kvm without breaking anything extra. I'm looking into how to fix that build breakage, but I don't think that need delay applying this patch. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index e663ff0acb..9870d60884 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1077,7 +1077,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) dprintf("handle halt\n"); ret = kvmppc_handle_halt(cpu); break; -#ifdef CONFIG_PSERIES +#if defined(TARGET_PPC64) case KVM_EXIT_PAPR_HCALL: dprintf("handle PAPR hypercall\n"); run->papr_hcall.ret = spapr_hypercall(cpu, From 7b5651605836fd29572fd4c8769af5378d351712 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Mar 2013 15:53:28 +0000 Subject: [PATCH 04/58] pseries: Move XICS initialization before cpu initialization Currently, the pseries machine initializes the cpus, then the XICS interrupt controller. However, to support the upcoming in-kernel XICS implementation we will need to initialize the irq controller before the vcpus. This patch makes the necesssary rearrangement. This means the xics init code can no longer auto-detect the number of cpus ("interrupt servers" in XICS terminology) and so we must pass that in explicitly from the platform code. Signed-off-by: Michael Ellerman Signed-off-by: Ben Herrenschmidt Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 12 +++++----- hw/ppc/xics.c | 59 ++++++++++++++++++++++---------------------------- hw/xics.h | 3 ++- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 74ae83bb77..7b2a11fbe4 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -779,6 +779,11 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) spapr->htab_shift++; } + /* Set up Interrupt Controller before we create the VCPUs */ + spapr->icp = xics_system_init(smp_cpus * kvmppc_smt_threads() / smp_threads, + XICS_IRQS); + spapr->next_irq = XICS_IRQ_BASE; + /* init CPUs */ if (cpu_model == NULL) { cpu_model = kvm_enabled() ? "host" : "POWER7"; @@ -791,6 +796,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } env = &cpu->env; + xics_cpu_setup(spapr->icp, cpu); + /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); @@ -830,11 +837,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } g_free(filename); - - /* Set up Interrupt Controller */ - spapr->icp = xics_system_init(XICS_IRQS); - spapr->next_irq = XICS_IRQ_BASE; - /* Set up EPOW events infrastructure */ spapr_events_init(spapr); diff --git a/hw/ppc/xics.c b/hw/ppc/xics.c index c3ef12fff4..374da5bbfd 100644 --- a/hw/ppc/xics.c +++ b/hw/ppc/xics.c @@ -521,46 +521,39 @@ static void xics_reset(void *opaque) } } -struct icp_state *xics_system_init(int nr_irqs) +void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + struct icp_server_state *ss = &icp->ss[cs->cpu_index]; + + assert(cs->cpu_index < icp->nr_servers); + + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; + + default: + fprintf(stderr, "XICS interrupt controller does not support this CPU " + "bus model\n"); + abort(); + } +} + +struct icp_state *xics_system_init(int nr_servers, int nr_irqs) { - CPUPPCState *env; - CPUState *cpu; - int max_server_num; struct icp_state *icp; struct ics_state *ics; - max_server_num = -1; - for (env = first_cpu; env != NULL; env = env->next_cpu) { - cpu = CPU(ppc_env_get_cpu(env)); - if (cpu->cpu_index > max_server_num) { - max_server_num = cpu->cpu_index; - } - } - icp = g_malloc0(sizeof(*icp)); - icp->nr_servers = max_server_num + 1; + icp->nr_servers = nr_servers; icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); - for (env = first_cpu; env != NULL; env = env->next_cpu) { - cpu = CPU(ppc_env_get_cpu(env)); - struct icp_server_state *ss = &icp->ss[cpu->cpu_index]; - - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; - - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; - - default: - hw_error("XICS interrupt model does not support this CPU bus " - "model\n"); - exit(1); - } - } - ics = g_malloc0(sizeof(*ics)); ics->nr_irqs = nr_irqs; ics->offset = XICS_IRQ_BASE; diff --git a/hw/xics.h b/hw/xics.h index c3bf0083e2..6bce0424df 100644 --- a/hw/xics.h +++ b/hw/xics.h @@ -35,6 +35,7 @@ struct icp_state; qemu_irq xics_get_qirq(struct icp_state *icp, int irq); void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi); -struct icp_state *xics_system_init(int nr_irqs); +struct icp_state *xics_system_init(int nr_servers, int nr_irqs); +void xics_cpu_setup(struct icp_state *icp, PowerPCCPU *cpu); #endif /* __XICS_H__ */ From d6478bc7e92db4669fac701d7bb8c51756b61d8a Mon Sep 17 00:00:00 2001 From: Fabien Chouteau Date: Tue, 19 Mar 2013 07:41:53 +0000 Subject: [PATCH 05/58] PPC/GDB: handle read and write of fpscr Although the support of this register may be uncomplete, there are no reason to prevent the debugger from reading or writing it. Signed-off-by: Fabien Chouteau Signed-off-by: Alexander Graf --- gdbstub.c | 3 ++- target-ppc/cpu.h | 2 ++ target-ppc/fpu_helper.c | 5 +++++ target-ppc/translate_init.c | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 43b7d4d00f..5167c64bdf 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -781,7 +781,8 @@ static int cpu_gdb_write_register(CPUPPCState *env, uint8_t *mem_buf, int n) /* fpscr */ if (gdb_has_xml) return 0; - return 4; + store_fpscr(env, ldtul_p(mem_buf), 0xffffffff); + return sizeof(target_ulong); } } return 0; diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 6886666d6e..9b9c784366 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1180,6 +1180,8 @@ void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); #endif #endif +void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask); + static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) { uint64_t gprv; diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index 9d67926209..9e779eace6 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -463,6 +463,11 @@ void helper_store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask) fpscr_set_rounding_mode(env); } +void store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask) +{ + helper_store_fpscr(env, arg, mask); +} + void helper_float_check_status(CPUPPCState *env) { if (env->exception_index == POWERPC_EXCP_PROGRAM && diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 15eebe9177..bd5033c415 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7693,7 +7693,7 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 8; } if (n == 32) { - /* FPSCR not implemented */ + helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff); return 4; } return 0; From 9baea4a303323932ec913728173ea38a4af05f3e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:03 +0000 Subject: [PATCH 06/58] target-ppc: Remove vestigial PowerPC 620 support The PowerPC 620 was the very first 64-bit PowerPC implementation, but hardly anyone ever actually used the chips. qemu notionally supports the 620, but since we don't actually have code to implement the segment table, the support is broken (quite likely in other ways too). This patch, therefore, removes all remaining pieces of 620 support, to stop it cluttering up the platforms we actually care about. This includes removing support for the ASR register, used only on segment table based machines. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- monitor.c | 4 - target-ppc/cpu-models.c | 2 +- target-ppc/cpu.h | 30 ----- target-ppc/helper.h | 1 - target-ppc/machine.c | 4 +- target-ppc/misc_helper.c | 6 - target-ppc/mmu_helper.c | 44 +------ target-ppc/translate.c | 1 - target-ppc/translate_init.c | 255 ------------------------------------ 9 files changed, 8 insertions(+), 339 deletions(-) diff --git a/monitor.c b/monitor.c index 680d344211..881589bbf9 100644 --- a/monitor.c +++ b/monitor.c @@ -2960,10 +2960,6 @@ static const MonitorDef monitor_defs[] = { { "xer", 0, &monitor_get_xer, }, { "tbu", 0, &monitor_get_tbu, }, { "tbl", 0, &monitor_get_tbl, }, -#if defined(TARGET_PPC64) - /* Address space register */ - { "asr", offsetof(CPUPPCState, asr) }, -#endif /* Segment registers */ { "sdr1", offsetof(CPUPPCState, spr[SPR_SDR1]) }, { "sr0", offsetof(CPUPPCState, sr[0]) }, diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c index 20ca84e614..17f56b7504 100644 --- a/target-ppc/cpu-models.c +++ b/target-ppc/cpu-models.c @@ -1101,9 +1101,9 @@ "PowerPC 7457A v1.2 (G4)") /* 64 bits PowerPC */ #if defined (TARGET_PPC64) +#if defined(TODO) POWERPC_DEF("620", CPU_POWERPC_620, 620, "PowerPC 620") -#if defined(TODO) POWERPC_DEF("630", CPU_POWERPC_630, 630, "PowerPC 630 (POWER3)") #endif diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 9b9c784366..c8b6fc4e90 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -115,8 +115,6 @@ enum powerpc_mmu_t { #define POWERPC_MMU_1TSEG 0x00020000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, - /* 620 variant (no segment exceptions) */ - POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003, /* Architecture 2.06 "degraded" (no 1T segments) */ @@ -965,8 +963,6 @@ struct CPUPPCState { /* MMU context - only relevant for full system emulation */ #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) - /* Address space register */ - target_ulong asr; /* PowerPC 64 SLB area */ ppc_slb_t slb[64]; int slb_nr; @@ -1138,7 +1134,6 @@ void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); #if defined(TARGET_PPC64) -void ppc_store_asr (CPUPPCState *env, target_ulong value); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); #endif /* defined(TARGET_PPC64) */ #endif /* !defined(CONFIG_USER_ONLY) */ @@ -1491,11 +1486,9 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_RCPU_MI_RBA2 (0x302) #define SPR_MPC_MI_AP (0x302) #define SPR_PERF3 (0x303) -#define SPR_620_PMC1R (0x303) #define SPR_RCPU_MI_RBA3 (0x303) #define SPR_MPC_MI_EPN (0x303) #define SPR_PERF4 (0x304) -#define SPR_620_PMC2R (0x304) #define SPR_PERF5 (0x305) #define SPR_MPC_MI_TWC (0x305) #define SPR_PERF6 (0x306) @@ -1511,7 +1504,6 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_RCPU_L2U_RBA2 (0x30A) #define SPR_MPC_MD_AP (0x30A) #define SPR_PERFB (0x30B) -#define SPR_620_MMCR0R (0x30B) #define SPR_RCPU_L2U_RBA3 (0x30B) #define SPR_MPC_MD_EPN (0x30B) #define SPR_PERFC (0x30C) @@ -1526,9 +1518,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_UPERF1 (0x311) #define SPR_UPERF2 (0x312) #define SPR_UPERF3 (0x313) -#define SPR_620_PMC1W (0x313) #define SPR_UPERF4 (0x314) -#define SPR_620_PMC2W (0x314) #define SPR_UPERF5 (0x315) #define SPR_UPERF6 (0x316) #define SPR_UPERF7 (0x317) @@ -1536,7 +1526,6 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_UPERF9 (0x319) #define SPR_UPERFA (0x31A) #define SPR_UPERFB (0x31B) -#define SPR_620_MMCR0W (0x31B) #define SPR_UPERFC (0x31C) #define SPR_UPERFD (0x31D) #define SPR_UPERFE (0x31E) @@ -1608,49 +1597,33 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_USDA (0x3AF) #define SPR_40x_ZPR (0x3B0) #define SPR_BOOKE_MAS7 (0x3B0) -#define SPR_620_PMR0 (0x3B0) #define SPR_MMCR2 (0x3B0) #define SPR_PMC5 (0x3B1) #define SPR_40x_PID (0x3B1) -#define SPR_620_PMR1 (0x3B1) #define SPR_PMC6 (0x3B2) #define SPR_440_MMUCR (0x3B2) -#define SPR_620_PMR2 (0x3B2) #define SPR_4xx_CCR0 (0x3B3) #define SPR_BOOKE_EPLC (0x3B3) -#define SPR_620_PMR3 (0x3B3) #define SPR_405_IAC3 (0x3B4) #define SPR_BOOKE_EPSC (0x3B4) -#define SPR_620_PMR4 (0x3B4) #define SPR_405_IAC4 (0x3B5) -#define SPR_620_PMR5 (0x3B5) #define SPR_405_DVC1 (0x3B6) -#define SPR_620_PMR6 (0x3B6) #define SPR_405_DVC2 (0x3B7) -#define SPR_620_PMR7 (0x3B7) #define SPR_BAMR (0x3B7) #define SPR_MMCR0 (0x3B8) -#define SPR_620_PMR8 (0x3B8) #define SPR_PMC1 (0x3B9) #define SPR_40x_SGR (0x3B9) -#define SPR_620_PMR9 (0x3B9) #define SPR_PMC2 (0x3BA) #define SPR_40x_DCWR (0x3BA) -#define SPR_620_PMRA (0x3BA) #define SPR_SIAR (0x3BB) #define SPR_405_SLER (0x3BB) -#define SPR_620_PMRB (0x3BB) #define SPR_MMCR1 (0x3BC) #define SPR_405_SU0R (0x3BC) -#define SPR_620_PMRC (0x3BC) #define SPR_401_SKR (0x3BC) #define SPR_PMC3 (0x3BD) #define SPR_405_DBCR1 (0x3BD) -#define SPR_620_PMRD (0x3BD) #define SPR_PMC4 (0x3BE) -#define SPR_620_PMRE (0x3BE) #define SPR_SDA (0x3BF) -#define SPR_620_PMRF (0x3BF) #define SPR_403_VTBL (0x3CC) #define SPR_403_VTBU (0x3CD) #define SPR_DMISS (0x3D0) @@ -1718,15 +1691,12 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_LDSTCR (0x3F8) #define SPR_L2PMCR (0x3F8) #define SPR_750FX_HID2 (0x3F8) -#define SPR_620_BUSCSR (0x3F8) #define SPR_Exxx_L1FINV0 (0x3F8) #define SPR_L2CR (0x3F9) -#define SPR_620_L2CR (0x3F9) #define SPR_L3CR (0x3FA) #define SPR_750_TDCH (0x3FA) #define SPR_IABR2 (0x3FA) #define SPR_40x_DCCR (0x3FA) -#define SPR_620_L2SR (0x3FA) #define SPR_ICTC (0x3FB) #define SPR_40x_ICCR (0x3FB) #define SPR_THRM1 (0x3FC) diff --git a/target-ppc/helper.h b/target-ppc/helper.h index fcf372ab45..d33ee66b53 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -382,7 +382,6 @@ DEF_HELPER_1(load_601_rtcl, tl, env) DEF_HELPER_1(load_601_rtcu, tl, env) #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) -DEF_HELPER_2(store_asr, void, env, tl) DEF_HELPER_1(load_purr, tl, env) #endif DEF_HELPER_2(store_sdr1, void, env, tl) diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 708a840da7..235b0d5f49 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -37,7 +37,7 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_be32s(f, &fpscr); qemu_put_sbe32s(f, &env->access_type); #if defined(TARGET_PPC64) - qemu_put_betls(f, &env->asr); + qemu_put_betls(f, &env->spr[SPR_ASR]); qemu_put_sbe32s(f, &env->slb_nr); #endif qemu_put_betls(f, &env->spr[SPR_SDR1]); @@ -125,7 +125,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) env->fpscr = fpscr; qemu_get_sbe32s(f, &env->access_type); #if defined(TARGET_PPC64) - qemu_get_betls(f, &env->asr); + qemu_get_betls(f, &env->spr[SPR_ASR]); qemu_get_sbe32s(f, &env->slb_nr); #endif qemu_get_betls(f, &sdr1); diff --git a/target-ppc/misc_helper.c b/target-ppc/misc_helper.c index 26edcca2df..616aab6fb6 100644 --- a/target-ppc/misc_helper.c +++ b/target-ppc/misc_helper.c @@ -35,12 +35,6 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn) env->spr[sprn]); } #if !defined(CONFIG_USER_ONLY) -#if defined(TARGET_PPC64) -void helper_store_asr(CPUPPCState *env, target_ulong val) -{ - ppc_store_asr(env, val); -} -#endif void helper_store_sdr1(CPUPPCState *env, target_ulong val) { diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 1cc1c1649a..2f01b319bf 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1659,7 +1659,6 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, ctx->prot |= PAGE_WRITE; break; #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: @@ -1741,7 +1740,6 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, ret = get_bat(env, ctx, eaddr, rw, access_type); } #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: @@ -1883,7 +1881,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, case POWERPC_MMU_32B: case POWERPC_MMU_601: #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: @@ -1935,14 +1932,8 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #if defined(TARGET_PPC64) case -5: /* No match in segment table */ - if (env->mmu_model == POWERPC_MMU_620) { - env->exception_index = POWERPC_EXCP_ISI; - /* XXX: this might be incorrect */ - env->error_code = 0x40000000; - } else { - env->exception_index = POWERPC_EXCP_ISEG; - env->error_code = 0; - } + env->exception_index = POWERPC_EXCP_ISEG; + env->error_code = 0; break; #endif } @@ -1995,7 +1986,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, case POWERPC_MMU_32B: case POWERPC_MMU_601: #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: @@ -2097,21 +2087,9 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #if defined(TARGET_PPC64) case -5: /* No match in segment table */ - if (env->mmu_model == POWERPC_MMU_620) { - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - /* XXX: this might be incorrect */ - if (rw == 1) { - env->spr[SPR_DSISR] = 0x42000000; - } else { - env->spr[SPR_DSISR] = 0x40000000; - } - } else { - env->exception_index = POWERPC_EXCP_DSEG; - env->error_code = 0; - env->spr[SPR_DAR] = address; - } + env->exception_index = POWERPC_EXCP_DSEG; + env->error_code = 0; + env->spr[SPR_DAR] = address; break; #endif } @@ -2326,7 +2304,6 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) case POWERPC_MMU_32B: case POWERPC_MMU_601: #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: @@ -2396,7 +2373,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) tlb_flush_page(env, addr | (0xF << 28)); break; #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: @@ -2420,16 +2396,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) /*****************************************************************************/ /* Special registers manipulation */ -#if defined(TARGET_PPC64) -void ppc_store_asr(CPUPPCState *env, target_ulong value) -{ - if (env->asr != value) { - env->asr = value; - tlb_flush(env, 1); - } -} -#endif - void ppc_store_sdr1(CPUPPCState *env, target_ulong value) { LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value); diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 380a884131..bb690f817a 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -9428,7 +9428,6 @@ void cpu_dump_state (CPUPPCState *env, FILE *f, fprintf_function cpu_fprintf, case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: #if defined(TARGET_PPC64) - case POWERPC_MMU_620: case POWERPC_MMU_64B: #endif cpu_fprintf(f, " SDR1 " TARGET_FMT_lx "\n", env->spr[SPR_SDR1]); diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index bd5033c415..09fb29ec97 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -365,7 +365,6 @@ static void spr_write_sdr1 (void *opaque, int sprn, int gprn) } /* 64 bits PowerPC specific SPRs */ -/* ASR */ #if defined(TARGET_PPC64) static void spr_read_hior (void *opaque, int gprn, int sprn) { @@ -379,16 +378,6 @@ static void spr_write_hior (void *opaque, int sprn, int gprn) tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); tcg_temp_free(t0); } - -static void spr_read_asr (void *opaque, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, asr)); -} - -static void spr_write_asr (void *opaque, int sprn, int gprn) -{ - gen_helper_store_asr(cpu_env, cpu_gpr[gprn]); -} #endif #endif @@ -2151,173 +2140,6 @@ static void gen_spr_compress (CPUPPCState *env) 0x00000000); } -#if defined (TARGET_PPC64) -/* SPR specific to PowerPC 620 */ -static void gen_spr_620 (CPUPPCState *env) -{ - /* Processor identification */ - spr_register(env, SPR_PIR, "PIR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_pir, - 0x00000000); - spr_register(env, SPR_ASR, "ASR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_asr, &spr_write_asr, - 0x00000000); - /* Breakpoints */ - /* XXX : not implemented */ - spr_register(env, SPR_IABR, "IABR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_DABR, "DABR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_SIAR, "SIAR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_SDA, "SDA", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMC1R, "PMC1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); - spr_register(env, SPR_620_PMC1W, "PMC1", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMC2R, "PMC2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); - spr_register(env, SPR_620_PMC2W, "PMC2", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_MMCR0R, "MMCR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, SPR_NOACCESS, - 0x00000000); - spr_register(env, SPR_620_MMCR0W, "MMCR0", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_generic, - 0x00000000); - /* External access control */ - /* XXX : not implemented */ - spr_register(env, SPR_EAR, "EAR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); -#if 0 // XXX: check this - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR0, "PMR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR1, "PMR1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR2, "PMR2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR3, "PMR3", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR4, "PMR4", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR5, "PMR5", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR6, "PMR6", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR7, "PMR7", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR8, "PMR8", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMR9, "PMR9", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMRA, "PMR10", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMRB, "PMR11", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMRC, "PMR12", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMRD, "PMR13", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMRE, "PMR14", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_PMRF, "PMR15", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); -#endif - /* XXX : not implemented */ - spr_register(env, SPR_620_BUSCSR, "BUSCSR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_L2CR, "L2CR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_620_L2SR, "L2SR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); -} -#endif /* defined (TARGET_PPC64) */ - static void gen_spr_5xx_8xx (CPUPPCState *env) { /* Exception processing */ @@ -2993,31 +2815,6 @@ static void init_excp_604 (CPUPPCState *env) #endif } -#if defined(TARGET_PPC64) -static void init_excp_620 (CPUPPCState *env) -{ -#if !defined(CONFIG_USER_ONLY) - env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; - env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; - env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; - env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; - env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; - env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; - env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; - env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; - env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; - env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; - env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; - env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00; - env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; - env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; - env->hreset_excp_prefix = 0xFFF00000UL; - /* Hardware reset vector */ - env->hreset_vector = 0x0000000000000100ULL; -#endif -} -#endif /* defined(TARGET_PPC64) */ - static void init_excp_7x0 (CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -7129,55 +6926,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR; } - -static void init_proc_620 (CPUPPCState *env) -{ - gen_spr_ne_601(env); - gen_spr_620(env); - /* Time base */ - gen_tbl(env); - /* Hardware implementation registers */ - /* XXX : not implemented */ - spr_register(env, SPR_HID0, "HID0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Memory management */ - gen_low_BATs(env); - init_excp_620(env); - env->dcache_line_size = 64; - env->icache_line_size = 64; - /* Allocate hardware IRQ controller */ - ppc6xx_irq_init(env); -} - -POWERPC_FAMILY(620)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 620"; - pcc->init_proc = init_proc_620; - pcc->check_pow = check_pow_nocheck; /* Check this */ - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | - PPC_FLOAT_STFIWX | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_SEGMENT | PPC_EXTERN | - PPC_64B | PPC_SLBI; - pcc->insns_flags2 = PPC_NONE; - pcc->msr_mask = 0x800000000005FF77ULL; - pcc->mmu_model = POWERPC_MMU_620; - pcc->excp_model = POWERPC_EXCP_970; - pcc->bus_model = PPC_FLAGS_INPUT_6xx; - pcc->bfd_mach = bfd_mach_ppc64; - pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; -} - #endif /* defined (TARGET_PPC64) */ @@ -7915,9 +7663,6 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) case POWERPC_MMU_64B: mmu_model = "PowerPC 64"; break; - case POWERPC_MMU_620: - mmu_model = "PowerPC 620"; - break; #endif default: mmu_model = "Unknown or invalid"; From 213c718080f51aa4f054a79c0c5743e0e2c15f67 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:04 +0000 Subject: [PATCH 07/58] target-ppc: Trivial cleanups in mmu_helper.c This removes the never-used pte64_invalidate() function, and makes ppcmas_tlb_check() static, since it's only used within that file. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 3 --- target-ppc/mmu_helper.c | 11 +++-------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index c8b6fc4e90..83b41f3ceb 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1167,9 +1167,6 @@ void store_40x_dbcr0 (CPUPPCState *env, uint32_t val); void store_40x_sler (CPUPPCState *env, uint32_t val); void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, target_ulong address, - uint32_t pid); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); #endif diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 2f01b319bf..3570e91e23 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -98,11 +98,6 @@ static inline int pte64_is_valid(target_ulong pte0) { return pte0 & 0x0000000000000001ULL ? 1 : 0; } - -static inline void pte64_invalidate(target_ulong *pte0) -{ - *pte0 &= ~0x0000000000000001ULL; -} #endif #define PTE_PTEM_MASK 0x7FFFFFBF @@ -1309,9 +1304,9 @@ static hwaddr booke206_tlb_to_page_size(CPUPPCState *env, } /* TLB check function for MAS based SoftTLBs */ -int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddrp, - target_ulong address, uint32_t pid) +static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddrp, + target_ulong address, uint32_t pid) { target_ulong mask; uint32_t tlb_pid; From 8152ceaf6eea6d63f6ee65eb419fff56bb3b987b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:05 +0000 Subject: [PATCH 08/58] target-ppc: Remove address check for logging One LOG_MMU statement in mmu_helper.c has an odd check on the effective address being translated. I can see no reason for this; I suspect it was a debugging hack from long ago. This patch removes it. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu_helper.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 3570e91e23..f1de84c0cc 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -906,12 +906,10 @@ static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx, ret = find_pte(env, ctx, 0, rw, type, target_page_bits); if (ret < 0) { /* Secondary table lookup */ - if (eaddr != 0xEFFFFFFF) { - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - } + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); ret2 = find_pte(env, ctx, 1, rw, type, target_page_bits); if (ret2 != -1) { From 10b4652543313ca82284193fa107151c437f9b04 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:06 +0000 Subject: [PATCH 09/58] target-ppc: Move SLB handling into a mmu-hash64.c As a first step to disentangling the handling for 64-bit hash MMUs from the rest, we move the code handling the Segment Lookaside Buffer (SLB) (which only exists on 64-bit hash MMUs) into a new mmu-hash64.c file. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/Makefile.objs | 5 +- target-ppc/cpu.h | 3 - target-ppc/mmu-hash64.c | 211 +++++++++++++++++++++++++++++++++++++++ target-ppc/mmu-hash64.h | 14 +++ target-ppc/mmu_helper.c | 198 +----------------------------------- 5 files changed, 232 insertions(+), 199 deletions(-) create mode 100644 target-ppc/mmu-hash64.c create mode 100644 target-ppc/mmu-hash64.h diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index 00ac4adc51..4ef9fc0491 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -1,6 +1,9 @@ obj-y += cpu-models.o obj-y += translate.o -obj-$(CONFIG_SOFTMMU) += machine.o +ifeq ($(CONFIG_SOFTMMU),y) +obj-y += machine.o +obj-$(TARGET_PPC64) += mmu-hash64.o +endif obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o obj-y += excp_helper.o obj-y += fpu_helper.o diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 83b41f3ceb..a3e1362b7a 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1133,9 +1133,6 @@ void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); -#if defined(TARGET_PPC64) -int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -#endif /* defined(TARGET_PPC64) */ #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c new file mode 100644 index 0000000000..a72e7c102b --- /dev/null +++ b/target-ppc/mmu-hash64.c @@ -0,0 +1,211 @@ +/* + * PowerPC MMU, TLB, SLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2013 David Gibson, 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 "cpu.h" +#include "helper.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash64.h" + +//#define DEBUG_SLB + +#ifdef DEBUG_SLB +# define LOG_SLB(...) qemu_log(__VA_ARGS__) +#else +# define LOG_SLB(...) do { } while (0) +#endif + +/* + * SLB handling + */ + +ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) +{ + uint64_t esid_256M, esid_1T; + int n; + + LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); + + esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; + esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; + + for (n = 0; n < env->slb_nr; n++) { + ppc_slb_t *slb = &env->slb[n]; + + LOG_SLB("%s: slot %d %016" PRIx64 " %016" + PRIx64 "\n", __func__, n, slb->esid, slb->vsid); + /* We check for 1T matches on all MMUs here - if the MMU + * doesn't have 1T segment support, we will have prevented 1T + * entries from being inserted in the slbmte code. */ + if (((slb->esid == esid_256M) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) + || ((slb->esid == esid_1T) && + ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { + return slb; + } + } + + return NULL; +} + +void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) +{ + int i; + uint64_t slbe, slbv; + + cpu_synchronize_state(env); + + cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); + for (i = 0; i < env->slb_nr; i++) { + slbe = env->slb[i].esid; + slbv = env->slb[i].vsid; + if (slbe == 0 && slbv == 0) { + continue; + } + cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", + i, slbe, slbv); + } +} + +void helper_slbia(CPUPPCState *env) +{ + int n, do_invalidate; + + 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]; + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in QEMU, we just invalidate all TLBs + */ + do_invalidate = 1; + } + } + if (do_invalidate) { + tlb_flush(env, 1); + } +} + +void helper_slbie(CPUPPCState *env, target_ulong addr) +{ + ppc_slb_t *slb; + + slb = slb_lookup(env, addr); + if (!slb) { + return; + } + + if (slb->esid & SLB_ESID_V) { + slb->esid &= ~SLB_ESID_V; + + /* XXX: given the fact that segment size is 256 MB or 1TB, + * and we still don't have a tlb_flush_mask(env, n, mask) + * in QEMU, we just invalidate all TLBs + */ + tlb_flush(env, 1); + } +} + +int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +{ + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (rb & (0x1000 - env->slb_nr)) { + return -1; /* Reserved bits set or slot too high */ + } + if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { + return -1; /* Bad segment size */ + } + if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + return -1; /* 1T segment on MMU that doesn't support it */ + } + + /* Mask out the slot number as we store the entry */ + slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); + slb->vsid = rs; + + LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 + " %016" PRIx64 "\n", __func__, slot, rb, rs, + slb->esid, slb->vsid); + + return 0; +} + +static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, + target_ulong *rt) +{ + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->esid; + return 0; +} + +static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, + target_ulong *rt) +{ + int slot = rb & 0xfff; + ppc_slb_t *slb = &env->slb[slot]; + + if (slot >= env->slb_nr) { + return -1; + } + + *rt = slb->vsid; + return 0; +} + +void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) +{ + if (ppc_store_slb(env, rb, rs) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } +} + +target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) +{ + target_ulong rt = 0; + + if (ppc_load_slb_esid(env, rb, &rt) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } + return rt; +} + +target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) +{ + target_ulong rt = 0; + + if (ppc_load_slb_vsid(env, rb, &rt) < 0) { + helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_INVAL); + } + return rt; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h new file mode 100644 index 0000000000..894b1f2a2b --- /dev/null +++ b/target-ppc/mmu-hash64.h @@ -0,0 +1,14 @@ +#if !defined (__MMU_HASH64_H__) +#define __MMU_HASH64_H__ + +#ifndef CONFIG_USER_ONLY + +#ifdef TARGET_PPC64 +ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr); +void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); +int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); +#endif + +#endif /* CONFIG_USER_ONLY */ + +#endif /* !defined (__MMU_HASH64_H__) */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index f1de84c0cc..671ca5e83d 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -20,10 +20,10 @@ #include "helper.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" +#include "mmu-hash64.h" //#define DEBUG_MMU //#define DEBUG_BATS -//#define DEBUG_SLB //#define DEBUG_SOFTWARE_TLB //#define DUMP_PAGE_TABLES //#define DEBUG_SOFTWARE_TLB @@ -49,12 +49,6 @@ # define LOG_BATS(...) do { } while (0) #endif -#ifdef DEBUG_SLB -# define LOG_SLB(...) qemu_log(__VA_ARGS__) -#else -# define LOG_SLB(...) do { } while (0) -#endif - /*****************************************************************************/ /* PowerPC MMU emulation */ #if defined(CONFIG_USER_ONLY) @@ -677,137 +671,6 @@ static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, return find_pte2(env, ctx, 0, h, rw, type, target_page_bits); } -#if defined(TARGET_PPC64) -static inline ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) -{ - uint64_t esid_256M, esid_1T; - int n; - - LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr); - - esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; - esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; - - for (n = 0; n < env->slb_nr; n++) { - ppc_slb_t *slb = &env->slb[n]; - - LOG_SLB("%s: slot %d %016" PRIx64 " %016" - PRIx64 "\n", __func__, n, slb->esid, slb->vsid); - /* We check for 1T matches on all MMUs here - if the MMU - * doesn't have 1T segment support, we will have prevented 1T - * entries from being inserted in the slbmte code. */ - if (((slb->esid == esid_256M) && - ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_256M)) - || ((slb->esid == esid_1T) && - ((slb->vsid & SLB_VSID_B) == SLB_VSID_B_1T))) { - return slb; - } - } - - return NULL; -} - -/*****************************************************************************/ -/* SPR accesses */ - -void helper_slbia(CPUPPCState *env) -{ - int n, do_invalidate; - - 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]; - - if (slb->esid & SLB_ESID_V) { - slb->esid &= ~SLB_ESID_V; - /* XXX: given the fact that segment size is 256 MB or 1TB, - * and we still don't have a tlb_flush_mask(env, n, mask) - * in QEMU, we just invalidate all TLBs - */ - do_invalidate = 1; - } - } - if (do_invalidate) { - tlb_flush(env, 1); - } -} - -void helper_slbie(CPUPPCState *env, target_ulong addr) -{ - ppc_slb_t *slb; - - slb = slb_lookup(env, addr); - if (!slb) { - return; - } - - if (slb->esid & SLB_ESID_V) { - slb->esid &= ~SLB_ESID_V; - - /* XXX: given the fact that segment size is 256 MB or 1TB, - * and we still don't have a tlb_flush_mask(env, n, mask) - * in QEMU, we just invalidate all TLBs - */ - tlb_flush(env, 1); - } -} - -int ppc_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) -{ - int slot = rb & 0xfff; - ppc_slb_t *slb = &env->slb[slot]; - - if (rb & (0x1000 - env->slb_nr)) { - return -1; /* Reserved bits set or slot too high */ - } - if (rs & (SLB_VSID_B & ~SLB_VSID_B_1T)) { - return -1; /* Bad segment size */ - } - if ((rs & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { - return -1; /* 1T segment on MMU that doesn't support it */ - } - - /* Mask out the slot number as we store the entry */ - slb->esid = rb & (SLB_ESID_ESID | SLB_ESID_V); - slb->vsid = rs; - - LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64 - " %016" PRIx64 "\n", __func__, slot, rb, rs, - slb->esid, slb->vsid); - - return 0; -} - -static int ppc_load_slb_esid(CPUPPCState *env, target_ulong rb, - target_ulong *rt) -{ - int slot = rb & 0xfff; - ppc_slb_t *slb = &env->slb[slot]; - - if (slot >= env->slb_nr) { - return -1; - } - - *rt = slb->esid; - return 0; -} - -static int ppc_load_slb_vsid(CPUPPCState *env, target_ulong rb, - target_ulong *rt) -{ - int slot = rb & 0xfff; - ppc_slb_t *slb = &env->slb[slot]; - - if (slot >= env->slb_nr) { - return -1; - } - - *rt = slb->vsid; - return 0; -} -#endif /* defined(TARGET_PPC64) */ - /* Perform segment based translation */ static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int type) @@ -1304,7 +1167,7 @@ static hwaddr booke206_tlb_to_page_size(CPUPPCState *env, /* TLB check function for MAS based SoftTLBs */ static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, - target_ulong address, uint32_t pid) + target_ulong address, uint32_t pid) { target_ulong mask; uint32_t tlb_pid; @@ -1590,28 +1453,6 @@ static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf, } } -#if defined(TARGET_PPC64) -static void mmubooks_dump_mmu(FILE *f, fprintf_function cpu_fprintf, - CPUPPCState *env) -{ - int i; - uint64_t slbe, slbv; - - cpu_synchronize_state(env); - - cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); - for (i = 0; i < env->slb_nr; i++) { - slbe = env->slb[i].esid; - slbv = env->slb[i].vsid; - if (slbe == 0 && slbv == 0) { - continue; - } - cpu_fprintf(f, "%d\t0x%016" PRIx64 "\t0x%016" PRIx64 "\n", - i, slbe, slbv); - } -} -#endif - void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) { switch (env->mmu_model) { @@ -1625,7 +1466,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: - mmubooks_dump_mmu(f, cpu_fprintf, env); + dump_slb(f, cpu_fprintf, env); break; #endif default: @@ -2473,39 +2314,6 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) #endif /* !defined(CONFIG_USER_ONLY) */ #if !defined(CONFIG_USER_ONLY) -/* SLB management */ -#if defined(TARGET_PPC64) -void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs) -{ - if (ppc_store_slb(env, rb, rs) < 0) { - helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL); - } -} - -target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb) -{ - target_ulong rt = 0; - - if (ppc_load_slb_esid(env, rb, &rt) < 0) { - helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL); - } - return rt; -} - -target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) -{ - target_ulong rt = 0; - - if (ppc_load_slb_vsid(env, rb, &rt) < 0) { - helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_INVAL); - } - return rt; -} -#endif /* defined(TARGET_PPC64) */ - /* TLB management */ void helper_tlbia(CPUPPCState *env) { From 9d7c3f4a2935a70e7299a6862792bbfc48d62211 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:07 +0000 Subject: [PATCH 10/58] target-ppc: Disentangle pte_check() Currently support for both 32-bit and 64-bit hash MMUs share an implementation of pte_check. But there are enough differences that this means the shared function has several very ugly conditionals on "is_64b". This patch cleans things up by separating out the 64-bit version (putting it into mmu-hash64.c) and the 32-bit hash version (putting it in mmu-hash32.c). Another copy remains in mmu_helper.c, which is used for the 6xx software loaded TLB paths. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/Makefile.objs | 2 +- target-ppc/cpu.h | 2 + target-ppc/mmu-hash32.c | 85 ++++++++++++++++++++++++++++++++++++++++ target-ppc/mmu-hash32.h | 12 ++++++ target-ppc/mmu-hash64.c | 65 ++++++++++++++++++++++++++++++ target-ppc/mmu-hash64.h | 2 + target-ppc/mmu_helper.c | 67 ++++++------------------------- 7 files changed, 179 insertions(+), 56 deletions(-) create mode 100644 target-ppc/mmu-hash32.c create mode 100644 target-ppc/mmu-hash32.h diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index 4ef9fc0491..c80911f2c7 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -1,7 +1,7 @@ obj-y += cpu-models.o obj-y += translate.o ifeq ($(CONFIG_SOFTMMU),y) -obj-y += machine.o +obj-y += machine.o mmu-hash32.o obj-$(TARGET_PPC64) += mmu-hash64.o endif obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index a3e1362b7a..625feb2664 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1133,6 +1133,8 @@ void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); +int pp_check(int key, int pp, int nx); +int check_prot(int prot, int rw, int access_type); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c new file mode 100644 index 0000000000..ce5389d217 --- /dev/null +++ b/target-ppc/mmu-hash32.c @@ -0,0 +1,85 @@ +/* + * PowerPC MMU, TLB and BAT emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2013 David Gibson, 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 "cpu.h" +#include "helper.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash32.h" + +//#define DEBUG_MMU + +#ifdef DEBUG_MMU +# define LOG_MMU(...) qemu_log(__VA_ARGS__) +# define LOG_MMU_STATE(env) log_cpu_state((env), 0) +#else +# define LOG_MMU(...) do { } while (0) +# define LOG_MMU_STATE(...) do { } while (0) +#endif + +#define PTE_PTEM_MASK 0x7FFFFFBF +#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) + +static inline int pte_is_valid_hash32(target_ulong pte0) +{ + return pte0 & 0x80000000 ? 1 : 0; +} + +int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) +{ + target_ulong ptem, mmask; + int access, ret, pteh, ptev, pp; + + ret = -1; + /* Check validity and table match */ + ptev = pte_is_valid_hash32(pte0); + pteh = (pte0 >> 6) & 1; + if (ptev && h == pteh) { + /* Check vsid & api */ + ptem = pte0 & PTE_PTEM_MASK; + mmask = PTE_CHECK_MASK; + pp = pte1 & 0x00000003; + if (ptem == ctx->ptem) { + if (ctx->raddr != (hwaddr)-1ULL) { + /* all matches should have equal RPN, WIMG & PP */ + if ((ctx->raddr & mmask) != (pte1 & mmask)) { + qemu_log("Bad RPN/WIMG/PP\n"); + return -3; + } + } + /* Compute access rights */ + access = pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte1; + ctx->prot = access; + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); + } + } + } + + return ret; +} diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h new file mode 100644 index 0000000000..24110857d2 --- /dev/null +++ b/target-ppc/mmu-hash32.h @@ -0,0 +1,12 @@ +#if !defined (__MMU_HASH32_H__) +#define __MMU_HASH32_H__ + +#ifndef CONFIG_USER_ONLY + +int pte32_is_valid(target_ulong pte0); +int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type); + +#endif /* CONFIG_USER_ONLY */ + +#endif /* __MMU_HASH32_H__ */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index a72e7c102b..9c0de1bbd9 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -23,8 +23,17 @@ #include "kvm_ppc.h" #include "mmu-hash64.h" +//#define DEBUG_MMU //#define DEBUG_SLB +#ifdef DEBUG_MMU +# define LOG_MMU(...) qemu_log(__VA_ARGS__) +# define LOG_MMU_STATE(env) log_cpu_state((env), 0) +#else +# define LOG_MMU(...) do { } while (0) +# define LOG_MMU_STATE(...) do { } while (0) +#endif + #ifdef DEBUG_SLB # define LOG_SLB(...) qemu_log(__VA_ARGS__) #else @@ -209,3 +218,59 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) } return rt; } + +/* + * 64-bit hash table MMU handling + */ + +#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL +#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) + +static inline int pte64_is_valid(target_ulong pte0) +{ + return pte0 & 0x0000000000000001ULL ? 1 : 0; +} + +int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) +{ + target_ulong ptem, mmask; + int access, ret, pteh, ptev, pp; + + ret = -1; + /* Check validity and table match */ + ptev = pte64_is_valid(pte0); + pteh = (pte0 >> 1) & 1; + if (ptev && h == pteh) { + /* Check vsid & api */ + ptem = pte0 & PTE64_PTEM_MASK; + mmask = PTE64_CHECK_MASK; + pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); + ctx->nx = (pte1 >> 2) & 1; /* No execute bit */ + ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ + if (ptem == ctx->ptem) { + if (ctx->raddr != (hwaddr)-1ULL) { + /* all matches should have equal RPN, WIMG & PP */ + if ((ctx->raddr & mmask) != (pte1 & mmask)) { + qemu_log("Bad RPN/WIMG/PP\n"); + return -3; + } + } + /* Compute access rights */ + access = pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte1; + ctx->prot = access; + ret = check_prot(ctx->prot, rw, type); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); + } + } + } + + return ret; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 894b1f2a2b..1a2e3e7258 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -7,6 +7,8 @@ ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr); void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); +int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type); #endif #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 671ca5e83d..c7620c0c42 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -21,6 +21,7 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "mmu-hash64.h" +#include "mmu-hash32.h" //#define DEBUG_MMU //#define DEBUG_BATS @@ -87,21 +88,10 @@ static inline void pte_invalidate(target_ulong *pte0) *pte0 &= ~0x80000000; } -#if defined(TARGET_PPC64) -static inline int pte64_is_valid(target_ulong pte0) -{ - return pte0 & 0x0000000000000001ULL ? 1 : 0; -} -#endif - #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) -#if defined(TARGET_PPC64) -#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL -#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) -#endif -static inline int pp_check(int key, int pp, int nx) +int pp_check(int key, int pp, int nx) { int access; @@ -142,7 +132,7 @@ static inline int pp_check(int key, int pp, int nx) return access; } -static inline int check_prot(int prot, int rw, int access_type) +int check_prot(int prot, int rw, int access_type) { int ret; @@ -169,40 +159,21 @@ static inline int check_prot(int prot, int rw, int access_type) return ret; } -static inline int pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) +static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) { target_ulong ptem, mmask; int access, ret, pteh, ptev, pp; ret = -1; /* Check validity and table match */ -#if defined(TARGET_PPC64) - if (is_64b) { - ptev = pte64_is_valid(pte0); - pteh = (pte0 >> 1) & 1; - } else -#endif - { - ptev = pte_is_valid(pte0); - pteh = (pte0 >> 6) & 1; - } + ptev = pte_is_valid(pte0); + pteh = (pte0 >> 6) & 1; if (ptev && h == pteh) { /* Check vsid & api */ -#if defined(TARGET_PPC64) - if (is_64b) { - ptem = pte0 & PTE64_PTEM_MASK; - mmask = PTE64_CHECK_MASK; - pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); - ctx->nx = (pte1 >> 2) & 1; /* No execute bit */ - ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ - } else -#endif - { - ptem = pte0 & PTE_PTEM_MASK; - mmask = PTE_CHECK_MASK; - pp = pte1 & 0x00000003; - } + ptem = pte0 & PTE_PTEM_MASK; + mmask = PTE_CHECK_MASK; + pp = pte1 & 0x00000003; if (ptem == ctx->ptem) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ @@ -230,20 +201,6 @@ static inline int pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0, return ret; } -static inline int pte32_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) -{ - return pte_check(ctx, 0, pte0, pte1, h, rw, type); -} - -#if defined(TARGET_PPC64) -static inline int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) -{ - return pte_check(ctx, 1, pte0, pte1, h, rw, type); -} -#endif - static inline int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, int ret, int rw) { @@ -381,7 +338,7 @@ static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, pte_is_valid(tlb->pte0) ? "valid" : "inval", tlb->EPN, eaddr, tlb->pte1, rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); - switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { + switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) { case -3: /* TLB inconsistency */ return -1; @@ -590,7 +547,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); } - r = pte32_check(ctx, pte0, pte1, h, rw, type); + r = pte_check_hash32(ctx, pte0, pte1, h, rw, type); LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, From c69b6151e7f242b02f261f321c392e5ef933176f Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:08 +0000 Subject: [PATCH 11/58] target-ppc: Disentangle find_pte() 32-bit and 64-bit hash MMU implementations currently share a find_pte function. This results in a whole bunch of ugly conditionals in the shared function, and not all that much actually shared code. This patch separates out the 32-bit and 64-bit versions, putting then in mmu-hash64.c and mmu-has32.c, and removes the conditionals from both versions. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 2 + target-ppc/mmu-hash32.c | 78 +++++++++++++++++++++++++- target-ppc/mmu-hash32.h | 4 +- target-ppc/mmu-hash64.c | 79 +++++++++++++++++++++++++- target-ppc/mmu-hash64.h | 4 +- target-ppc/mmu_helper.c | 119 ++-------------------------------------- 6 files changed, 164 insertions(+), 122 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 625feb2664..cf8ba2e5e3 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1135,6 +1135,8 @@ void ppc_hw_interrupt (CPUPPCState *env); void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); int pp_check(int key, int pp, int nx); int check_prot(int prot, int rw, int access_type); +int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, int ret, int rw); +hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index ce5389d217..f852e5c67e 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -42,8 +42,8 @@ static inline int pte_is_valid_hash32(target_ulong pte0) return pte0 & 0x80000000 ? 1 : 0; } -int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) +static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) { target_ulong ptem, mmask; int access, ret, pteh, ptev, pp; @@ -83,3 +83,77 @@ int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, return ret; } + +/* PTE table lookup */ +int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, + int rw, int type, int target_page_bits) +{ + hwaddr pteg_off; + target_ulong pte0, pte1; + int i, good = -1; + int ret, r; + + ret = -1; /* No entry found */ + pteg_off = get_pteg_offset(env, ctx->hash[h], HASH_PTE_SIZE_32); + for (i = 0; i < 8; i++) { + if (env->external_htab) { + pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); + pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); + } else { + pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); + pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); + } + r = pte_check_hash32(ctx, pte0, pte1, h, rw, type); + LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " + TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", + pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, + (int)((pte0 >> 6) & 1), ctx->ptem); + switch (r) { + case -3: + /* PTE inconsistency */ + return -1; + case -2: + /* Access violation */ + ret = -2; + good = i; + break; + case -1: + default: + /* No PTE match */ + break; + case 0: + /* access granted */ + /* XXX: we should go on looping to check all PTEs consistency + * but if we can speed-up the whole thing as the + * result would be undefined if PTEs are not consistent. + */ + ret = 0; + good = i; + goto done; + } + } + if (good != -1) { + done: + LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", + ctx->raddr, ctx->prot, ret); + /* Update page flags */ + pte1 = ctx->raddr; + if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { + if (env->external_htab) { + stl_p(env->external_htab + pteg_off + (good * 8) + 4, + pte1); + } else { + stl_phys_notdirty(env->htab_base + pteg_off + + (good * 8) + 4, pte1); + } + } + } + + /* We have a TLB that saves 4K pages, so let's + * split a huge page to 4k chunks */ + if (target_page_bits != TARGET_PAGE_BITS) { + ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) + & TARGET_PAGE_MASK; + } + return ret; +} diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 24110857d2..6bf8f92463 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -4,8 +4,8 @@ #ifndef CONFIG_USER_ONLY int pte32_is_valid(target_ulong pte0); -int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type); +int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, + int rw, int type, int target_page_bits); #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 9c0de1bbd9..a525bd552f 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -231,8 +231,8 @@ static inline int pte64_is_valid(target_ulong pte0) return pte0 & 0x0000000000000001ULL ? 1 : 0; } -int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) +static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, int rw, int type) { target_ulong ptem, mmask; int access, ret, pteh, ptev, pp; @@ -274,3 +274,78 @@ int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, return ret; } + +/* PTE table lookup */ +int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, + int rw, int type, int target_page_bits) +{ + hwaddr pteg_off; + target_ulong pte0, pte1; + int i, good = -1; + int ret, r; + + ret = -1; /* No entry found */ + pteg_off = get_pteg_offset(env, ctx->hash[h], HASH_PTE_SIZE_64); + for (i = 0; i < 8; i++) { + if (env->external_htab) { + pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); + pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); + } else { + pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16)); + pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8); + } + + r = pte64_check(ctx, pte0, pte1, h, rw, type); + LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " + TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", + pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, + (int)((pte0 >> 1) & 1), ctx->ptem); + switch (r) { + case -3: + /* PTE inconsistency */ + return -1; + case -2: + /* Access violation */ + ret = -2; + good = i; + break; + case -1: + default: + /* No PTE match */ + break; + case 0: + /* access granted */ + /* XXX: we should go on looping to check all PTEs consistency + * but if we can speed-up the whole thing as the + * result would be undefined if PTEs are not consistent. + */ + ret = 0; + good = i; + goto done; + } + } + if (good != -1) { + done: + LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", + ctx->raddr, ctx->prot, ret); + /* Update page flags */ + pte1 = ctx->raddr; + if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { + if (env->external_htab) { + stq_p(env->external_htab + pteg_off + (good * 16) + 8, + pte1); + } else { + stq_phys_notdirty(env->htab_base + pteg_off + + (good * 16) + 8, pte1); + } + } + } + + /* We have a TLB that saves 4K pages, so let's + * split a huge page to 4k chunks */ + if (target_page_bits != TARGET_PAGE_BITS) { + ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) + & TARGET_PAGE_MASK; + } + return ret; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 1a2e3e7258..7b9713da28 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -7,8 +7,8 @@ ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr); void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type); +int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, + int rw, int type, int target_page_bits); #endif #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index c7620c0c42..1301391800 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -201,8 +201,8 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, return ret; } -static inline int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, int rw) +int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) { int store = 0; @@ -502,130 +502,21 @@ static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -static inline hwaddr get_pteg_offset(CPUPPCState *env, - hwaddr hash, - int pte_size) +hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size) { return (hash * pte_size * 8) & env->htab_mask; } -/* PTE table lookup */ -static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, - int rw, int type, int target_page_bits) -{ - hwaddr pteg_off; - target_ulong pte0, pte1; - int i, good = -1; - int ret, r; - - ret = -1; /* No entry found */ - pteg_off = get_pteg_offset(env, ctx->hash[h], - is_64b ? HASH_PTE_SIZE_64 : HASH_PTE_SIZE_32); - for (i = 0; i < 8; i++) { -#if defined(TARGET_PPC64) - if (is_64b) { - if (env->external_htab) { - pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); - pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); - } else { - pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16)); - pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8); - } - - r = pte64_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " - TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, - (int)((pte0 >> 1) & 1), ctx->ptem); - } else -#endif - { - if (env->external_htab) { - pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); - pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); - } else { - pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); - pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); - } - r = pte_check_hash32(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " - TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, - (int)((pte0 >> 6) & 1), ctx->ptem); - } - switch (r) { - case -3: - /* PTE inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; - good = i; - break; - case -1: - default: - /* No PTE match */ - break; - case 0: - /* access granted */ - /* XXX: we should go on looping to check all PTEs consistency - * but if we can speed-up the whole thing as the - * result would be undefined if PTEs are not consistent. - */ - ret = 0; - good = i; - goto done; - } - } - if (good != -1) { - done: - LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", - ctx->raddr, ctx->prot, ret); - /* Update page flags */ - pte1 = ctx->raddr; - if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { -#if defined(TARGET_PPC64) - if (is_64b) { - if (env->external_htab) { - stq_p(env->external_htab + pteg_off + (good * 16) + 8, - pte1); - } else { - stq_phys_notdirty(env->htab_base + pteg_off + - (good * 16) + 8, pte1); - } - } else -#endif - { - if (env->external_htab) { - stl_p(env->external_htab + pteg_off + (good * 8) + 4, - pte1); - } else { - stl_phys_notdirty(env->htab_base + pteg_off + - (good * 8) + 4, pte1); - } - } - } - } - - /* We have a TLB that saves 4K pages, so let's - * split a huge page to 4k chunks */ - if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) - & TARGET_PAGE_MASK; - } - return ret; -} - static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, int type, int target_page_bits) { #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { - return find_pte2(env, ctx, 1, h, rw, type, target_page_bits); + return find_pte64(env, ctx, h, rw, type, target_page_bits); } #endif - return find_pte2(env, ctx, 0, h, rw, type, target_page_bits); + return find_pte32(env, ctx, h, rw, type, target_page_bits); } /* Perform segment based translation */ From 0480884f1404295ba0d242791e036b05c4957bab Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:09 +0000 Subject: [PATCH 12/58] target-ppc: Disentangle get_segment() The poorly named get_segment() function handles most of the address translation logic for hash-based MMUs. It has many ugly conditionals on whether the MMU is 32-bit or 64-bit. This patch splits the function into 32 and 64-bit versions, using the switch on mmu_type that's already in the caller (get_physical_address()) to select the right one. Most of the original function remains in mmu_helper.c to support the 6xx software loaded TLB implementations (cleaning those up is a project for another day). Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 149 +++++++++++++++++++++++++++++++++++++++- target-ppc/mmu-hash32.h | 4 +- target-ppc/mmu-hash64.c | 92 ++++++++++++++++++++++++- target-ppc/mmu-hash64.h | 5 +- target-ppc/mmu_helper.c | 141 +++++++++++-------------------------- 5 files changed, 280 insertions(+), 111 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index f852e5c67e..4bae72a17a 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -85,8 +85,8 @@ static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, } /* PTE table lookup */ -int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, - int rw, int type, int target_page_bits) +static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, + int rw, int type, int target_page_bits) { hwaddr pteg_off; target_ulong pte0, pte1; @@ -157,3 +157,148 @@ int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, } return ret; } + +int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) +{ + hwaddr hash; + target_ulong vsid; + int ds, pr, target_page_bits; + int ret, ret2; + target_ulong sr, pgidx; + + pr = msr_pr; + ctx->eaddr = eaddr; + + sr = env->sr[eaddr >> 28]; + ctx->key = (((sr & 0x20000000) && (pr != 0)) || + ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; + ds = sr & 0x80000000 ? 1 : 0; + ctx->nx = sr & 0x10000000 ? 1 : 0; + vsid = sr & 0x00FFFFFF; + target_page_bits = TARGET_PAGE_BITS; + LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" + TARGET_FMT_lx " lr=" TARGET_FMT_lx + " ir=%d dr=%d pr=%d %d t=%d\n", + eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, + (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); + + LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", + ctx->key, ds, ctx->nx, vsid); + ret = -1; + if (!ds) { + /* Check if instruction fetch is allowed, if needed */ + if (type != ACCESS_CODE || ctx->nx == 0) { + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); + /* Primary table lookup */ + ret = find_pte32(env, ctx, 0, rw, type, target_page_bits); + if (ret < 0) { + /* Secondary table lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); + ret2 = find_pte32(env, ctx, 1, rw, type, + target_page_bits); + if (ret2 != -1) { + ret = ret2; + } + } +#if defined(DUMP_PAGE_TABLES) + if (qemu_log_enabled()) { + hwaddr curaddr; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + "\n", sdr, mask + 0x80); + for (curaddr = sdr; curaddr < (sdr + mask + 0x80); + curaddr += 16) { + a0 = ldl_phys(curaddr); + a1 = ldl_phys(curaddr + 4); + a2 = ldl_phys(curaddr + 8); + a3 = ldl_phys(curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif + } else { + LOG_MMU("No access allowed\n"); + ret = -3; + } + } else { + target_ulong sr; + + LOG_MMU("direct store...\n"); + /* Direct-store segment : absolutely *BUGGY* for now */ + + /* Direct-store implies a 32-bit MMU. + * Check the Segment Register's bus unit ID (BUID). + */ + sr = env->sr[eaddr >> 28]; + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ + /* Should make the instruction do no-op. + * As it already do no-op, it's quite easy :-) + */ + ctx->raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log("ERROR: instruction should not need " + "address translation\n"); + return -4; + } + if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { + ctx->raddr = eaddr; + ret = 2; + } else { + ret = -2; + } + } + + return ret; +} diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 6bf8f92463..6f9a0c2bf8 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -4,8 +4,8 @@ #ifndef CONFIG_USER_ONLY int pte32_is_valid(target_ulong pte0); -int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, - int rw, int type, int target_page_bits); +int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type); #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index a525bd552f..23eb4801cc 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -44,7 +44,7 @@ * SLB handling */ -ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) +static ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr) { uint64_t esid_256M, esid_1T; int n; @@ -276,8 +276,8 @@ static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, } /* PTE table lookup */ -int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, - int rw, int type, int target_page_bits) +static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, + int rw, int type, int target_page_bits) { hwaddr pteg_off; target_ulong pte0, pte1; @@ -349,3 +349,89 @@ int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, } return ret; } + +int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) +{ + hwaddr hash; + target_ulong vsid; + int pr, target_page_bits; + int ret, ret2; + + pr = msr_pr; + ctx->eaddr = eaddr; + ppc_slb_t *slb; + target_ulong pageaddr; + int segment_bits; + + LOG_MMU("Check SLBs\n"); + slb = slb_lookup(env, eaddr); + if (!slb) { + return -5; + } + + if (slb->vsid & SLB_VSID_B) { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; + segment_bits = 40; + } else { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + segment_bits = 28; + } + + target_page_bits = (slb->vsid & SLB_VSID_L) + ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); + ctx->nx = !!(slb->vsid & SLB_VSID_N); + + pageaddr = eaddr & ((1ULL << segment_bits) + - (1ULL << target_page_bits)); + if (slb->vsid & SLB_VSID_B) { + hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); + } else { + hash = vsid ^ (pageaddr >> target_page_bits); + } + /* Only 5 bits of the page index are used in the AVPN */ + ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | + ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); + + LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n", + ctx->key, ctx->nx, vsid); + ret = -1; + + /* Check if instruction fetch is allowed, if needed */ + if (type != ACCESS_CODE || ctx->nx == 0) { + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); + /* Primary table lookup */ + ret = find_pte64(env, ctx, 0, rw, type, target_page_bits); + if (ret < 0) { + /* Secondary table lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); + ret2 = find_pte64(env, ctx, 1, rw, type, target_page_bits); + if (ret2 != -1) { + ret = ret2; + } + } + } else { + LOG_MMU("No access allowed\n"); + ret = -3; + } + + return ret; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 7b9713da28..690c1d829f 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -4,11 +4,10 @@ #ifndef CONFIG_USER_ONLY #ifdef TARGET_PPC64 -ppc_slb_t *slb_lookup(CPUPPCState *env, target_ulong eaddr); void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, - int rw, int type, int target_page_bits); +int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type); #endif #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 1301391800..db5c15a926 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -507,87 +507,35 @@ hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size) return (hash * pte_size * 8) & env->htab_mask; } -static inline int find_pte(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, - int type, int target_page_bits) -{ -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - return find_pte64(env, ctx, h, rw, type, target_page_bits); - } -#endif - - return find_pte32(env, ctx, h, rw, type, target_page_bits); -} - /* Perform segment based translation */ -static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type) +static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) { hwaddr hash; target_ulong vsid; int ds, pr, target_page_bits; - int ret, ret2; + int ret; + target_ulong sr, pgidx; pr = msr_pr; ctx->eaddr = eaddr; -#if defined(TARGET_PPC64) - if (env->mmu_model & POWERPC_MMU_64) { - ppc_slb_t *slb; - target_ulong pageaddr; - int segment_bits; - LOG_MMU("Check SLBs\n"); - slb = slb_lookup(env, eaddr); - if (!slb) { - return -5; - } + sr = env->sr[eaddr >> 28]; + ctx->key = (((sr & 0x20000000) && (pr != 0)) || + ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; + ds = sr & 0x80000000 ? 1 : 0; + ctx->nx = sr & 0x10000000 ? 1 : 0; + vsid = sr & 0x00FFFFFF; + target_page_bits = TARGET_PAGE_BITS; + LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" + TARGET_FMT_lx " lr=" TARGET_FMT_lx + " ir=%d dr=%d pr=%d %d t=%d\n", + eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, + (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); - if (slb->vsid & SLB_VSID_B) { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; - segment_bits = 40; - } else { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; - segment_bits = 28; - } - - target_page_bits = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) - : (slb->vsid & SLB_VSID_KS)); - ds = 0; - ctx->nx = !!(slb->vsid & SLB_VSID_N); - - pageaddr = eaddr & ((1ULL << segment_bits) - - (1ULL << target_page_bits)); - if (slb->vsid & SLB_VSID_B) { - hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); - } else { - hash = vsid ^ (pageaddr >> target_page_bits); - } - /* Only 5 bits of the page index are used in the AVPN */ - ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | - ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); - } else -#endif /* defined(TARGET_PPC64) */ - { - target_ulong sr, pgidx; - - sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && (pr != 0)) || - ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; - target_page_bits = TARGET_PAGE_BITS; - LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" - TARGET_FMT_lx " lr=" TARGET_FMT_lx - " ir=%d dr=%d pr=%d %d t=%d\n", - eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, rw, type); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; - hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); - } LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", ctx->key, ds, ctx->nx, vsid); ret = -1; @@ -603,31 +551,8 @@ static inline int get_segment(CPUPPCState *env, mmu_ctx_t *ctx, /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; - if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx || - env->mmu_model == POWERPC_MMU_SOFT_74xx)) { - /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); - } else { - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte(env, ctx, 0, rw, type, target_page_bits); - if (ret < 0) { - /* Secondary table lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte(env, ctx, 1, rw, type, - target_page_bits); - if (ret2 != -1) { - ret = ret2; - } - } - } + /* Software TLB search */ + ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); #if defined(DUMP_PAGE_TABLES) if (qemu_log_enabled()) { hwaddr curaddr; @@ -1415,22 +1340,36 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: + /* Try to find a BAT */ + if (env->nb_BATs != 0) { + ret = get_bat(env, ctx, eaddr, rw, access_type); + } + if (ret < 0) { + /* We didn't match any BAT entry or don't have BATs */ + ret = get_segment32(env, ctx, eaddr, rw, access_type); + } + break; + case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: /* Try to find a BAT */ if (env->nb_BATs != 0) { ret = get_bat(env, ctx, eaddr, rw, access_type); } + if (ret < 0) { + /* We didn't match any BAT entry or don't have BATs */ + ret = get_segment_6xx_tlb(env, ctx, eaddr, rw, access_type); + } + break; + #if defined(TARGET_PPC64) case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: -#endif - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment(env, ctx, eaddr, rw, access_type); - } + ret = get_segment64(env, ctx, eaddr, rw, access_type); break; +#endif + case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: ret = mmu40x_get_physical_address(env, ctx, eaddr, From 44bc910794eff956ceba0030f0751a26bed748b5 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:10 +0000 Subject: [PATCH 13/58] target-ppc: Rework get_physical_address() Currently get_physical_address() first checks to see if translation is enabled in the MSR, then in the translation on case switches on the mmu type. Except that for BookE MMUs, translation is always on, and so it has to switch in the "translation off" case as well and do the same thing as the translation on path for those MMUs. Plus, even translation off doesn't behave exactly the same on the various MMU types so there are further mmu type checks in the "translation off" path. As a first step to cleaning this up, this patch moves the switch on mmu type to the top level, then makes the translation on/off check just for those mmu types where it is meaningful. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu_helper.c | 102 +++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index db5c15a926..5b82731133 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1316,30 +1316,20 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int access_type) { - int ret; + int ret = -1; + bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) + || (access_type != ACCESS_CODE && msr_dr == 0); #if 0 qemu_log("%s\n", __func__); #endif - if ((access_type == ACCESS_CODE && msr_ir == 0) || - (access_type != ACCESS_CODE && msr_dr == 0)) { - if (env->mmu_model == POWERPC_MMU_BOOKE) { - /* The BookE MMU always performs address translation. The - IS and DS bits only affect the address space. */ - ret = mmubooke_get_physical_address(env, ctx, eaddr, - rw, access_type); - } else if (env->mmu_model == POWERPC_MMU_BOOKE206) { - ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type); - } else { - /* No address translation. */ + + switch (env->mmu_model) { + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + if (real_mode) { ret = check_physical(env, ctx, eaddr, rw); - } - } else { - ret = -1; - switch (env->mmu_model) { - case POWERPC_MMU_32B: - case POWERPC_MMU_601: + } else { /* Try to find a BAT */ if (env->nb_BATs != 0) { ret = get_bat(env, ctx, eaddr, rw, access_type); @@ -1348,10 +1338,14 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* We didn't match any BAT entry or don't have BATs */ ret = get_segment32(env, ctx, eaddr, rw, access_type); } - break; + } + break; - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { /* Try to find a BAT */ if (env->nb_BATs != 0) { ret = get_bat(env, ctx, eaddr, rw, access_type); @@ -1360,40 +1354,52 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* We didn't match any BAT entry or don't have BATs */ ret = get_segment_6xx_tlb(env, ctx, eaddr, rw, access_type); } - break; + } + break; #if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_06d: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { ret = get_segment64(env, ctx, eaddr, rw, access_type); - break; + } + break; #endif - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: + case POWERPC_MMU_SOFT_4xx: + case POWERPC_MMU_SOFT_4xx_Z: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { ret = mmu40x_get_physical_address(env, ctx, eaddr, rw, access_type); - break; - case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, - rw, access_type); - break; - case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_REAL: - cpu_abort(env, "PowerPC in real mode do not do any translation\n"); - return -1; - default: - cpu_abort(env, "Unknown or invalid MMU model\n"); - return -1; } + break; + case POWERPC_MMU_BOOKE: + ret = mmubooke_get_physical_address(env, ctx, eaddr, + rw, access_type); + break; + case POWERPC_MMU_BOOKE206: + ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, + access_type); + break; + case POWERPC_MMU_MPC8xx: + /* XXX: TODO */ + cpu_abort(env, "MPC8xx MMU model is not implemented\n"); + break; + case POWERPC_MMU_REAL: + if (real_mode) { + ret = check_physical(env, ctx, eaddr, rw); + } else { + cpu_abort(env, "PowerPC in real mode do not do any translation\n"); + } + return -1; + default: + cpu_abort(env, "Unknown or invalid MMU model\n"); + return -1; } #if 0 qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n", From 629bd516fda67c95ba1c7d1393bacb9e68ea0712 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:11 +0000 Subject: [PATCH 14/58] target-ppc: Disentangle get_physical_address() paths Depending on the MSR state, for 64-bit hash MMUs, get_physical_address can either call check_physical (which has further tests for mmu type) or get_segment64. Similarly for 32-bit hash MMUs we can either call check_physucal or get_bat() and get_segment32(). This patch splits off the whole get_physical_addresss() path for hash MMUs into 32-bit and 64-bit versions, handling real mode correctly for such MMUs without going to check_physical and rechecking the mmu type. Correspondingly, the hash MMU specific paths in check_physical() are removed. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 2 ++ target-ppc/mmu-hash32.c | 29 ++++++++++++++++++++++++-- target-ppc/mmu-hash32.h | 4 ++-- target-ppc/mmu-hash64.c | 19 +++++++++++++++-- target-ppc/mmu-hash64.h | 4 ++-- target-ppc/mmu_helper.c | 46 +++++++---------------------------------- 6 files changed, 58 insertions(+), 46 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index cf8ba2e5e3..716ffe08b2 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1137,6 +1137,8 @@ int pp_check(int key, int pp, int nx); int check_prot(int prot, int rw, int access_type); int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, int ret, int rw); hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size); +int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 4bae72a17a..3998d635f2 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -158,8 +158,8 @@ static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, return ret; } -int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type) +static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) { hwaddr hash; target_ulong vsid; @@ -302,3 +302,28 @@ int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } + +int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type) +{ + bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) + || (access_type != ACCESS_CODE && msr_dr == 0); + + if (real_mode) { + ctx->raddr = eaddr; + ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; + return 0; + } else { + int ret = -1; + + /* Try to find a BAT */ + if (env->nb_BATs != 0) { + ret = get_bat(env, ctx, eaddr, rw, access_type); + } + if (ret < 0) { + /* We didn't match any BAT entry or don't have BATs */ + ret = get_segment32(env, ctx, eaddr, rw, access_type); + } + return ret; + } +} diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 6f9a0c2bf8..1318562107 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -4,8 +4,8 @@ #ifndef CONFIG_USER_ONLY int pte32_is_valid(target_ulong pte0); -int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type); +int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type); #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 23eb4801cc..c7272982b8 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -350,8 +350,8 @@ static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, return ret; } -int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type) +static int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int type) { hwaddr hash; target_ulong vsid; @@ -435,3 +435,18 @@ int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } + +int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type) +{ + bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) + || (access_type != ACCESS_CODE && msr_dr == 0); + + if (real_mode) { + ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; + return 0; + } else { + return get_segment64(env, ctx, eaddr, rw, access_type); + } +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 690c1d829f..d8eb8ded8a 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -6,8 +6,8 @@ #ifdef TARGET_PPC64 void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type); +int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type); #endif #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 5b82731133..ce39f494c5 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -426,8 +426,8 @@ static inline void bat_601_size_prot(CPUPPCState *env, target_ulong *blp, *protp = prot; } -static inline int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, int rw, int type) +int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; @@ -1256,8 +1256,6 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, ctx->prot = PAGE_READ | PAGE_EXEC; ret = 0; switch (env->mmu_model) { - case POWERPC_MMU_32B: - case POWERPC_MMU_601: case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: case POWERPC_MMU_SOFT_4xx: @@ -1265,15 +1263,7 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, case POWERPC_MMU_BOOKE: ctx->prot |= PAGE_WRITE; break; -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_06d: - /* Real address are 60 bits long */ - ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL; - ctx->prot |= PAGE_WRITE; - break; -#endif + case POWERPC_MMU_SOFT_4xx_Z: if (unlikely(msr_pe != 0)) { /* 403 family add some particular protections, @@ -1298,15 +1288,10 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, } } break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env, "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_BOOKE206: - cpu_abort(env, "BookE 2.06 MMU doesn't have physical real mode\n"); - break; + default: - cpu_abort(env, "Unknown or invalid MMU model\n"); + /* Caller's checks mean we should never get here for other models */ + abort(); return -1; } @@ -1327,18 +1312,7 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, rw); - } else { - /* Try to find a BAT */ - if (env->nb_BATs != 0) { - ret = get_bat(env, ctx, eaddr, rw, access_type); - } - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment32(env, ctx, eaddr, rw, access_type); - } - } + ret = ppc_hash32_get_physical_address(env, ctx, eaddr, rw, access_type); break; case POWERPC_MMU_SOFT_6xx: @@ -1361,11 +1335,7 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, case POWERPC_MMU_64B: case POWERPC_MMU_2_06: case POWERPC_MMU_2_06d: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, rw); - } else { - ret = get_segment64(env, ctx, eaddr, rw, access_type); - } + ret = ppc_hash64_get_physical_address(env, ctx, eaddr, rw, access_type); break; #endif From 25de24ab838be5801d5cc13b8a347922a3770fa5 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:12 +0000 Subject: [PATCH 15/58] target-ppc: Disentangle hash mmu paths for cpu_ppc_handle_mmu_fault cpu_ppc_handle_mmu_fault() calls get_physical_address() (whose behaviour depends on MMU type) then, if that fails, issues an appropriate exception - which again has a number of dependencies on MMU type. This patch starts converting cpu_ppc_handle_mmu_fault() to have a single switch on MMU type, calling MMU specific fault handler functions which deal with both translation and exception delivery appropriately for the MMU type. We convert 32-bit and 64-bit hash MMUs to this new model, but the existing code is left in place for other MMU types for now. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 124 ++++++++++++++++++++++++++++++++++++++++ target-ppc/mmu-hash32.h | 2 + target-ppc/mmu-hash64.c | 87 ++++++++++++++++++++++++++++ target-ppc/mmu-hash64.h | 2 + target-ppc/mmu_helper.c | 57 ++++++------------ 5 files changed, 231 insertions(+), 41 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 3998d635f2..50f8c5495c 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -303,6 +303,7 @@ static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } + int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int access_type) { @@ -327,3 +328,126 @@ int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } } + +int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx) +{ + mmu_ctx_t ctx; + int access_type; + int ret = 0; + + if (rw == 2) { + /* code access */ + rw = 0; + access_type = ACCESS_CODE; + } else { + /* data access */ + access_type = env->access_type; + } + ret = ppc_hash32_get_physical_address(env, &ctx, address, rw, access_type); + if (ret == 0) { + tlb_set_page(env, address & TARGET_PAGE_MASK, + ctx.raddr & TARGET_PAGE_MASK, ctx.prot, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + } else if (ret < 0) { + LOG_MMU_STATE(env); + if (access_type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + break; + case -2: + /* Access rights violation */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + case -3: + /* No execute protection violation */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x42000000; + } else { + env->spr[SPR_DSISR] = 0x40000000; + } + break; + case -2: + /* Access rights violation */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + break; + case -4: + /* Direct store exception */ + switch (access_type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + env->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = address; + break; + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x06000000; + } else { + env->spr[SPR_DSISR] = 0x04000000; + } + break; + case ACCESS_EXT: + /* eciwx or ecowx */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; + } + break; + default: + printf("DSI: invalid exception (%d)\n", ret); + env->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = address; + break; + } + break; + } + } +#if 0 + printf("%s: set exception to %d %02x\n", __func__, + env->exception, env->error_code); +#endif + ret = 1; + } + + return ret; +} diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 1318562107..8f1f2a92a3 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -6,6 +6,8 @@ int pte32_is_valid(target_ulong pte0); int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int access_type); +int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx); #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index c7272982b8..32825ff04e 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -450,3 +450,90 @@ int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, return get_segment64(env, ctx, eaddr, rw, access_type); } } + +int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx) +{ + mmu_ctx_t ctx; + int access_type; + int ret = 0; + + if (rw == 2) { + /* code access */ + rw = 0; + access_type = ACCESS_CODE; + } else { + /* data access */ + access_type = env->access_type; + } + ret = ppc_hash64_get_physical_address(env, &ctx, address, rw, access_type); + if (ret == 0) { + tlb_set_page(env, address & TARGET_PAGE_MASK, + ctx.raddr & TARGET_PAGE_MASK, ctx.prot, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + } else if (ret < 0) { + LOG_MMU_STATE(env); + if (access_type == ACCESS_CODE) { + switch (ret) { + case -1: + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + break; + case -2: + /* Access rights violation */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + case -3: + /* No execute protection violation */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + case -5: + /* No match in segment table */ + env->exception_index = POWERPC_EXCP_ISEG; + env->error_code = 0; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x42000000; + } else { + env->spr[SPR_DSISR] = 0x40000000; + } + break; + case -2: + /* Access rights violation */ + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + break; + case -5: + /* No match in segment table */ + env->exception_index = POWERPC_EXCP_DSEG; + env->error_code = 0; + env->spr[SPR_DAR] = address; + break; + } + } +#if 0 + printf("%s: set exception to %d %02x\n", __func__, + env->exception, env->error_code); +#endif + ret = 1; + } + + return ret; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index d8eb8ded8a..3a53e61673 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -8,6 +8,8 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int access_type); +int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx); #endif #endif /* CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index ce39f494c5..287334ffb9 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1438,6 +1438,22 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int access_type; int ret = 0; + switch (env->mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: + return ppc_hash64_handle_mmu_fault(env, address, rw, mmu_idx); +#endif + + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + return ppc_hash32_handle_mmu_fault(env, address, rw, mmu_idx); + + default: + ; /* Otherwise fall through to the general code below */ + } + if (rw == 2) { /* code access */ rw = 0; @@ -1475,16 +1491,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->spr[SPR_40x_DEAR] = address; env->spr[SPR_40x_ESR] = 0x00000000; break; - case POWERPC_MMU_32B: - case POWERPC_MMU_601: -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_06d: -#endif - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x40000000; - break; case POWERPC_MMU_BOOKE206: booke206_update_mas_tlb_miss(env, address, rw); /* fall through */ @@ -1526,13 +1532,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x10000000; break; -#if defined(TARGET_PPC64) - case -5: - /* No match in segment table */ - env->exception_index = POWERPC_EXCP_ISEG; - env->error_code = 0; - break; -#endif } } else { switch (ret) { @@ -1580,22 +1579,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->spr[SPR_40x_ESR] = 0x00000000; } break; - case POWERPC_MMU_32B: - case POWERPC_MMU_601: -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_06d: -#endif - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rw == 1) { - env->spr[SPR_DSISR] = 0x42000000; - } else { - env->spr[SPR_DSISR] = 0x40000000; - } - break; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); @@ -1681,14 +1664,6 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, break; } break; -#if defined(TARGET_PPC64) - case -5: - /* No match in segment table */ - env->exception_index = POWERPC_EXCP_DSEG; - env->error_code = 0; - env->spr[SPR_DAR] = address; - break; -#endif } } #if 0 From f2ad6be83bc284d6c7677bdca879db38d4fdccd5 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:13 +0000 Subject: [PATCH 16/58] target-ppc: Disentangle hash mmu versions of cpu_get_phys_page_debug() cpu_get_phys_page_debug() is a trivial wrapper around get_physical_address(). But even the signature of get_physical_address() has some things we'd like to clean up on a per-mmu basis, so this patch moves the test on mmu model out to cpu_get_phys_page_debug(), moving the version for 64-bit hash MMUs out to mmu-hash64.c and the version for 32-bit hash MMUs to mmu-hash32.c Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 17 +++++++++++++++-- target-ppc/mmu-hash32.h | 3 +-- target-ppc/mmu-hash64.c | 17 +++++++++++++++-- target-ppc/mmu-hash64.h | 3 +-- target-ppc/mmu_helper.c | 29 ++++++++++++++++------------- 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 50f8c5495c..c0e5742f16 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -304,8 +304,9 @@ static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, } -int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) +static int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, + int access_type) { bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) || (access_type != ACCESS_CODE && msr_dr == 0); @@ -329,6 +330,18 @@ int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } } +hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) +{ + mmu_ctx_t ctx; + + if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) + != 0)) { + return -1; + } + + return ctx.raddr & TARGET_PAGE_MASK; +} + int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx) { diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 8f1f2a92a3..8f10e0d15c 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -4,8 +4,7 @@ #ifndef CONFIG_USER_ONLY int pte32_is_valid(target_ulong pte0); -int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type); +hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr); int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 32825ff04e..427b09597f 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -436,8 +436,9 @@ static int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) +static int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, + int access_type) { bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) || (access_type != ACCESS_CODE && msr_dr == 0); @@ -451,6 +452,18 @@ int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } } +hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) +{ + mmu_ctx_t ctx; + + if (unlikely(ppc_hash64_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) + != 0)) { + return -1; + } + + return ctx.raddr & TARGET_PAGE_MASK; +} + int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx) { diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 3a53e61673..665d3b0247 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -6,8 +6,7 @@ #ifdef TARGET_PPC64 void dump_slb(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs); -int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type); +hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr); int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); #endif diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 287334ffb9..818f1b595f 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1310,11 +1310,6 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, #endif switch (env->mmu_model) { - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - ret = ppc_hash32_get_physical_address(env, ctx, eaddr, rw, access_type); - break; - case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: if (real_mode) { @@ -1331,14 +1326,6 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } break; -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_06d: - ret = ppc_hash64_get_physical_address(env, ctx, eaddr, rw, access_type); - break; -#endif - case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: if (real_mode) { @@ -1383,6 +1370,22 @@ hwaddr cpu_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { mmu_ctx_t ctx; + switch (env->mmu_model) { +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06d: + return ppc_hash64_get_phys_page_debug(env, addr); +#endif + + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + return ppc_hash32_get_phys_page_debug(env, addr); + + default: + ; + } + if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) { return -1; } From 496272a7018ba01aa2b87a1a5ed866ff85133401 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:14 +0000 Subject: [PATCH 17/58] target-ppc: Disentangle hash mmu helper functions The newly separated paths for hash mmus rely on several helper functions which are still shared with 32-bit hash mmus: pp_check(), check_prot() and pte_update_flags(). While these don't have ugly ifdefs on the mmu type, they're not very well thought out, so sharing them impedes cleaning up the hash mmu paths. For now, put near-duplicate versions into mmu-hash64.c and mmu-hash32.c, leaving the old version in mmu_helper.c for 6xx software loaded tlb implementations. The hash 32 and software loaded implementations are simplfied slightly, using the fact that no 32-bit CPUs implement the 3rd page protection bit. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 3 -- target-ppc/mmu-hash32.c | 96 +++++++++++++++++++++++++++++++++++++-- target-ppc/mmu-hash64.c | 99 +++++++++++++++++++++++++++++++++++++++-- target-ppc/mmu_helper.c | 11 ++--- 4 files changed, 193 insertions(+), 16 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 716ffe08b2..41cd5d6aba 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1133,9 +1133,6 @@ void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); -int pp_check(int key, int pp, int nx); -int check_prot(int prot, int rw, int access_type); -int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, int ret, int rw); hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size); int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong virtual, int rw, int type); diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index c0e5742f16..4b7598bdfc 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -37,6 +37,71 @@ #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) +static int ppc_hash32_pp_check(int key, int pp, int nx) +{ + int access; + + /* Compute access rights */ + access = 0; + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + access |= PAGE_WRITE; + /* No break here */ + case 0x3: + access |= PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (nx == 0) { + access |= PAGE_EXEC; + } + + return access; +} + +static int ppc_hash32_check_prot(int prot, int rw, int access_type) +{ + int ret; + + if (access_type == ACCESS_CODE) { + if (prot & PAGE_EXEC) { + ret = 0; + } else { + ret = -2; + } + } else if (rw) { + if (prot & PAGE_WRITE) { + ret = 0; + } else { + ret = -2; + } + } else { + if (prot & PAGE_READ) { + ret = 0; + } else { + ret = -2; + } + } + + return ret; +} + static inline int pte_is_valid_hash32(target_ulong pte0) { return pte0 & 0x80000000 ? 1 : 0; @@ -66,11 +131,11 @@ static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, } } /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); + access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; - ret = check_prot(ctx->prot, rw, type); + ret = ppc_hash32_check_prot(ctx->prot, rw, type); if (ret == 0) { /* Access granted */ LOG_MMU("PTE access granted !\n"); @@ -84,6 +149,31 @@ static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, return ret; } +static int ppc_hash32_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) +{ + int store = 0; + + /* Update page flags */ + if (!(*pte1p & 0x00000100)) { + /* Update accessed flag */ + *pte1p |= 0x00000100; + store = 1; + } + if (!(*pte1p & 0x00000080)) { + if (rw == 1 && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + store = 1; + } else { + /* Force page fault for first write access */ + ctx->prot &= ~PAGE_WRITE; + } + } + + return store; +} + /* PTE table lookup */ static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, int type, int target_page_bits) @@ -138,7 +228,7 @@ static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, ctx->raddr, ctx->prot, ret); /* Update page flags */ pte1 = ctx->raddr; - if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { + if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rw) == 1) { if (env->external_htab) { stl_p(env->external_htab + pteg_off + (good * 8) + 4, pte1); diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 427b09597f..f9c5b099b4 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -226,6 +226,74 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) #define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL #define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) +static int ppc_hash64_pp_check(int key, int pp, int nx) +{ + int access; + + /* Compute access rights */ + /* When pp is 4, 5 or 7, the result is undefined. Set it to noaccess */ + access = 0; + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + access |= PAGE_WRITE; + /* No break here */ + case 0x3: + case 0x6: + access |= PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + case 0x6: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (nx == 0) { + access |= PAGE_EXEC; + } + + return access; +} + +static int ppc_hash64_check_prot(int prot, int rw, int access_type) +{ + int ret; + + if (access_type == ACCESS_CODE) { + if (prot & PAGE_EXEC) { + ret = 0; + } else { + ret = -2; + } + } else if (rw) { + if (prot & PAGE_WRITE) { + ret = 0; + } else { + ret = -2; + } + } else { + if (prot & PAGE_READ) { + ret = 0; + } else { + ret = -2; + } + } + + return ret; +} + static inline int pte64_is_valid(target_ulong pte0) { return pte0 & 0x0000000000000001ULL ? 1 : 0; @@ -257,11 +325,11 @@ static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, } } /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); + access = ppc_hash64_pp_check(ctx->key, pp, ctx->nx); /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; - ret = check_prot(ctx->prot, rw, type); + ret = ppc_hash64_check_prot(ctx->prot, rw, type); if (ret == 0) { /* Access granted */ LOG_MMU("PTE access granted !\n"); @@ -275,6 +343,31 @@ static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, return ret; } +static int ppc_hash64_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) +{ + int store = 0; + + /* Update page flags */ + if (!(*pte1p & 0x00000100)) { + /* Update accessed flag */ + *pte1p |= 0x00000100; + store = 1; + } + if (!(*pte1p & 0x00000080)) { + if (rw == 1 && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + store = 1; + } else { + /* Force page fault for first write access */ + ctx->prot &= ~PAGE_WRITE; + } + } + + return store; +} + /* PTE table lookup */ static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, int type, int target_page_bits) @@ -330,7 +423,7 @@ static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, ctx->raddr, ctx->prot, ret); /* Update page flags */ pte1 = ctx->raddr; - if (pte_update_flags(ctx, &pte1, ret, rw) == 1) { + if (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rw) == 1) { if (env->external_htab) { stq_p(env->external_htab + pteg_off + (good * 16) + 8, pte1); diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 818f1b595f..2deb635d75 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -91,12 +91,11 @@ static inline void pte_invalidate(target_ulong *pte0) #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) -int pp_check(int key, int pp, int nx) +static int pp_check(int key, int pp, int nx) { int access; /* Compute access rights */ - /* When pp is 3/7, the result is undefined. Set it to noaccess */ access = 0; if (key == 0) { switch (pp) { @@ -106,14 +105,12 @@ int pp_check(int key, int pp, int nx) access |= PAGE_WRITE; /* No break here */ case 0x3: - case 0x6: access |= PAGE_READ; break; } } else { switch (pp) { case 0x0: - case 0x6: access = 0; break; case 0x1: @@ -132,7 +129,7 @@ int pp_check(int key, int pp, int nx) return access; } -int check_prot(int prot, int rw, int access_type) +static int check_prot(int prot, int rw, int access_type) { int ret; @@ -201,8 +198,8 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, return ret; } -int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, int rw) +static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) { int store = 0; From 59191721a16ae393c01280dc633937374cdf474e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:15 +0000 Subject: [PATCH 18/58] target-ppc: Don't share get_pteg_offset() between 32 and 64-bit The get_pteg_offset() helper function is currently shared between 32-bit and 64-bit hash mmus, taking a parameter for the hash pte size. In the 64-bit paths, it's only called in one place, and it's a trivial calculation. This patch, therefore, open codes it for 64-bit. The remaining version, which is used in two places is made 32-bit only and moved to mmu-hash32.c. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 1 - target-ppc/mmu-hash32.c | 7 ++++++- target-ppc/mmu-hash32.h | 2 +- target-ppc/mmu-hash64.c | 2 +- target-ppc/mmu_helper.c | 9 ++------- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 41cd5d6aba..86e8978ff8 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1133,7 +1133,6 @@ void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); -hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size); int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong virtual, int rw, int type); #endif /* !defined(CONFIG_USER_ONLY) */ diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 4b7598bdfc..0ccaf7cf06 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -174,6 +174,11 @@ static int ppc_hash32_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, return store; } +hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) +{ + return (hash * HASH_PTE_SIZE_32 * 8) & env->htab_mask; +} + /* PTE table lookup */ static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, int rw, int type, int target_page_bits) @@ -184,7 +189,7 @@ static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, int ret, r; ret = -1; /* No entry found */ - pteg_off = get_pteg_offset(env, ctx->hash[h], HASH_PTE_SIZE_32); + pteg_off = get_pteg_offset32(env, ctx->hash[h]); for (i = 0; i < 8; i++) { if (env->external_htab) { pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 8f10e0d15c..3435aa5513 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -3,7 +3,7 @@ #ifndef CONFIG_USER_ONLY -int pte32_is_valid(target_ulong pte0); +hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash); hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr); int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index f9c5b099b4..7134616f2c 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -378,7 +378,7 @@ static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, int ret, r; ret = -1; /* No entry found */ - pteg_off = get_pteg_offset(env, ctx->hash[h], HASH_PTE_SIZE_64); + pteg_off = (ctx->hash[h] * HASH_PTE_SIZE_64 * 8) & env->htab_mask; for (i = 0; i < 8; i++) { if (env->external_htab) { pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 2deb635d75..50ec0ace7d 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -499,11 +499,6 @@ int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -hwaddr get_pteg_offset(CPUPPCState *env, hwaddr hash, int pte_size) -{ - return (hash * pte_size * 8) & env->htab_mask; -} - /* Perform segment based translation */ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int type) @@ -1551,9 +1546,9 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, tlb_miss: env->error_code |= ctx.key << 19; env->spr[SPR_HASH1] = env->htab_base + - get_pteg_offset(env, ctx.hash[0], HASH_PTE_SIZE_32); + get_pteg_offset32(env, ctx.hash[0]); env->spr[SPR_HASH2] = env->htab_base + - get_pteg_offset(env, ctx.hash[1], HASH_PTE_SIZE_32); + get_pteg_offset32(env, ctx.hash[1]); break; case POWERPC_MMU_SOFT_74xx: if (rw == 1) { From 9813279664162fa50d6124fe8c5ac4871fa59c13 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:16 +0000 Subject: [PATCH 19/58] target-ppc: Disentangle BAT code for 32-bit hash MMUs The functions for looking up BATs (Block Address Translation - essentially a level 0 TLB) are shared between the classic 32-bit hash MMUs and the 6xx style software loaded TLB implementations. This patch splits out a copy for the 32-bit hash MMUs, to facilitate cleaning it up. The remaining version is left, but cleaned up slightly to no longer deal with PowerPC 601 peculiarities (601 has a hash MMU). Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 2 - target-ppc/mmu-hash32.c | 140 +++++++++++++++++++++++++++++++++++++++- target-ppc/mmu_helper.c | 38 ++--------- 3 files changed, 142 insertions(+), 38 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 86e8978ff8..a3e1362b7a 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1133,8 +1133,6 @@ void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); -int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, int rw, int type); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 0ccaf7cf06..f94b65dce8 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -25,6 +25,7 @@ #include "mmu-hash32.h" //#define DEBUG_MMU +//#define DEBUG_BAT #ifdef DEBUG_MMU # define LOG_MMU(...) qemu_log(__VA_ARGS__) @@ -34,6 +35,12 @@ # define LOG_MMU_STATE(...) do { } while (0) #endif +#ifdef DEBUG_BATS +# define LOG_BATS(...) qemu_log(__VA_ARGS__) +#else +# define LOG_BATS(...) do { } while (0) +#endif + #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) @@ -102,6 +109,136 @@ static int ppc_hash32_check_prot(int prot, int rw, int access_type) return ret; } +/* Perform BAT hit & translation */ +static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp, + int *validp, int *protp, target_ulong *BATu, + target_ulong *BATl) +{ + target_ulong bl; + int pp, valid, prot; + + bl = (*BATu & 0x00001FFC) << 15; + valid = 0; + prot = 0; + if (((msr_pr == 0) && (*BATu & 0x00000002)) || + ((msr_pr != 0) && (*BATu & 0x00000001))) { + valid = 1; + pp = *BATl & 0x00000003; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, + int *validp, int *protp, + target_ulong *BATu, target_ulong *BATl) +{ + target_ulong bl; + int key, pp, valid, prot; + + bl = (*BATl & 0x0000003F) << 17; + LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", + (uint8_t)(*BATl & 0x0000003F), bl, ~bl); + prot = 0; + valid = (*BATl >> 6) & 1; + if (valid) { + pp = *BATu & 0x00000003; + if (msr_pr == 0) { + key = (*BATu >> 3) & 1; + } else { + key = (*BATu >> 2) & 1; + } + prot = ppc_hash32_pp_check(key, pp, 0); + } + *blp = bl; + *validp = valid; + *protp = prot; +} + +static int ppc_hash32_get_bat(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type) +{ + target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong BEPIl, BEPIu, bl; + int i, valid, prot; + int ret = -1; + + LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', virtual); + switch (type) { + case ACCESS_CODE: + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + break; + default: + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + break; + } + for (i = 0; i < env->nb_BATs; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + hash32_bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); + } else { + hash32_bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + } + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); + if ((virtual & 0xF0000000) == BEPIu && + ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + /* BAT matches */ + if (valid != 0) { + /* Get physical address */ + ctx->raddr = (*BATl & 0xF0000000) | + ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + (virtual & 0x0001F000); + /* Compute access rights */ + ctx->prot = prot; + ret = ppc_hash32_check_prot(ctx->prot, rw, type); + if (ret == 0) { + LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", + i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', + ctx->prot & PAGE_WRITE ? 'W' : '-'); + } + break; + } + } + } + if (ret < 0) { +#if defined(DEBUG_BATS) + if (qemu_log_enabled()) { + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, + *BATu, *BATl, BEPIu, BEPIl, bl); + } + } +#endif + } + /* No hit */ + return ret; +} + + static inline int pte_is_valid_hash32(target_ulong pte0) { return pte0 & 0x80000000 ? 1 : 0; @@ -398,7 +535,6 @@ static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } - static int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw, int access_type) @@ -415,7 +551,7 @@ static int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, /* Try to find a BAT */ if (env->nb_BATs != 0) { - ret = get_bat(env, ctx, eaddr, rw, access_type); + ret = ppc_hash32_get_bat(env, ctx, eaddr, rw, access_type); } if (ret < 0) { /* We didn't match any BAT entry or don't have BATs */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 50ec0ace7d..9471d59674 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -397,34 +397,8 @@ static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, *protp = prot; } -static inline void bat_601_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, - target_ulong *BATu, target_ulong *BATl) -{ - target_ulong bl; - int key, pp, valid, prot; - - bl = (*BATl & 0x0000003F) << 17; - LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", - (uint8_t)(*BATl & 0x0000003F), bl, ~bl); - prot = 0; - valid = (*BATl >> 6) & 1; - if (valid) { - pp = *BATu & 0x00000003; - if (msr_pr == 0) { - key = (*BATu >> 3) & 1; - } else { - key = (*BATu >> 2) & 1; - } - prot = pp_check(key, pp, 0); - } - *blp = bl; - *validp = valid; - *protp = prot; -} - -int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, int rw, int type) +static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong virtual, int rw, int type) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; @@ -448,11 +422,7 @@ int get_bat(CPUPPCState *env, mmu_ctx_t *ctx, BATl = &BATlt[i]; BEPIu = *BATu & 0xF0000000; BEPIl = *BATu & 0x0FFE0000; - if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); - } else { - bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); - } + bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); @@ -1309,7 +1279,7 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } else { /* Try to find a BAT */ if (env->nb_BATs != 0) { - ret = get_bat(env, ctx, eaddr, rw, access_type); + ret = get_bat_6xx_tlb(env, ctx, eaddr, rw, access_type); } if (ret < 0) { /* We didn't match any BAT entry or don't have BATs */ From 5dc68eb0e4e41462bf93cf5c67fe4045571fc7bf Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:17 +0000 Subject: [PATCH 20/58] target-ppc: mmu_ctx_t should not be a global type mmu_ctx_t is currently defined in cpu.h. However it is used for temporary information relating to mmu translation, and is only used in mmu_helper.c and (now) mmu-hash{32,64}.c. Furthermore it contains information which should be specific to particular MMU types. Therefore, move its definition to mmu_helper.c. mmu-hash{32,64}.c are converted to use new data types private to the relevant MMUs (identical to mmu_ctx_t for now, but that will change in future patches). Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 14 -------------- target-ppc/mmu-hash32.c | 26 ++++++++++++++++++-------- target-ppc/mmu-hash64.c | 26 +++++++++++++++++++------- target-ppc/mmu_helper.c | 13 +++++++++++++ 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index a3e1362b7a..e5f46b3418 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1101,20 +1101,6 @@ do { \ env->wdt_period[3] = (d_); \ } while (0) -#if !defined(CONFIG_USER_ONLY) -/* Context used internally during MMU translations */ -typedef struct mmu_ctx_t mmu_ctx_t; -struct mmu_ctx_t { - hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ - 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 */ -}; -#endif - #include "cpu-qom.h" /*****************************************************************************/ diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index f94b65dce8..5b48aa8486 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -41,6 +41,16 @@ # define LOG_BATS(...) do { } while (0) #endif +struct mmu_ctx_hash32 { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + 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 */ +}; + #define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) @@ -162,7 +172,7 @@ static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, *protp = prot; } -static int ppc_hash32_get_bat(CPUPPCState *env, mmu_ctx_t *ctx, +static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong virtual, int rw, int type) { target_ulong *BATlt, *BATut, *BATu, *BATl; @@ -244,7 +254,7 @@ static inline int pte_is_valid_hash32(target_ulong pte0) return pte0 & 0x80000000 ? 1 : 0; } -static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, +static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, target_ulong pte1, int h, int rw, int type) { target_ulong ptem, mmask; @@ -286,7 +296,7 @@ static int pte_check_hash32(mmu_ctx_t *ctx, target_ulong pte0, return ret; } -static int ppc_hash32_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, +static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong *pte1p, int ret, int rw) { int store = 0; @@ -317,7 +327,7 @@ hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) } /* PTE table lookup */ -static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, +static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, int rw, int type, int target_page_bits) { hwaddr pteg_off; @@ -390,7 +400,7 @@ static int find_pte32(CPUPPCState *env, mmu_ctx_t *ctx, int h, return ret; } -static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, +static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong eaddr, int rw, int type) { hwaddr hash; @@ -535,7 +545,7 @@ static int get_segment32(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -static int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, +static int ppc_hash32_get_physical_address(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong eaddr, int rw, int access_type) { @@ -563,7 +573,7 @@ static int ppc_hash32_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { - mmu_ctx_t ctx; + struct mmu_ctx_hash32 ctx; if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) { @@ -576,7 +586,7 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx) { - mmu_ctx_t ctx; + struct mmu_ctx_hash32 ctx; int access_type; int ret = 0; diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 7134616f2c..3008be8ee9 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -40,6 +40,16 @@ # define LOG_SLB(...) do { } while (0) #endif +struct mmu_ctx_hash64 { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + 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 */ +}; + /* * SLB handling */ @@ -299,7 +309,7 @@ static inline int pte64_is_valid(target_ulong pte0) return pte0 & 0x0000000000000001ULL ? 1 : 0; } -static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, +static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, target_ulong pte1, int h, int rw, int type) { target_ulong ptem, mmask; @@ -343,7 +353,8 @@ static int pte64_check(mmu_ctx_t *ctx, target_ulong pte0, return ret; } -static int ppc_hash64_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, +static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, + target_ulong *pte1p, int ret, int rw) { int store = 0; @@ -369,7 +380,7 @@ static int ppc_hash64_pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, } /* PTE table lookup */ -static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, +static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, int rw, int type, int target_page_bits) { hwaddr pteg_off; @@ -443,7 +454,7 @@ static int find_pte64(CPUPPCState *env, mmu_ctx_t *ctx, int h, return ret; } -static int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, +static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, target_ulong eaddr, int rw, int type) { hwaddr hash; @@ -529,7 +540,8 @@ static int get_segment64(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -static int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, +static int ppc_hash64_get_physical_address(CPUPPCState *env, + struct mmu_ctx_hash64 *ctx, target_ulong eaddr, int rw, int access_type) { @@ -547,7 +559,7 @@ static int ppc_hash64_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { - mmu_ctx_t ctx; + struct mmu_ctx_hash64 ctx; if (unlikely(ppc_hash64_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) { @@ -560,7 +572,7 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx) { - mmu_ctx_t ctx; + struct mmu_ctx_hash64 ctx; int access_type; int ret = 0; diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 9471d59674..c780ce1f1c 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -77,6 +77,19 @@ int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, } #else + +/* Context used internally during MMU translations */ +typedef struct mmu_ctx_t mmu_ctx_t; +struct mmu_ctx_t { + hwaddr raddr; /* Real address */ + hwaddr eaddr; /* Effective address */ + 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 */ +}; + /* Common routines used by software and hardware TLBs emulation */ static inline int pte_is_valid(target_ulong pte0) { From d5aea6f367d25b630a952a5a0c8289add774a8e8 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:18 +0000 Subject: [PATCH 21/58] mmu-hash*: Add header file for definitions Currently cpu.h contains a number of definitions relating to the 64-bit hash MMU. Some are used in the MMU emulation code, but some are only used in the spapr MMU management hcall implementations. This patch moves these definitions (except for a few that are needed more widely) into mmu-hash64.h header, shared between the MMU emulation code and the spapr hcall code. The MMU emulation code is also updated to actually use a number of those definitions in place of hard coded constants. Similarly, we add new analogous definitions to mmu-hash32.h and use those in place of many hard-coded constants in mmu-hash32.c Signed-off-by: David Gibson [agraf: fix 32-bit hosts] Signed-off-by: Alexander Graf --- hw/ppc/spapr_hcall.c | 70 ++++++++++-------------------------- target-ppc/cpu.h | 24 ------------- target-ppc/kvm.c | 1 + target-ppc/mmu-hash32.c | 78 ++++++++++++++++++----------------------- target-ppc/mmu-hash32.h | 61 ++++++++++++++++++++++++++++++++ target-ppc/mmu-hash64.c | 35 +++++++----------- target-ppc/mmu-hash64.h | 62 ++++++++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 141 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index dd72743b52..7be743c723 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -3,39 +3,7 @@ #include "sysemu/sysemu.h" #include "helper_regs.h" #include "hw/spapr.h" - -#define HPTES_PER_GROUP 8 - -#define HPTE_V_SSIZE_SHIFT 62 -#define HPTE_V_AVPN_SHIFT 7 -#define HPTE_V_AVPN 0x3fffffffffffff80ULL -#define HPTE_V_AVPN_VAL(x) (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT) -#define HPTE_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80UL)) -#define HPTE_V_BOLTED 0x0000000000000010ULL -#define HPTE_V_LOCK 0x0000000000000008ULL -#define HPTE_V_LARGE 0x0000000000000004ULL -#define HPTE_V_SECONDARY 0x0000000000000002ULL -#define HPTE_V_VALID 0x0000000000000001ULL - -#define HPTE_R_PP0 0x8000000000000000ULL -#define HPTE_R_TS 0x4000000000000000ULL -#define HPTE_R_KEY_HI 0x3000000000000000ULL -#define HPTE_R_RPN_SHIFT 12 -#define HPTE_R_RPN 0x3ffffffffffff000ULL -#define HPTE_R_FLAGS 0x00000000000003ffULL -#define HPTE_R_PP 0x0000000000000003ULL -#define HPTE_R_N 0x0000000000000004ULL -#define HPTE_R_G 0x0000000000000008ULL -#define HPTE_R_M 0x0000000000000010ULL -#define HPTE_R_I 0x0000000000000020ULL -#define HPTE_R_W 0x0000000000000040ULL -#define HPTE_R_WIMG 0x0000000000000078ULL -#define HPTE_R_C 0x0000000000000080ULL -#define HPTE_R_R 0x0000000000000100ULL -#define HPTE_R_KEY_LO 0x0000000000000e00ULL - -#define HPTE_V_1TB_SEG 0x4000000000000000ULL -#define HPTE_V_VRMA_MASK 0x4001ffffff000000ULL +#include "mmu-hash64.h" static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, target_ulong pte_index) @@ -44,17 +12,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, rb = (v & ~0x7fULL) << 16; /* AVA field */ va_low = pte_index >> 3; - if (v & HPTE_V_SECONDARY) { + if (v & HPTE64_V_SECONDARY) { va_low = ~va_low; } /* xor vsid from AVA */ - if (!(v & HPTE_V_1TB_SEG)) { + if (!(v & HPTE64_V_1TB_SEG)) { va_low ^= v >> 12; } else { va_low ^= v >> 24; } va_low &= 0x7ff; - if (v & HPTE_V_LARGE) { + if (v & HPTE64_V_LARGE) { rb |= 1; /* L field */ #if 0 /* Disable that P7 specific bit for now */ if (r & 0xff000) { @@ -87,7 +55,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint8_t *hpte; /* only handle 4k and 16M pages for now */ - if (pteh & HPTE_V_LARGE) { + if (pteh & HPTE64_V_LARGE) { #if 0 /* We don't support 64k pages yet */ if ((ptel & 0xf000) == 0x1000) { /* 64k page */ @@ -105,11 +73,11 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, } } - raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1); + raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1); if (raddr < spapr->ram_limit) { /* Regular RAM - should have WIMG=0010 */ - if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { + if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { return H_PARAMETER; } } else { @@ -117,7 +85,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, /* FIXME: What WIMG combinations could be sensible for IO? * For now we allow WIMG=010x, but are there others? */ /* FIXME: Should we check against registered IO addresses? */ - if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) { + if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) { return H_PARAMETER; } } @@ -134,7 +102,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, if (i == 8) { return H_PTEG_FULL; } - if ((ldq_p(hpte) & HPTE_V_VALID) == 0) { + if ((ldq_p(hpte) & HPTE64_V_VALID) == 0) { break; } hpte += HASH_PTE_SIZE_64; @@ -142,7 +110,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, } else { i = 0; hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); - if (ldq_p(hpte) & HPTE_V_VALID) { + if (ldq_p(hpte) & HPTE64_V_VALID) { return H_PTEG_FULL; } } @@ -178,7 +146,7 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, v = ldq_p(hpte); r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); - if ((v & HPTE_V_VALID) == 0 || + if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || ((flags & H_ANDCOND) && (v & avpn) != 0)) { return REMOVE_NOT_FOUND; @@ -271,7 +239,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr, switch (ret) { case REMOVE_SUCCESS: - *tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43; + *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; break; case REMOVE_PARM: @@ -304,18 +272,18 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, v = ldq_p(hpte); r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); - if ((v & HPTE_V_VALID) == 0 || + if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { return H_NOT_FOUND; } - r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N | - HPTE_R_KEY_HI | HPTE_R_KEY_LO); - r |= (flags << 55) & HPTE_R_PP0; - r |= (flags << 48) & HPTE_R_KEY_HI; - r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO); + r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | + HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); + r |= (flags << 55) & HPTE64_R_PP0; + r |= (flags << 48) & HPTE64_R_KEY_HI; + r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - stq_p(hpte, v & ~HPTE_V_VALID); + stq_p(hpte, v & ~HPTE64_V_VALID); ppc_tlb_invalidate_one(env, rb); stq_p(hpte + (HASH_PTE_SIZE_64/2), r); /* Don't need a memory barrier, due to qemu's global lock */ diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index e5f46b3418..e96afa61ec 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -394,36 +394,12 @@ union ppc_tlb_t { #define SDR_64_HTABSIZE 0x000000000000001FULL #endif /* defined(TARGET_PPC64 */ -#define HASH_PTE_SIZE_32 8 -#define HASH_PTE_SIZE_64 16 - typedef struct ppc_slb_t ppc_slb_t; struct ppc_slb_t { uint64_t esid; uint64_t vsid; }; -/* Bits in the SLB ESID word */ -#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL -#define SLB_ESID_V 0x0000000008000000ULL /* valid */ - -/* Bits in the SLB VSID word */ -#define SLB_VSID_SHIFT 12 -#define SLB_VSID_SHIFT_1T 24 -#define SLB_VSID_SSIZE_SHIFT 62 -#define SLB_VSID_B 0xc000000000000000ULL -#define SLB_VSID_B_256M 0x0000000000000000ULL -#define SLB_VSID_B_1T 0x4000000000000000ULL -#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL -#define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) -#define SLB_VSID_KS 0x0000000000000800ULL -#define SLB_VSID_KP 0x0000000000000400ULL -#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */ -#define SLB_VSID_L 0x0000000000000100ULL -#define SLB_VSID_C 0x0000000000000080ULL /* class */ -#define SLB_VSID_LP 0x0000000000000030ULL -#define SLB_VSID_ATTR 0x0000000000000FFFULL - #define SEGMENT_SHIFT_256M 28 #define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1)) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 9870d60884..597066f5a0 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -32,6 +32,7 @@ #include "sysemu/device_tree.h" #include "hw/sysbus.h" #include "hw/spapr.h" +#include "mmu-hash64.h" #include "hw/sysbus.h" #include "hw/spapr.h" diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 5b48aa8486..a1bbd50398 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -51,7 +51,6 @@ struct mmu_ctx_hash32 { int nx; /* Non-execute area */ }; -#define PTE_PTEM_MASK 0x7FFFFFBF #define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) static int ppc_hash32_pp_check(int key, int pp, int nx) @@ -127,13 +126,13 @@ static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp, target_ulong bl; int pp, valid, prot; - bl = (*BATu & 0x00001FFC) << 15; + bl = (*BATu & BATU32_BL) << 15; valid = 0; prot = 0; - if (((msr_pr == 0) && (*BATu & 0x00000002)) || - ((msr_pr != 0) && (*BATu & 0x00000001))) { + if (((msr_pr == 0) && (*BATu & BATU32_VS)) || + ((msr_pr != 0) && (*BATu & BATU32_VP))) { valid = 1; - pp = *BATl & 0x00000003; + pp = *BATl & BATL32_PP; if (pp != 0) { prot = PAGE_READ | PAGE_EXEC; if (pp == 0x2) { @@ -153,17 +152,17 @@ static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, target_ulong bl; int key, pp, valid, prot; - bl = (*BATl & 0x0000003F) << 17; + bl = (*BATl & BATL32_601_BL) << 17; LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", - (uint8_t)(*BATl & 0x0000003F), bl, ~bl); + (uint8_t)(*BATl & BATL32_601_BL), bl, ~bl); prot = 0; - valid = (*BATl >> 6) & 1; + valid = !!(*BATl & BATL32_601_V); if (valid) { - pp = *BATu & 0x00000003; + pp = *BATu & BATU32_601_PP; if (msr_pr == 0) { - key = (*BATu >> 3) & 1; + key = !!(*BATu & BATU32_601_KS); } else { - key = (*BATu >> 2) & 1; + key = !!(*BATu & BATU32_601_KP); } prot = ppc_hash32_pp_check(key, pp, 0); } @@ -195,8 +194,8 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; if (unlikely(env->mmu_model == POWERPC_MMU_601)) { hash32_bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); } else { @@ -205,13 +204,13 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); - if ((virtual & 0xF0000000) == BEPIu && - ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + if ((virtual & BATU32_BEPIU) == BEPIu && + ((virtual & BATU32_BEPIL) & ~bl) == BEPIl) { /* BAT matches */ if (valid != 0) { /* Get physical address */ - ctx->raddr = (*BATl & 0xF0000000) | - ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + ctx->raddr = (*BATl & BATL32_BRPNU) | + ((virtual & BATU32_BEPIL & bl) | (*BATl & BATL32_BRPNL)) | (virtual & 0x0001F000); /* Compute access rights */ ctx->prot = prot; @@ -232,8 +231,8 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, for (i = 0; i < 4; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; bl = (*BATu & 0x00001FFC) << 15; LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " @@ -248,28 +247,19 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, return ret; } - -static inline int pte_is_valid_hash32(target_ulong pte0) -{ - return pte0 & 0x80000000 ? 1 : 0; -} - static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, target_ulong pte1, int h, int rw, int type) { - target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; + target_ulong mmask; + int access, ret, pp; ret = -1; /* Check validity and table match */ - ptev = pte_is_valid_hash32(pte0); - pteh = (pte0 >> 6) & 1; - if (ptev && h == pteh) { + if ((pte0 & HPTE32_V_VALID) && (h == !!(pte0 & HPTE32_V_SECONDARY))) { /* Check vsid & api */ - ptem = pte0 & PTE_PTEM_MASK; mmask = PTE_CHECK_MASK; - pp = pte1 & 0x00000003; - if (ptem == ctx->ptem) { + pp = pte1 & HPTE32_R_PP; + if (HPTE32_V_COMPARE(pte0, ctx->ptem)) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ if ((ctx->raddr & mmask) != (pte1 & mmask)) { @@ -302,15 +292,15 @@ static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong int store = 0; /* Update page flags */ - if (!(*pte1p & 0x00000100)) { + if (!(*pte1p & HPTE32_R_R)) { /* Update accessed flag */ - *pte1p |= 0x00000100; + *pte1p |= HPTE32_R_R; store = 1; } - if (!(*pte1p & 0x00000080)) { + if (!(*pte1p & HPTE32_R_C)) { if (rw == 1 && ret == 0) { /* Update changed flag */ - *pte1p |= 0x00000080; + *pte1p |= HPTE32_R_C; store = 1; } else { /* Force page fault for first write access */ @@ -323,7 +313,7 @@ static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) { - return (hash * HASH_PTE_SIZE_32 * 8) & env->htab_mask; + return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; } /* PTE table lookup */ @@ -337,7 +327,7 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, ret = -1; /* No entry found */ pteg_off = get_pteg_offset32(env, ctx->hash[h]); - for (i = 0; i < 8; i++) { + for (i = 0; i < HPTES_PER_GROUP; i++) { if (env->external_htab) { pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); @@ -413,11 +403,11 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ctx->eaddr = eaddr; sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && (pr != 0)) || - ((sr & 0x40000000) && (pr == 0))) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; + ctx->key = (((sr & SR32_KP) && (pr != 0)) || + ((sr & SR32_KS) && (pr == 0))) ? 1 : 0; + ds = !!(sr & SR32_T); + ctx->nx = !!(sr & SR32_NX); + vsid = sr & SR32_VSID; target_page_bits = TARGET_PAGE_BITS; LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 3435aa5513..a09ccb3d38 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -8,6 +8,67 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr); int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); +/* + * Segment register definitions + */ + +#define SR32_T 0x80000000 +#define SR32_KS 0x40000000 +#define SR32_KP 0x20000000 +#define SR32_NX 0x10000000 +#define SR32_VSID 0x00ffffff + +/* + * Block Address Translation (BAT) definitions + */ + +#define BATU32_BEPIU 0xf0000000 +#define BATU32_BEPIL 0x0ffe0000 +#define BATU32_BEPI 0xfffe0000 +#define BATU32_BL 0x00001ffc +#define BATU32_VS 0x00000002 +#define BATU32_VP 0x00000001 + + +#define BATL32_BRPNU 0xf0000000 +#define BATL32_BRPNL 0x0ffe0000 +#define BATL32_BRPN 0xfffe0000 +#define BATL32_WIMG 0x00000078 +#define BATL32_PP 0x00000003 + +/* PowerPC 601 has slightly different BAT registers */ + +#define BATU32_601_KS 0x00000008 +#define BATU32_601_KP 0x00000004 +#define BATU32_601_PP 0x00000003 + +#define BATL32_601_V 0x00000040 +#define BATL32_601_BL 0x0000003f + +/* + * Hash page table definitions + */ + +#define HPTES_PER_GROUP 8 +#define HASH_PTE_SIZE_32 8 +#define HASH_PTEG_SIZE_32 (HASH_PTE_SIZE_32 * HPTES_PER_GROUP) + +#define HPTE32_V_VALID 0x80000000 +#define HPTE32_V_VSID 0x7fffff80 +#define HPTE32_V_SECONDARY 0x00000040 +#define HPTE32_V_API 0x0000003f +#define HPTE32_V_COMPARE(x, y) (!(((x) ^ (y)) & 0x7fffffbf)) + +#define HPTE32_R_RPN 0xfffff000 +#define HPTE32_R_R 0x00000100 +#define HPTE32_R_C 0x00000080 +#define HPTE32_R_W 0x00000040 +#define HPTE32_R_I 0x00000020 +#define HPTE32_R_M 0x00000010 +#define HPTE32_R_G 0x00000008 +#define HPTE32_R_WIMG 0x00000078 +#define HPTE32_R_PP 0x00000003 + #endif /* CONFIG_USER_ONLY */ #endif /* __MMU_HASH32_H__ */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 3008be8ee9..f3223dd383 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -233,7 +233,6 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) * 64-bit hash table MMU handling */ -#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL #define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) static int ppc_hash64_pp_check(int key, int pp, int nx) @@ -304,29 +303,21 @@ static int ppc_hash64_check_prot(int prot, int rw, int access_type) return ret; } -static inline int pte64_is_valid(target_ulong pte0) -{ - return pte0 & 0x0000000000000001ULL ? 1 : 0; -} - static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, target_ulong pte1, int h, int rw, int type) { - target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; + target_ulong mmask; + int access, ret, pp; ret = -1; /* Check validity and table match */ - ptev = pte64_is_valid(pte0); - pteh = (pte0 >> 1) & 1; - if (ptev && h == pteh) { + if ((pte0 & HPTE64_V_VALID) && (h == !!(pte0 & HPTE64_V_SECONDARY))) { /* Check vsid & api */ - ptem = pte0 & PTE64_PTEM_MASK; mmask = PTE64_CHECK_MASK; - pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004); - ctx->nx = (pte1 >> 2) & 1; /* No execute bit */ - ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */ - if (ptem == ctx->ptem) { + pp = (pte1 & HPTE64_R_PP) | ((pte1 & HPTE64_R_PP0) >> 61); + /* No execute if either noexec or guarded bits set */ + ctx->nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G); + if (HPTE64_V_COMPARE(pte0, ctx->ptem)) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ if ((ctx->raddr & mmask) != (pte1 & mmask)) { @@ -360,15 +351,15 @@ static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, int store = 0; /* Update page flags */ - if (!(*pte1p & 0x00000100)) { + if (!(*pte1p & HPTE64_R_R)) { /* Update accessed flag */ - *pte1p |= 0x00000100; + *pte1p |= HPTE64_R_R; store = 1; } - if (!(*pte1p & 0x00000080)) { + if (!(*pte1p & HPTE64_R_C)) { if (rw == 1 && ret == 0) { /* Update changed flag */ - *pte1p |= 0x00000080; + *pte1p |= HPTE64_R_C; store = 1; } else { /* Force page fault for first write access */ @@ -389,8 +380,8 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, int ret, r; ret = -1; /* No entry found */ - pteg_off = (ctx->hash[h] * HASH_PTE_SIZE_64 * 8) & env->htab_mask; - for (i = 0; i < 8; i++) { + pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask; + for (i = 0; i < HPTES_PER_GROUP; i++) { if (env->external_htab) { pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 665d3b0247..80b86d91ae 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -11,6 +11,68 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, int mmu_idx); #endif +/* + * SLB definitions + */ + +/* Bits in the SLB ESID word */ +#define SLB_ESID_ESID 0xFFFFFFFFF0000000ULL +#define SLB_ESID_V 0x0000000008000000ULL /* valid */ + +/* Bits in the SLB VSID word */ +#define SLB_VSID_SHIFT 12 +#define SLB_VSID_SHIFT_1T 24 +#define SLB_VSID_SSIZE_SHIFT 62 +#define SLB_VSID_B 0xc000000000000000ULL +#define SLB_VSID_B_256M 0x0000000000000000ULL +#define SLB_VSID_B_1T 0x4000000000000000ULL +#define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL +#define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) +#define SLB_VSID_KS 0x0000000000000800ULL +#define SLB_VSID_KP 0x0000000000000400ULL +#define SLB_VSID_N 0x0000000000000200ULL /* no-execute */ +#define SLB_VSID_L 0x0000000000000100ULL +#define SLB_VSID_C 0x0000000000000080ULL /* class */ +#define SLB_VSID_LP 0x0000000000000030ULL +#define SLB_VSID_ATTR 0x0000000000000FFFULL + +/* + * Hash page table definitions + */ + +#define HPTES_PER_GROUP 8 +#define HASH_PTE_SIZE_64 16 +#define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP) + +#define HPTE64_V_SSIZE_SHIFT 62 +#define HPTE64_V_AVPN_SHIFT 7 +#define HPTE64_V_AVPN 0x3fffffffffffff80ULL +#define HPTE64_V_AVPN_VAL(x) (((x) & HPTE64_V_AVPN) >> HPTE64_V_AVPN_SHIFT) +#define HPTE64_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80ULL)) +#define HPTE64_V_LARGE 0x0000000000000004ULL +#define HPTE64_V_SECONDARY 0x0000000000000002ULL +#define HPTE64_V_VALID 0x0000000000000001ULL + +#define HPTE64_R_PP0 0x8000000000000000ULL +#define HPTE64_R_TS 0x4000000000000000ULL +#define HPTE64_R_KEY_HI 0x3000000000000000ULL +#define HPTE64_R_RPN_SHIFT 12 +#define HPTE64_R_RPN 0x0ffffffffffff000ULL +#define HPTE64_R_FLAGS 0x00000000000003ffULL +#define HPTE64_R_PP 0x0000000000000003ULL +#define HPTE64_R_N 0x0000000000000004ULL +#define HPTE64_R_G 0x0000000000000008ULL +#define HPTE64_R_M 0x0000000000000010ULL +#define HPTE64_R_I 0x0000000000000020ULL +#define HPTE64_R_W 0x0000000000000040ULL +#define HPTE64_R_WIMG 0x0000000000000078ULL +#define HPTE64_R_C 0x0000000000000080ULL +#define HPTE64_R_R 0x0000000000000100ULL +#define HPTE64_R_KEY_LO 0x0000000000000e00ULL + +#define HPTE64_V_1TB_SEG 0x4000000000000000ULL +#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL + #endif /* CONFIG_USER_ONLY */ #endif /* !defined (__MMU_HASH64_H__) */ From dffdaf6162d20b992e34c4708969ed4de0353417 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:19 +0000 Subject: [PATCH 22/58] mmu-hash*: Add hash pte load/store helpers On real hardware the ppc hash page table is stored in memory; accordingly our mmu emulation code can read a hash page table in guest memory. But, when paravirtualized under PAPR, the real hash page table is in host memory, accessible to the guest only via hypercalls. We model this by also allowing the MMU emulation code to access a specially allocated hash page table outside the guest's memory image. At present these two options are implemented with some ugly conditionals at each access point in the mmu emulation code. In the implementation of the PAPR hypercalls, we assume the external hash table. This patch cleans things up by adding helpers to load and store from the hash table for both 32-bit and 64-bit hash mmus. The 64-bit versions handle both the in-guest-memory and outside guest memory cases. The 32-bit versions only handle the in-guest-memory case since no 32-bit systems can have an external hash table at present. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- hw/ppc/spapr_hcall.c | 38 +++++++++++++++++++------------------- target-ppc/mmu-hash32.c | 18 ++++-------------- target-ppc/mmu-hash32.h | 28 ++++++++++++++++++++++++++++ target-ppc/mmu-hash64.c | 17 +++-------------- target-ppc/mmu-hash64.h | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 47 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 7be743c723..22cfb7e674 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -52,7 +52,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong page_shift = 12; target_ulong raddr; target_ulong i; - uint8_t *hpte; + hwaddr hpte; /* only handle 4k and 16M pages for now */ if (pteh & HPTE64_V_LARGE) { @@ -97,26 +97,26 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, } if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7ULL; - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + hpte = pte_index * HASH_PTE_SIZE_64; for (i = 0; ; ++i) { if (i == 8) { return H_PTEG_FULL; } - if ((ldq_p(hpte) & HPTE64_V_VALID) == 0) { + if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { break; } hpte += HASH_PTE_SIZE_64; } } else { i = 0; - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); - if (ldq_p(hpte) & HPTE64_V_VALID) { + hpte = pte_index * HASH_PTE_SIZE_64; + if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { return H_PTEG_FULL; } } - stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel); + ppc_hash64_store_hpte1(env, hpte, ptel); /* eieio(); FIXME: need some sort of barrier for smp? */ - stq_p(hpte, pteh); + ppc_hash64_store_hpte0(env, hpte, pteh); args[0] = pte_index + i; return H_SUCCESS; @@ -134,17 +134,17 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong flags, target_ulong *vp, target_ulong *rp) { - uint8_t *hpte; + hwaddr hpte; target_ulong v, r, rb; if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { return REMOVE_PARM; } - hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64); + hpte = ptex * HASH_PTE_SIZE_64; - v = ldq_p(hpte); - r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + v = ppc_hash64_load_hpte0(env, hpte); + r = ppc_hash64_load_hpte1(env, hpte); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -153,7 +153,7 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - stq_p(hpte, 0); + ppc_hash64_store_hpte0(env, hpte, 0); rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); return REMOVE_SUCCESS; @@ -260,17 +260,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - uint8_t *hpte; + hwaddr hpte; target_ulong v, r, rb; if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { return H_PARAMETER; } - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + hpte = pte_index * HASH_PTE_SIZE_64; - v = ldq_p(hpte); - r = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + v = ppc_hash64_load_hpte0(env, hpte); + r = ppc_hash64_load_hpte1(env, hpte); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -283,11 +283,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - stq_p(hpte, v & ~HPTE64_V_VALID); + ppc_hash64_store_hpte0(env, hpte, v & ~HPTE64_V_VALID); ppc_tlb_invalidate_one(env, rb); - stq_p(hpte + (HASH_PTE_SIZE_64/2), r); + ppc_hash64_store_hpte1(env, hpte, r); /* Don't need a memory barrier, due to qemu's global lock */ - stq_p(hpte, v); + ppc_hash64_store_hpte0(env, hpte, v); return H_SUCCESS; } diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index a1bbd50398..a7c2e356e8 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -328,13 +328,8 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, ret = -1; /* No entry found */ pteg_off = get_pteg_offset32(env, ctx->hash[h]); for (i = 0; i < HPTES_PER_GROUP; i++) { - if (env->external_htab) { - pte0 = ldl_p(env->external_htab + pteg_off + (i * 8)); - pte1 = ldl_p(env->external_htab + pteg_off + (i * 8) + 4); - } else { - pte0 = ldl_phys(env->htab_base + pteg_off + (i * 8)); - pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); - } + pte0 = ppc_hash32_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_32); + pte1 = ppc_hash32_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_32); r = pte_check_hash32(ctx, pte0, pte1, h, rw, type); LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", @@ -371,13 +366,8 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, /* Update page flags */ pte1 = ctx->raddr; if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rw) == 1) { - if (env->external_htab) { - stl_p(env->external_htab + pteg_off + (good * 8) + 4, - pte1); - } else { - stl_phys_notdirty(env->htab_base + pteg_off + - (good * 8) + 4, pte1); - } + ppc_hash32_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_32, + pte1); } } diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index a09ccb3d38..2344184584 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -69,6 +69,34 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #define HPTE32_R_WIMG 0x00000078 #define HPTE32_R_PP 0x00000003 +static inline target_ulong ppc_hash32_load_hpte0(CPUPPCState *env, + hwaddr pte_offset) +{ + assert(!env->external_htab); /* Not supported on 32-bit for now */ + return ldl_phys(env->htab_base + pte_offset); +} + +static inline target_ulong ppc_hash32_load_hpte1(CPUPPCState *env, + hwaddr pte_offset) +{ + assert(!env->external_htab); /* Not supported on 32-bit for now */ + return ldl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2); +} + +static inline void ppc_hash32_store_hpte0(CPUPPCState *env, + hwaddr pte_offset, target_ulong pte0) +{ + assert(!env->external_htab); /* Not supported on 32-bit for now */ + stl_phys(env->htab_base + pte_offset, pte0); +} + +static inline void ppc_hash32_store_hpte1(CPUPPCState *env, + hwaddr pte_offset, target_ulong pte1) +{ + assert(!env->external_htab); /* Not supported on 32-bit for now */ + stl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2, pte1); +} + #endif /* CONFIG_USER_ONLY */ #endif /* __MMU_HASH32_H__ */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index f3223dd383..30c25dd24b 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -382,13 +382,8 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, ret = -1; /* No entry found */ pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask; for (i = 0; i < HPTES_PER_GROUP; i++) { - if (env->external_htab) { - pte0 = ldq_p(env->external_htab + pteg_off + (i * 16)); - pte1 = ldq_p(env->external_htab + pteg_off + (i * 16) + 8); - } else { - pte0 = ldq_phys(env->htab_base + pteg_off + (i * 16)); - pte1 = ldq_phys(env->htab_base + pteg_off + (i * 16) + 8); - } + pte0 = ppc_hash64_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_64); + pte1 = ppc_hash64_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_64); r = pte64_check(ctx, pte0, pte1, h, rw, type); LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " @@ -426,13 +421,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, /* Update page flags */ pte1 = ctx->raddr; if (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rw) == 1) { - if (env->external_htab) { - stq_p(env->external_htab + pteg_off + (good * 16) + 8, - pte1); - } else { - stq_phys_notdirty(env->htab_base + pteg_off + - (good * 16) + 8, pte1); - } + ppc_hash64_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_64, pte1); } } diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 80b86d91ae..84576c0648 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -73,6 +73,46 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL +static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env, + hwaddr pte_offset) +{ + if (env->external_htab) { + return ldq_p(env->external_htab + pte_offset); + } else { + return ldq_phys(env->htab_base + pte_offset); + } +} + +static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env, + hwaddr pte_offset) +{ + if (env->external_htab) { + return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2); + } else { + return ldq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2); + } +} + +static inline void ppc_hash64_store_hpte0(CPUPPCState *env, + hwaddr pte_offset, target_ulong pte0) +{ + if (env->external_htab) { + stq_p(env->external_htab + pte_offset, pte0); + } else { + stq_phys(env->htab_base + pte_offset, pte0); + } +} + +static inline void ppc_hash64_store_hpte1(CPUPPCState *env, + hwaddr pte_offset, target_ulong pte1) +{ + if (env->external_htab) { + stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1); + } else { + stq_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1); + } +} + #endif /* CONFIG_USER_ONLY */ #endif /* !defined (__MMU_HASH64_H__) */ From 91cda45b69e45a089f9989979a65db3f710c9925 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:20 +0000 Subject: [PATCH 23/58] mmu-hash*: Reduce use of access_type In ppc env->access_type is updated by e.g. integer load/stores with ACCESS_INT floating point load/stores with ACCESS_FLOAT and so forth. In hash mmu fault paths it can also b set to ACCESS_CODE for instruction fetch accesses. But the only place which uses anything more of the access_type than whether it is instruction fetch or data access is the direct store segment handling. Instruction versus data access can be more simply determined from the rw value passed down from the top. This changes the code to use rw in preference to checking access_type. For the 32-bit case there is a small amount of code (for direct store segments) that still needs the full access type. Instead of passing it all the way down the stack, we retrieve it from the env structure, which is where it came anyway, before this patch. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 98 ++++++++++++++++++----------------------- target-ppc/mmu-hash64.c | 55 +++++++++-------------- 2 files changed, 66 insertions(+), 87 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index a7c2e356e8..ad5ef44c28 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -91,17 +91,17 @@ static int ppc_hash32_pp_check(int key, int pp, int nx) return access; } -static int ppc_hash32_check_prot(int prot, int rw, int access_type) +static int ppc_hash32_check_prot(int prot, int rwx) { int ret; - if (access_type == ACCESS_CODE) { + if (rwx == 2) { if (prot & PAGE_EXEC) { ret = 0; } else { ret = -2; } - } else if (rw) { + } else if (rwx) { if (prot & PAGE_WRITE) { ret = 0; } else { @@ -172,7 +172,7 @@ static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, } static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong virtual, int rw, int type) + target_ulong virtual, int rwx) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; @@ -180,16 +180,13 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int ret = -1; LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', virtual); - switch (type) { - case ACCESS_CODE: + rwx == 2 ? 'I' : 'D', virtual); + if (rwx == 2) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; - break; - default: + } else { BATlt = env->DBAT[1]; BATut = env->DBAT[0]; - break; } for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; @@ -214,7 +211,7 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, (virtual & 0x0001F000); /* Compute access rights */ ctx->prot = prot; - ret = ppc_hash32_check_prot(ctx->prot, rw, type); + ret = ppc_hash32_check_prot(ctx->prot, rwx); if (ret == 0) { LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', @@ -248,7 +245,7 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) + target_ulong pte1, int h, int rwx) { target_ulong mmask; int access, ret, pp; @@ -272,7 +269,7 @@ static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; - ret = ppc_hash32_check_prot(ctx->prot, rw, type); + ret = ppc_hash32_check_prot(ctx->prot, rwx); if (ret == 0) { /* Access granted */ LOG_MMU("PTE access granted !\n"); @@ -287,7 +284,7 @@ static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, } static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong *pte1p, - int ret, int rw) + int ret, int rwx) { int store = 0; @@ -298,7 +295,7 @@ static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong store = 1; } if (!(*pte1p & HPTE32_R_C)) { - if (rw == 1 && ret == 0) { + if (rwx == 1 && ret == 0) { /* Update changed flag */ *pte1p |= HPTE32_R_C; store = 1; @@ -318,7 +315,7 @@ hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) /* PTE table lookup */ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, - int rw, int type, int target_page_bits) + int rwx, int target_page_bits) { hwaddr pteg_off; target_ulong pte0, pte1; @@ -330,7 +327,7 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, for (i = 0; i < HPTES_PER_GROUP; i++) { pte0 = ppc_hash32_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_32); pte1 = ppc_hash32_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_32); - r = pte_check_hash32(ctx, pte0, pte1, h, rw, type); + r = pte_check_hash32(ctx, pte0, pte1, h, rwx); LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, @@ -365,7 +362,7 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, ctx->raddr, ctx->prot, ret); /* Update page flags */ pte1 = ctx->raddr; - if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rw) == 1) { + if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rwx) == 1) { ppc_hash32_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_32, pte1); } @@ -381,7 +378,7 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, } static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong eaddr, int rw, int type) + target_ulong eaddr, int rwx) { hwaddr hash; target_ulong vsid; @@ -401,9 +398,9 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_page_bits = TARGET_PAGE_BITS; LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx - " ir=%d dr=%d pr=%d %d t=%d\n", + " ir=%d dr=%d pr=%d %d\n", eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + (int)msr_dr, pr != 0 ? 1 : 0, rwx); pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; hash = vsid ^ pgidx; ctx->ptem = (vsid << 7) | (pgidx >> 10); @@ -413,7 +410,7 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ret = -1; if (!ds) { /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || ctx->nx == 0) { + if (rwx != 2 || ctx->nx == 0) { /* Page address translation */ LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", @@ -429,15 +426,14 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[0]); /* Primary table lookup */ - ret = find_pte32(env, ctx, 0, rw, type, target_page_bits); + ret = find_pte32(env, ctx, 0, rwx, target_page_bits); if (ret < 0) { /* Secondary table lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte32(env, ctx, 1, rw, type, - target_page_bits); + ret2 = find_pte32(env, ctx, 1, rwx, target_page_bits); if (ret2 != -1) { ret = ret2; } @@ -486,13 +482,15 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, return 0; } - switch (type) { + if (rwx == 2) { + /* No code fetch is allowed in direct-store areas */ + return -4; + } + + switch (env->access_type) { case ACCESS_INT: /* Integer load/store : only access allowed */ break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; case ACCESS_FLOAT: /* Floating point load/store */ return -4; @@ -514,7 +512,7 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, "address translation\n"); return -4; } - if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { + if ((rwx == 1 || ctx->key != 1) && (rwx == 0 || ctx->key != 0)) { ctx->raddr = eaddr; ret = 2; } else { @@ -526,11 +524,10 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } static int ppc_hash32_get_physical_address(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong eaddr, int rw, - int access_type) + target_ulong eaddr, int rwx) { - bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) - || (access_type != ACCESS_CODE && msr_dr == 0); + bool real_mode = (rwx == 2 && msr_ir == 0) + || (rwx != 2 && msr_dr == 0); if (real_mode) { ctx->raddr = eaddr; @@ -541,11 +538,11 @@ static int ppc_hash32_get_physical_address(CPUPPCState *env, struct mmu_ctx_hash /* Try to find a BAT */ if (env->nb_BATs != 0) { - ret = ppc_hash32_get_bat(env, ctx, eaddr, rw, access_type); + ret = ppc_hash32_get_bat(env, ctx, eaddr, rwx); } if (ret < 0) { /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment32(env, ctx, eaddr, rw, access_type); + ret = get_segment32(env, ctx, eaddr, rwx); } return ret; } @@ -555,7 +552,9 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { struct mmu_ctx_hash32 ctx; - if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) + /* FIXME: Will not behave sanely for direct store segments, but + * they're almost never used */ + if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0) != 0)) { return -1; } @@ -563,22 +562,13 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) return ctx.raddr & TARGET_PAGE_MASK; } -int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, +int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, int mmu_idx) { struct mmu_ctx_hash32 ctx; - int access_type; int ret = 0; - if (rw == 2) { - /* code access */ - rw = 0; - access_type = ACCESS_CODE; - } else { - /* data access */ - access_type = env->access_type; - } - ret = ppc_hash32_get_physical_address(env, &ctx, address, rw, access_type); + ret = ppc_hash32_get_physical_address(env, &ctx, address, rwx); if (ret == 0) { tlb_set_page(env, address & TARGET_PAGE_MASK, ctx.raddr & TARGET_PAGE_MASK, ctx.prot, @@ -586,7 +576,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, ret = 0; } else if (ret < 0) { LOG_MMU_STATE(env); - if (access_type == ACCESS_CODE) { + if (rwx == 2) { switch (ret) { case -1: /* No matches in page tables or TLB */ @@ -617,7 +607,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (rwx == 1) { env->spr[SPR_DSISR] = 0x42000000; } else { env->spr[SPR_DSISR] = 0x40000000; @@ -628,7 +618,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (rwx == 1) { env->spr[SPR_DSISR] = 0x0A000000; } else { env->spr[SPR_DSISR] = 0x08000000; @@ -636,7 +626,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, break; case -4: /* Direct store exception */ - switch (access_type) { + switch (env->access_type) { case ACCESS_FLOAT: /* Floating point load/store */ env->exception_index = POWERPC_EXCP_ALIGN; @@ -648,7 +638,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (rwx == 1) { env->spr[SPR_DSISR] = 0x06000000; } else { env->spr[SPR_DSISR] = 0x04000000; @@ -659,7 +649,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (rwx == 1) { env->spr[SPR_DSISR] = 0x06100000; } else { env->spr[SPR_DSISR] = 0x04100000; diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 30c25dd24b..654ef06308 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -276,17 +276,17 @@ static int ppc_hash64_pp_check(int key, int pp, int nx) return access; } -static int ppc_hash64_check_prot(int prot, int rw, int access_type) +static int ppc_hash64_check_prot(int prot, int rwx) { int ret; - if (access_type == ACCESS_CODE) { + if (rwx == 2) { if (prot & PAGE_EXEC) { ret = 0; } else { ret = -2; } - } else if (rw) { + } else if (rwx == 1) { if (prot & PAGE_WRITE) { ret = 0; } else { @@ -304,7 +304,7 @@ static int ppc_hash64_check_prot(int prot, int rw, int access_type) } static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, - target_ulong pte1, int h, int rw, int type) + target_ulong pte1, int h, int rwx) { target_ulong mmask; int access, ret, pp; @@ -330,7 +330,7 @@ static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; - ret = ppc_hash64_check_prot(ctx->prot, rw, type); + ret = ppc_hash64_check_prot(ctx->prot, rwx); if (ret == 0) { /* Access granted */ LOG_MMU("PTE access granted !\n"); @@ -372,7 +372,7 @@ static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, /* PTE table lookup */ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, - int rw, int type, int target_page_bits) + int rwx, int target_page_bits) { hwaddr pteg_off; target_ulong pte0, pte1; @@ -385,7 +385,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, pte0 = ppc_hash64_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_64); pte1 = ppc_hash64_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_64); - r = pte64_check(ctx, pte0, pte1, h, rw, type); + r = pte64_check(ctx, pte0, pte1, h, rwx); LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, @@ -420,7 +420,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, ctx->raddr, ctx->prot, ret); /* Update page flags */ pte1 = ctx->raddr; - if (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rw) == 1) { + if (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rwx) == 1) { ppc_hash64_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_64, pte1); } } @@ -435,7 +435,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, } static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, - target_ulong eaddr, int rw, int type) + target_ulong eaddr, int rwx) { hwaddr hash; target_ulong vsid; @@ -484,7 +484,7 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ret = -1; /* Check if instruction fetch is allowed, if needed */ - if (type != ACCESS_CODE || ctx->nx == 0) { + if (rwx != 2 || ctx->nx == 0) { /* Page address translation */ LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", @@ -500,14 +500,14 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[0]); /* Primary table lookup */ - ret = find_pte64(env, ctx, 0, rw, type, target_page_bits); + ret = find_pte64(env, ctx, 0, rwx, target_page_bits); if (ret < 0) { /* Secondary table lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte64(env, ctx, 1, rw, type, target_page_bits); + ret2 = find_pte64(env, ctx, 1, rwx, target_page_bits); if (ret2 != -1) { ret = ret2; } @@ -522,18 +522,17 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, static int ppc_hash64_get_physical_address(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, - target_ulong eaddr, int rw, - int access_type) + target_ulong eaddr, int rwx) { - bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) - || (access_type != ACCESS_CODE && msr_dr == 0); + bool real_mode = (rwx == 2 && msr_ir == 0) + || (rwx != 2 && msr_dr == 0); if (real_mode) { ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; return 0; } else { - return get_segment64(env, ctx, eaddr, rw, access_type); + return get_segment64(env, ctx, eaddr, rwx); } } @@ -541,30 +540,20 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { struct mmu_ctx_hash64 ctx; - if (unlikely(ppc_hash64_get_physical_address(env, &ctx, addr, 0, ACCESS_INT) - != 0)) { + if (unlikely(ppc_hash64_get_physical_address(env, &ctx, addr, 0) != 0)) { return -1; } return ctx.raddr & TARGET_PAGE_MASK; } -int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, +int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, int mmu_idx) { struct mmu_ctx_hash64 ctx; - int access_type; int ret = 0; - if (rw == 2) { - /* code access */ - rw = 0; - access_type = ACCESS_CODE; - } else { - /* data access */ - access_type = env->access_type; - } - ret = ppc_hash64_get_physical_address(env, &ctx, address, rw, access_type); + ret = ppc_hash64_get_physical_address(env, &ctx, address, rwx); if (ret == 0) { tlb_set_page(env, address & TARGET_PAGE_MASK, ctx.raddr & TARGET_PAGE_MASK, ctx.prot, @@ -572,7 +561,7 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, ret = 0; } else if (ret < 0) { LOG_MMU_STATE(env); - if (access_type == ACCESS_CODE) { + if (rwx == 2) { switch (ret) { case -1: env->exception_index = POWERPC_EXCP_ISI; @@ -601,7 +590,7 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (rwx == 1) { env->spr[SPR_DSISR] = 0x42000000; } else { env->spr[SPR_DSISR] = 0x40000000; @@ -612,7 +601,7 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (rwx == 1) { env->spr[SPR_DSISR] = 0x0A000000; } else { env->spr[SPR_DSISR] = 0x08000000; From ba36ed10059f63c981d046a3fe0d716f77959429 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:21 +0000 Subject: [PATCH 24/58] mmu-hash64: Remove nx from mmu_ctx_hash64 The nx field in mmu_ctx_hash64 is used in two different functions. But its used for slightly different things in each place, and the value is never propagated between them. In other words, it might as well be two local variables. This patch makes it so. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash64.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 654ef06308..f89d00552f 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -47,7 +47,6 @@ struct mmu_ctx_hash64 { hwaddr hash[2]; /* Pagetable hash values */ target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ - int nx; /* Non-execute area */ }; /* @@ -235,7 +234,7 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) #define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) -static int ppc_hash64_pp_check(int key, int pp, int nx) +static int ppc_hash64_pp_check(int key, int pp, bool nx) { int access; @@ -269,7 +268,7 @@ static int ppc_hash64_pp_check(int key, int pp, int nx) break; } } - if (nx == 0) { + if (!nx) { access |= PAGE_EXEC; } @@ -312,11 +311,13 @@ static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, ret = -1; /* Check validity and table match */ if ((pte0 & HPTE64_V_VALID) && (h == !!(pte0 & HPTE64_V_SECONDARY))) { + bool nx; + /* Check vsid & api */ mmask = PTE64_CHECK_MASK; pp = (pte1 & HPTE64_R_PP) | ((pte1 & HPTE64_R_PP0) >> 61); /* No execute if either noexec or guarded bits set */ - ctx->nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G); + nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G); if (HPTE64_V_COMPARE(pte0, ctx->ptem)) { if (ctx->raddr != (hwaddr)-1ULL) { /* all matches should have equal RPN, WIMG & PP */ @@ -326,7 +327,7 @@ static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, } } /* Compute access rights */ - access = ppc_hash64_pp_check(ctx->key, pp, ctx->nx); + access = ppc_hash64_pp_check(ctx->key, pp, nx); /* Keep the matching PTE informations */ ctx->raddr = pte1; ctx->prot = access; @@ -466,7 +467,6 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) : (slb->vsid & SLB_VSID_KS)); - ctx->nx = !!(slb->vsid & SLB_VSID_N); pageaddr = eaddr & ((1ULL << segment_bits) - (1ULL << target_page_bits)); @@ -480,11 +480,11 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ctx->nx, vsid); + ctx->key, !!(slb->vsid & SLB_VSID_N), vsid); ret = -1; /* Check if instruction fetch is allowed, if needed */ - if (rwx != 2 || ctx->nx == 0) { + if (rwx != 2 || !(slb->vsid & SLB_VSID_N)) { /* Page address translation */ LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", From f078cd46de9efb5f102a4b32aaf1c8b96a90bfbd Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:22 +0000 Subject: [PATCH 25/58] mmu-hash*: Remove eaddr field from mmu_ctx_hash{32, 64} The eaddr field of mmu_ctx_hash{32,64} is effectively just used to pass the effective address from get_segment{32,64}() to find_pte{32,64}(). Just pass it as a normal parameter instead. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 12 +++++------- target-ppc/mmu-hash64.c | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index ad5ef44c28..8001563456 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -43,7 +43,6 @@ struct mmu_ctx_hash32 { hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ int prot; /* Protection bits */ hwaddr hash[2]; /* Pagetable hash values */ target_ulong ptem; /* Virtual segment ID | API */ @@ -314,8 +313,8 @@ hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) } /* PTE table lookup */ -static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, - int rwx, int target_page_bits) +static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, + target_ulong eaddr, int h, int rwx, int target_page_bits) { hwaddr pteg_off; target_ulong pte0, pte1; @@ -371,7 +370,7 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int h, /* We have a TLB that saves 4K pages, so let's * split a huge page to 4k chunks */ if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) + ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) & TARGET_PAGE_MASK; } return ret; @@ -387,7 +386,6 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong sr, pgidx; pr = msr_pr; - ctx->eaddr = eaddr; sr = env->sr[eaddr >> 28]; ctx->key = (((sr & SR32_KP) && (pr != 0)) || @@ -426,14 +424,14 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[0]); /* Primary table lookup */ - ret = find_pte32(env, ctx, 0, rwx, target_page_bits); + ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); if (ret < 0) { /* Secondary table lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte32(env, ctx, 1, rwx, target_page_bits); + ret2 = find_pte32(env, ctx, eaddr, 1, rwx, target_page_bits); if (ret2 != -1) { ret = ret2; } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index f89d00552f..407c6e6ede 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -42,7 +42,6 @@ struct mmu_ctx_hash64 { hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ int prot; /* Protection bits */ hwaddr hash[2]; /* Pagetable hash values */ target_ulong ptem; /* Virtual segment ID | API */ @@ -372,8 +371,8 @@ static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, } /* PTE table lookup */ -static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, - int rwx, int target_page_bits) +static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, + target_ulong eaddr, int h, int rwx, int target_page_bits) { hwaddr pteg_off; target_ulong pte0, pte1; @@ -429,7 +428,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int h, /* We have a TLB that saves 4K pages, so let's * split a huge page to 4k chunks */ if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (ctx->eaddr & ((1 << target_page_bits) - 1)) + ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) & TARGET_PAGE_MASK; } return ret; @@ -444,7 +443,6 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, int ret, ret2; pr = msr_pr; - ctx->eaddr = eaddr; ppc_slb_t *slb; target_ulong pageaddr; int segment_bits; @@ -500,14 +498,14 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[0]); /* Primary table lookup */ - ret = find_pte64(env, ctx, 0, rwx, target_page_bits); + ret = find_pte64(env, ctx, eaddr, 0, rwx, target_page_bits); if (ret < 0) { /* Secondary table lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte64(env, ctx, 1, rwx, target_page_bits); + ret2 = find_pte64(env, ctx, eaddr, 1, rwx, target_page_bits); if (ret2 != -1) { ret = ret2; } From 65d61643d01fec2792d195130531cbb71c783e8e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:23 +0000 Subject: [PATCH 26/58] mmu-hash*: Combine ppc_hash{32, 64}_get_physical_address and get_segment{32, 64}() After previous work, ppc_hash{32,64}_get_physical_address() are almost trivial wrappers around get_segment{32,64}() which does nearly all the work of translating an address according to the hash mmu model. Therefore combine the two functions into one, under the better name of ppc_hash{32,64}_translate(). Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 49 +++++++++++++++++------------------------ target-ppc/mmu-hash64.c | 37 +++++++++++++------------------ 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 8001563456..bc9783447d 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -376,8 +376,8 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, return ret; } -static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong eaddr, int rwx) +static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, + target_ulong eaddr, int rwx) { hwaddr hash; target_ulong vsid; @@ -385,6 +385,22 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, int ret, ret2; target_ulong sr, pgidx; + /* 1. Handle real mode accesses */ + if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { + /* Translation is off */ + ctx->raddr = eaddr; + ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; + return 0; + } + + /* 2. Check Block Address Translation entries (BATs) */ + if (env->nb_BATs != 0) { + ret = ppc_hash32_get_bat(env, ctx, eaddr, rwx); + if (ret == 0) { + return 0; + } + } + pr = msr_pr; sr = env->sr[eaddr >> 28]; @@ -521,38 +537,13 @@ static int get_segment32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, return ret; } -static int ppc_hash32_get_physical_address(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong eaddr, int rwx) -{ - bool real_mode = (rwx == 2 && msr_ir == 0) - || (rwx != 2 && msr_dr == 0); - - if (real_mode) { - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; - return 0; - } else { - int ret = -1; - - /* Try to find a BAT */ - if (env->nb_BATs != 0) { - ret = ppc_hash32_get_bat(env, ctx, eaddr, rwx); - } - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment32(env, ctx, eaddr, rwx); - } - return ret; - } -} - hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { struct mmu_ctx_hash32 ctx; /* FIXME: Will not behave sanely for direct store segments, but * they're almost never used */ - if (unlikely(ppc_hash32_get_physical_address(env, &ctx, addr, 0) + if (unlikely(ppc_hash32_translate(env, &ctx, addr, 0) != 0)) { return -1; } @@ -566,7 +557,7 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, struct mmu_ctx_hash32 ctx; int ret = 0; - ret = ppc_hash32_get_physical_address(env, &ctx, address, rwx); + ret = ppc_hash32_translate(env, &ctx, address, rwx); if (ret == 0) { tlb_set_page(env, address & TARGET_PAGE_MASK, ctx.raddr & TARGET_PAGE_MASK, ctx.prot, diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 407c6e6ede..9afc418cf5 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -434,19 +434,28 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, return ret; } -static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, - target_ulong eaddr, int rwx) +static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, + target_ulong eaddr, int rwx) { hwaddr hash; target_ulong vsid; int pr, target_page_bits; int ret, ret2; - - pr = msr_pr; ppc_slb_t *slb; target_ulong pageaddr; int segment_bits; + /* 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 */ + ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; + return 0; + } + + pr = msr_pr; + LOG_MMU("Check SLBs\n"); slb = slb_lookup(env, eaddr); if (!slb) { @@ -518,27 +527,11 @@ static int get_segment64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, return ret; } -static int ppc_hash64_get_physical_address(CPUPPCState *env, - struct mmu_ctx_hash64 *ctx, - target_ulong eaddr, int rwx) -{ - bool real_mode = (rwx == 2 && msr_ir == 0) - || (rwx != 2 && msr_dr == 0); - - if (real_mode) { - ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; - ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; - return 0; - } else { - return get_segment64(env, ctx, eaddr, rwx); - } -} - hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { struct mmu_ctx_hash64 ctx; - if (unlikely(ppc_hash64_get_physical_address(env, &ctx, addr, 0) != 0)) { + if (unlikely(ppc_hash64_translate(env, &ctx, addr, 0) != 0)) { return -1; } @@ -551,7 +544,7 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, struct mmu_ctx_hash64 ctx; int ret = 0; - ret = ppc_hash64_get_physical_address(env, &ctx, address, rwx); + ret = ppc_hash64_translate(env, &ctx, address, rwx); if (ret == 0) { tlb_set_page(env, address & TARGET_PAGE_MASK, ctx.raddr & TARGET_PAGE_MASK, ctx.prot, From 4b9605a5b16f86d9e8eccbc0bd522904137c1200 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:24 +0000 Subject: [PATCH 27/58] mmu-hash32: Split out handling of direct store segments At present a large chunk of ppc_hash32_translate() is taken up with an ugly if selecting between direct store segments (hardly ever used) and normal paged segments. This patch clarifies the flow of code by handling direct store segments immediately then returning, leaving the straight line code to describe the normal MMU path. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 156 ++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index bc9783447d..04ddf1d543 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -381,7 +381,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, { hwaddr hash; target_ulong vsid; - int ds, pr, target_page_bits; + int pr, target_page_bits; int ret, ret2; target_ulong sr, pgidx; @@ -401,91 +401,21 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } } - pr = msr_pr; - + /* 3. Look up the Segment Register */ sr = env->sr[eaddr >> 28]; + + pr = msr_pr; ctx->key = (((sr & SR32_KP) && (pr != 0)) || ((sr & SR32_KS) && (pr == 0))) ? 1 : 0; - ds = !!(sr & SR32_T); - ctx->nx = !!(sr & SR32_NX); - vsid = sr & SR32_VSID; - target_page_bits = TARGET_PAGE_BITS; - LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" - TARGET_FMT_lx " lr=" TARGET_FMT_lx - " ir=%d dr=%d pr=%d %d\n", - eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, rwx); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; - hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); - - LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ds, ctx->nx, vsid); - ret = -1; - if (!ds) { - /* Check if instruction fetch is allowed, if needed */ - if (rwx != 2 || ctx->nx == 0) { - /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret < 0) { - /* Secondary table lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte32(env, ctx, eaddr, 1, rwx, target_page_bits); - if (ret2 != -1) { - ret = ret2; - } - } -#if defined(DUMP_PAGE_TABLES) - if (qemu_log_enabled()) { - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx - "\n", sdr, mask + 0x80); - for (curaddr = sdr; curaddr < (sdr + mask + 0x80); - curaddr += 16) { - a0 = ldl_phys(curaddr); - a1 = ldl_phys(curaddr + 4); - a2 = ldl_phys(curaddr + 8); - a3 = ldl_phys(curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif - } else { - LOG_MMU("No access allowed\n"); - ret = -3; - } - } else { - target_ulong sr; + /* 4. Handle direct store segments */ + if (sr & SR32_T) { LOG_MMU("direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ /* Direct-store implies a 32-bit MMU. * Check the Segment Register's bus unit ID (BUID). */ - sr = env->sr[eaddr >> 28]; if ((sr & 0x1FF00000) >> 20 == 0x07f) { /* Memory-forced I/O controller interface access */ /* If T=1 and BUID=x'07F', the 601 performs a memory access @@ -528,12 +458,82 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } if ((rwx == 1 || ctx->key != 1) && (rwx == 0 || ctx->key != 0)) { ctx->raddr = eaddr; - ret = 2; + return 2; } else { - ret = -2; + return -2; } } + ctx->nx = !!(sr & SR32_NX); + vsid = sr & SR32_VSID; + target_page_bits = TARGET_PAGE_BITS; + LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" + TARGET_FMT_lx " lr=" TARGET_FMT_lx + " ir=%d dr=%d pr=%d %d\n", + eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, + (int)msr_dr, pr != 0 ? 1 : 0, rwx); + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + hash = vsid ^ pgidx; + ctx->ptem = (vsid << 7) | (pgidx >> 10); + + LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n", + ctx->key, ctx->nx, vsid); + ret = -1; + + /* Check if instruction fetch is allowed, if needed */ + if (rwx != 2 || ctx->nx == 0) { + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; + + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); + /* Primary table lookup */ + ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); + if (ret < 0) { + /* Secondary table lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); + ret2 = find_pte32(env, ctx, eaddr, 1, rwx, target_page_bits); + if (ret2 != -1) { + ret = ret2; + } + } +#if defined(DUMP_PAGE_TABLES) + if (qemu_log_enabled()) { + hwaddr curaddr; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + "\n", sdr, mask + 0x80); + for (curaddr = sdr; curaddr < (sdr + mask + 0x80); + curaddr += 16) { + a0 = ldl_phys(curaddr); + a1 = ldl_phys(curaddr + 4); + a2 = ldl_phys(curaddr + 8); + a3 = ldl_phys(curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif + } else { + LOG_MMU("No access allowed\n"); + ret = -3; + } + return ret; } From 723ed73ada1ed66a67722f3051059f634d60ebf7 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:25 +0000 Subject: [PATCH 28/58] mmu-hash32: Split direct store segment handling into a helper This further separates the unusual case handling of direct store segments from the main translation path by moving its logic into a helper function, with some tiny cleanups along the way. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 119 +++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 04ddf1d543..dbde2646bc 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -243,6 +243,62 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, return ret; } +static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, + target_ulong eaddr, int rwx, + hwaddr *raddr, int *prot) +{ + int key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + + LOG_MMU("direct store...\n"); + + if ((sr & 0x1FF00000) >> 20 == 0x07f) { + /* Memory-forced I/O controller interface access */ + /* If T=1 and BUID=x'07F', the 601 performs a memory access + * to SR[28-31] LA[4-31], bypassing all protection mechanisms. + */ + *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return 0; + } + + if (rwx == 2) { + /* No code fetch is allowed in direct-store areas */ + return -4; + } + + switch (env->access_type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ + /* Should make the instruction do no-op. + * As it already do no-op, it's quite easy :-) + */ + *raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + qemu_log("ERROR: instruction should not need " + "address translation\n"); + return -4; + } + if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { + *raddr = eaddr; + return 2; + } else { + return -2; + } +} + static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, target_ulong pte1, int h, int rwx) { @@ -404,66 +460,15 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, /* 3. Look up the Segment Register */ sr = env->sr[eaddr >> 28]; + /* 4. Handle direct store segments */ + if (sr & SR32_T) { + return ppc_hash32_direct_store(env, sr, eaddr, rwx, + &ctx->raddr, &ctx->prot); + } + pr = msr_pr; ctx->key = (((sr & SR32_KP) && (pr != 0)) || ((sr & SR32_KS) && (pr == 0))) ? 1 : 0; - - /* 4. Handle direct store segments */ - if (sr & SR32_T) { - LOG_MMU("direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ - - /* Direct-store implies a 32-bit MMU. - * Check the Segment Register's bus unit ID (BUID). - */ - if ((sr & 0x1FF00000) >> 20 == 0x07f) { - /* Memory-forced I/O controller interface access */ - /* If T=1 and BUID=x'07F', the 601 performs a memory access - * to SR[28-31] LA[4-31], bypassing all protection mechanisms. - */ - ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return 0; - } - - if (rwx == 2) { - /* No code fetch is allowed in direct-store areas */ - return -4; - } - - switch (env->access_type) { - case ACCESS_INT: - /* Integer load/store : only access allowed */ - break; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; - case ACCESS_CACHE: - /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ - /* Should make the instruction do no-op. - * As it already do no-op, it's quite easy :-) - */ - ctx->raddr = eaddr; - return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log("ERROR: instruction should not need " - "address translation\n"); - return -4; - } - if ((rwx == 1 || ctx->key != 1) && (rwx == 0 || ctx->key != 0)) { - ctx->raddr = eaddr; - return 2; - } else { - return -2; - } - } - ctx->nx = !!(sr & SR32_NX); vsid = sr & SR32_VSID; target_page_bits = TARGET_PAGE_BITS; From bb218042c8b4e4bbcf2ab3c8d961d78876178831 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:26 +0000 Subject: [PATCH 29/58] mmu-hash*: Cleanup segment-level NX check On the ppc hash mmus, no-execute can be set at the segment level (on more recent 64-bit hash mmus it can also be set at the page level). This patch separates out this check to make it clearer what is going on, and avoiding excessive indentation of the remaining translation code. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 102 ++++++++++++++++++++-------------------- target-ppc/mmu-hash64.c | 66 +++++++++++++------------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index dbde2646bc..d812adbbf1 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -466,10 +466,16 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, &ctx->raddr, &ctx->prot); } + /* 5. Check for segment level no-execute violation */ + ctx->nx = !!(sr & SR32_NX); + if ((rwx == 2) && ctx->nx) { + return -3; + } + pr = msr_pr; ctx->key = (((sr & SR32_KP) && (pr != 0)) || ((sr & SR32_KS) && (pr == 0))) ? 1 : 0; - ctx->nx = !!(sr & SR32_NX); + vsid = sr & SR32_VSID; target_page_bits = TARGET_PAGE_BITS; LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" @@ -485,59 +491,53 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ctx->key, ctx->nx, vsid); ret = -1; - /* Check if instruction fetch is allowed, if needed */ - if (rwx != 2 || ctx->nx == 0) { - /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret < 0) { - /* Secondary table lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte32(env, ctx, eaddr, 1, rwx, target_page_bits); - if (ret2 != -1) { - ret = ret2; - } + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); + /* Primary table lookup */ + ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); + if (ret < 0) { + /* Secondary table lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); + ret2 = find_pte32(env, ctx, eaddr, 1, rwx, target_page_bits); + if (ret2 != -1) { + ret = ret2; } -#if defined(DUMP_PAGE_TABLES) - if (qemu_log_enabled()) { - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx - "\n", sdr, mask + 0x80); - for (curaddr = sdr; curaddr < (sdr + mask + 0x80); - curaddr += 16) { - a0 = ldl_phys(curaddr); - a1 = ldl_phys(curaddr + 4); - a2 = ldl_phys(curaddr + 8); - a3 = ldl_phys(curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif - } else { - LOG_MMU("No access allowed\n"); - ret = -3; } +#if defined(DUMP_PAGE_TABLES) + if (qemu_log_enabled()) { + hwaddr curaddr; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx + "\n", sdr, mask + 0x80); + for (curaddr = sdr; curaddr < (sdr + mask + 0x80); + curaddr += 16) { + a0 = ldl_phys(curaddr); + a1 = ldl_phys(curaddr + 4); + a2 = ldl_phys(curaddr + 8); + a3 = ldl_phys(curaddr + 12); + if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { + qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif return ret; } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 9afc418cf5..4fb7ecdf07 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -454,14 +454,20 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, return 0; } - pr = msr_pr; - - LOG_MMU("Check SLBs\n"); + /* 2. Translation is on, so look up the SLB */ slb = slb_lookup(env, eaddr); + if (!slb) { return -5; } + /* 3. Check for segment level no-execute violation */ + if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { + return -3; + } + + pr = msr_pr; + if (slb->vsid & SLB_VSID_B) { vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; segment_bits = 40; @@ -490,38 +496,32 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ctx->key, !!(slb->vsid & SLB_VSID_N), vsid); ret = -1; - /* Check if instruction fetch is allowed, if needed */ - if (rwx != 2 || !(slb->vsid & SLB_VSID_N)) { - /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + ctx->hash[0] = hash; + ctx->hash[1] = ~hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte64(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret < 0) { - /* Secondary table lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte64(env, ctx, eaddr, 1, rwx, target_page_bits); - if (ret2 != -1) { - ret = ret2; - } + /* Initialize real address with an invalid value */ + ctx->raddr = (hwaddr)-1ULL; + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ctx->ptem, + ctx->hash[0]); + /* Primary table lookup */ + ret = find_pte64(env, ctx, eaddr, 0, rwx, target_page_bits); + if (ret < 0) { + /* Secondary table lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); + ret2 = find_pte64(env, ctx, eaddr, 1, rwx, target_page_bits); + if (ret2 != -1) { + ret = ret2; } - } else { - LOG_MMU("No access allowed\n"); - ret = -3; } return ret; From f95d7cc7fecbc0e320e83c864ce2d99fee3d9236 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:27 +0000 Subject: [PATCH 30/58] mmu-hash*: Don't keep looking for PTEs after we find a match BEHAVIOUR CHANGE The ppc hash mmu hashes each virtual address to a primary and secondary possible hash bucket (aka PTE group or PTEG) each with 8 PTEs. Then we need a linear search through the PTEs to find the correct one for the virtual address we're translating. It is a programming error for the guest to insert multiple PTEs mapping the same virtual address into a PTEG - in this case the ppc architecture says the MMU can either act as if just one was present, or give a machine check. Currently our code takes the first matching PTE in a PTEG if it finds a successful translation. But if a matching PTE is found, but permission bits don't allow the access, we keep looking through the PTEG, checking that any other matching PTEs contain an identical translation. That behaviour is perhaps not exactly wrong, but it's certainly not useful. This patch changes it to always just find the first matching PTE in a PTEG. In addition, if we get a permissions problem on the primary PTEG, we then search the secondary PTEG. This is incorrect - a permission denying PTE in the primary PTEG should not be overwritten by an access granting PTE in the secondary (although again, it would be a programming error for the guest to set up such a situation anyway). So additionally we update the code to only search the secondary PTEG if no matching PTE is found in the primary at all. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 88 ++++++++++++------------------------ target-ppc/mmu-hash64.c | 98 ++++++++++++++--------------------------- 2 files changed, 62 insertions(+), 124 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index d812adbbf1..3be1002e8c 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -50,8 +50,6 @@ struct mmu_ctx_hash32 { int nx; /* Non-execute area */ }; -#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B) - static int ppc_hash32_pp_check(int key, int pp, int nx) { int access; @@ -299,40 +297,32 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, } } -static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, - target_ulong pte1, int h, int rwx) +static bool pte32_match(target_ulong pte0, target_ulong pte1, + bool secondary, target_ulong ptem) +{ + return (pte0 & HPTE32_V_VALID) + && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) + && HPTE32_V_COMPARE(pte0, ptem); +} + +static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, + target_ulong pte1, int rwx) { - target_ulong mmask; int access, ret, pp; - ret = -1; - /* Check validity and table match */ - if ((pte0 & HPTE32_V_VALID) && (h == !!(pte0 & HPTE32_V_SECONDARY))) { - /* Check vsid & api */ - mmask = PTE_CHECK_MASK; - pp = pte1 & HPTE32_R_PP; - if (HPTE32_V_COMPARE(pte0, ctx->ptem)) { - if (ctx->raddr != (hwaddr)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log("Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Compute access rights */ - access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte1; - ctx->prot = access; - ret = ppc_hash32_check_prot(ctx->prot, rwx); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { - /* Access right violation */ - LOG_MMU("PTE access rejected\n"); - } - } + pp = pte1 & HPTE32_R_PP; + /* Compute access rights */ + access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte1; + ctx->prot = access; + ret = ppc_hash32_check_prot(ctx->prot, rwx); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); } return ret; @@ -375,44 +365,26 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, hwaddr pteg_off; target_ulong pte0, pte1; int i, good = -1; - int ret, r; + int ret; ret = -1; /* No entry found */ pteg_off = get_pteg_offset32(env, ctx->hash[h]); for (i = 0; i < HPTES_PER_GROUP; i++) { pte0 = ppc_hash32_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_32); pte1 = ppc_hash32_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_32); - r = pte_check_hash32(ctx, pte0, pte1, h, rwx); + LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem); - switch (r) { - case -3: - /* PTE inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; + + if (pte32_match(pte0, pte1, h, ctx->ptem)) { good = i; break; - case -1: - default: - /* No PTE match */ - break; - case 0: - /* access granted */ - /* XXX: we should go on looping to check all PTEs consistency - * but if we can speed-up the whole thing as the - * result would be undefined if PTEs are not consistent. - */ - ret = 0; - good = i; - goto done; } } if (good != -1) { - done: + ret = pte_check_hash32(ctx, pte0, pte1, rwx); LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); /* Update page flags */ @@ -498,8 +470,6 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ctx->hash[0] = hash; ctx->hash[1] = ~hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", @@ -507,7 +477,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ctx->hash[0]); /* Primary table lookup */ ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret < 0) { + if (ret == -1) { /* Secondary table lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 4fb7ecdf07..8664116aab 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -231,8 +231,6 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) * 64-bit hash table MMU handling */ -#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F) - static int ppc_hash64_pp_check(int key, int pp, bool nx) { int access; @@ -301,44 +299,35 @@ static int ppc_hash64_check_prot(int prot, int rwx) return ret; } -static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, - target_ulong pte1, int h, int rwx) +static bool pte64_match(target_ulong pte0, target_ulong pte1, + bool secondary, target_ulong ptem) +{ + return (pte0 & HPTE64_V_VALID) + && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) + && HPTE64_V_COMPARE(pte0, ptem); +} + +static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, + target_ulong pte1, int rwx) { - target_ulong mmask; int access, ret, pp; + bool nx; - ret = -1; - /* Check validity and table match */ - if ((pte0 & HPTE64_V_VALID) && (h == !!(pte0 & HPTE64_V_SECONDARY))) { - bool nx; - - /* Check vsid & api */ - mmask = PTE64_CHECK_MASK; - pp = (pte1 & HPTE64_R_PP) | ((pte1 & HPTE64_R_PP0) >> 61); - /* No execute if either noexec or guarded bits set */ - nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G); - if (HPTE64_V_COMPARE(pte0, ctx->ptem)) { - if (ctx->raddr != (hwaddr)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log("Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Compute access rights */ - access = ppc_hash64_pp_check(ctx->key, pp, nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte1; - ctx->prot = access; - ret = ppc_hash64_check_prot(ctx->prot, rwx); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { - /* Access right violation */ - LOG_MMU("PTE access rejected\n"); - } - } + pp = (pte1 & HPTE64_R_PP) | ((pte1 & HPTE64_R_PP0) >> 61); + /* No execute if either noexec or guarded bits set */ + nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G); + /* Compute access rights */ + access = ppc_hash64_pp_check(ctx->key, pp, nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte1; + ctx->prot = access; + ret = ppc_hash64_check_prot(ctx->prot, rwx); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); } return ret; @@ -377,7 +366,7 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, hwaddr pteg_off; target_ulong pte0, pte1; int i, good = -1; - int ret, r; + int ret; ret = -1; /* No entry found */ pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask; @@ -385,37 +374,18 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, pte0 = ppc_hash64_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_64); pte1 = ppc_hash64_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_64); - r = pte64_check(ctx, pte0, pte1, h, rwx); LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, - (int)((pte0 >> 1) & 1), ctx->ptem); - switch (r) { - case -3: - /* PTE inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; + pteg_off + (i * 16), pte0, pte1, !!(pte0 & HPTE64_V_VALID), + h, !!(pte0 & HPTE64_V_SECONDARY), ctx->ptem); + + if (pte64_match(pte0, pte1, h, ctx->ptem)) { good = i; break; - case -1: - default: - /* No PTE match */ - break; - case 0: - /* access granted */ - /* XXX: we should go on looping to check all PTEs consistency - * but if we can speed-up the whole thing as the - * result would be undefined if PTEs are not consistent. - */ - ret = 0; - good = i; - goto done; } } if (good != -1) { - done: + ret = pte64_check(ctx, pte0, pte1, rwx); LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); /* Update page flags */ @@ -503,8 +473,6 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ctx->hash[0] = hash; ctx->hash[1] = ~hash; - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", @@ -512,7 +480,7 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ctx->hash[0]); /* Primary table lookup */ ret = find_pte64(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret < 0) { + if (ret == -1) { /* Secondary table lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx From aea390e4be652d5b5457771d25eded0dba14fe37 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:28 +0000 Subject: [PATCH 31/58] mmu-hash*: Separate PTEG searching from permissions checking find_pte{32,64{() do several things. First they search through a PTEG ooking for a PTE matching our virtual address. Then they do permissions checking and other processing on that PTE. This patch separates the search by VA out from the rest. The search is combined with the pte{32,64}_match() functions into new ppc_has{32,64}_pteg_search() functions. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 69 +++++++++++++++++++++-------------------- target-ppc/mmu-hash32.h | 4 +++ target-ppc/mmu-hash64.c | 67 +++++++++++++++++++-------------------- target-ppc/mmu-hash64.h | 4 +++ 4 files changed, 77 insertions(+), 67 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 3be1002e8c..7c13a4957f 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -297,14 +297,6 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, } } -static bool pte32_match(target_ulong pte0, target_ulong pte1, - bool secondary, target_ulong ptem) -{ - return (pte0 & HPTE32_V_VALID) - && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) - && HPTE32_V_COMPARE(pte0, ptem); -} - static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, target_ulong pte1, int rwx) { @@ -328,8 +320,8 @@ static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, return ret; } -static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, target_ulong *pte1p, - int ret, int rwx) +static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, + uint32_t *pte1p, int ret, int rwx) { int store = 0; @@ -358,40 +350,49 @@ hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; } -/* PTE table lookup */ +static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, + bool secondary, target_ulong ptem, + ppc_hash_pte32_t *pte) +{ + hwaddr pte_offset = pteg_off; + target_ulong pte0, pte1; + int i; + + for (i = 0; i < HPTES_PER_GROUP; i++) { + pte0 = ppc_hash32_load_hpte0(env, pte_offset); + pte1 = ppc_hash32_load_hpte1(env, pte_offset); + + if ((pte0 & HPTE32_V_VALID) + && (secondary == !!(pte0 & HPTE32_V_SECONDARY)) + && HPTE32_V_COMPARE(pte0, ptem)) { + pte->pte0 = pte0; + pte->pte1 = pte1; + return pte_offset; + } + + pte_offset += HASH_PTE_SIZE_32; + } + + return -1; +} + static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong eaddr, int h, int rwx, int target_page_bits) { - hwaddr pteg_off; - target_ulong pte0, pte1; - int i, good = -1; + hwaddr pteg_off, pte_offset; + ppc_hash_pte32_t pte; int ret; ret = -1; /* No entry found */ pteg_off = get_pteg_offset32(env, ctx->hash[h]); - for (i = 0; i < HPTES_PER_GROUP; i++) { - pte0 = ppc_hash32_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_32); - pte1 = ppc_hash32_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_32); - - LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " - TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, - (int)((pte0 >> 6) & 1), ctx->ptem); - - if (pte32_match(pte0, pte1, h, ctx->ptem)) { - good = i; - break; - } - } - if (good != -1) { - ret = pte_check_hash32(ctx, pte0, pte1, rwx); + pte_offset = ppc_hash32_pteg_search(env, pteg_off, h, ctx->ptem, &pte); + if (pte_offset != -1) { + ret = pte_check_hash32(ctx, pte.pte0, pte.pte1, rwx); LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); /* Update page flags */ - pte1 = ctx->raddr; - if (ppc_hash32_pte_update_flags(ctx, &pte1, ret, rwx) == 1) { - ppc_hash32_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_32, - pte1); + if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { + ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); } } diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index 2344184584..f990edc3ef 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -97,6 +97,10 @@ static inline void ppc_hash32_store_hpte1(CPUPPCState *env, stl_phys(env->htab_base + pte_offset + HASH_PTE_SIZE_32/2, pte1); } +typedef struct { + uint32_t pte0, pte1; +} ppc_hash_pte32_t; + #endif /* CONFIG_USER_ONLY */ #endif /* __MMU_HASH32_H__ */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 8664116aab..66b2d2444b 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -299,14 +299,6 @@ static int ppc_hash64_check_prot(int prot, int rwx) return ret; } -static bool pte64_match(target_ulong pte0, target_ulong pte1, - bool secondary, target_ulong ptem) -{ - return (pte0 & HPTE64_V_VALID) - && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) - && HPTE64_V_COMPARE(pte0, ptem); -} - static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, target_ulong pte1, int rwx) { @@ -334,8 +326,7 @@ static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, } static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, - target_ulong *pte1p, - int ret, int rw) + uint64_t *pte1p, int ret, int rw) { int store = 0; @@ -359,39 +350,49 @@ static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, return store; } -/* PTE table lookup */ +static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, + bool secondary, target_ulong ptem, + ppc_hash_pte64_t *pte) +{ + hwaddr pte_offset = pteg_off; + target_ulong pte0, pte1; + int i; + + for (i = 0; i < HPTES_PER_GROUP; i++) { + pte0 = ppc_hash64_load_hpte0(env, pte_offset); + pte1 = ppc_hash64_load_hpte1(env, pte_offset); + + if ((pte0 & HPTE64_V_VALID) + && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) + && HPTE64_V_COMPARE(pte0, ptem)) { + pte->pte0 = pte0; + pte->pte1 = pte1; + return pte_offset; + } + + pte_offset += HASH_PTE_SIZE_64; + } + + return -1; +} + static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, target_ulong eaddr, int h, int rwx, int target_page_bits) { - hwaddr pteg_off; - target_ulong pte0, pte1; - int i, good = -1; + hwaddr pteg_off, pte_offset; + ppc_hash_pte64_t pte; int ret; ret = -1; /* No entry found */ pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask; - for (i = 0; i < HPTES_PER_GROUP; i++) { - pte0 = ppc_hash64_load_hpte0(env, pteg_off + i*HASH_PTE_SIZE_64); - pte1 = ppc_hash64_load_hpte1(env, pteg_off + i*HASH_PTE_SIZE_64); - - LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " - TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", - pteg_off + (i * 16), pte0, pte1, !!(pte0 & HPTE64_V_VALID), - h, !!(pte0 & HPTE64_V_SECONDARY), ctx->ptem); - - if (pte64_match(pte0, pte1, h, ctx->ptem)) { - good = i; - break; - } - } - if (good != -1) { - ret = pte64_check(ctx, pte0, pte1, rwx); + pte_offset = ppc_hash64_pteg_search(env, pteg_off, h, ctx->ptem, &pte); + if (pte_offset != -1) { + ret = pte64_check(ctx, pte.pte0, pte.pte1, rwx); LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); /* Update page flags */ - pte1 = ctx->raddr; - if (ppc_hash64_pte_update_flags(ctx, &pte1, ret, rwx) == 1) { - ppc_hash64_store_hpte1(env, pteg_off + good * HASH_PTE_SIZE_64, pte1); + if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { + ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); } } diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 84576c0648..37ed7ca4ef 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -113,6 +113,10 @@ static inline void ppc_hash64_store_hpte1(CPUPPCState *env, } } +typedef struct { + uint64_t pte0, pte1; +} ppc_hash_pte64_t; + #endif /* CONFIG_USER_ONLY */ #endif /* !defined (__MMU_HASH64_H__) */ From a1ff751abda89006f167b14fa4bfc5b59e4980f0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:29 +0000 Subject: [PATCH 32/58] mmu-hash*: Make find_pte{32, 64} do more of the job of finding ptes find_pte{32,64}() are not particularly well named. They only "find" a PTE within a given PTE group, and they also do permissions checking and other things. This patch makes it somewhat close to matching the name, by folding the search of both primary and secondary hash bucket into it, along with the various address bit shuffling to determine the right hash buckets. In the 32-bit case we also remove the code for splitting large pages into 4k pieces for the qemu tlb, since no 32-bit hash MMUs support multiple page sizes. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 116 ++++++++++++------------------------- target-ppc/mmu-hash64.c | 123 +++++++++++++++++++--------------------- 2 files changed, 92 insertions(+), 147 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 7c13a4957f..b7a26a2a90 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -44,8 +44,6 @@ struct mmu_ctx_hash32 { hwaddr raddr; /* Real address */ 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 */ }; @@ -377,15 +375,44 @@ static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, } static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong eaddr, int h, int rwx, int target_page_bits) + target_ulong sr, target_ulong eaddr, int rwx) { hwaddr pteg_off, pte_offset; ppc_hash_pte32_t pte; + hwaddr hash; + uint32_t vsid, pgidx, ptem; int ret; ret = -1; /* No entry found */ - pteg_off = get_pteg_offset32(env, ctx->hash[h]); - pte_offset = ppc_hash32_pteg_search(env, pteg_off, h, ctx->ptem, &pte); + vsid = sr & SR32_VSID; + ctx->key = (((sr & SR32_KP) && (msr_pr != 0)) || + ((sr & SR32_KS) && (msr_pr == 0))) ? 1 : 0; + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; + hash = vsid ^ pgidx; + ptem = (vsid << 7) | (pgidx >> 10); + + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + + /* Primary PTEG lookup */ + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=%" PRIx32 " ptem=%" PRIx32 + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ptem, hash); + pteg_off = get_pteg_offset32(env, hash); + pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, &pte); + if (pte_offset == -1) { + /* Secondary PTEG lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=%" PRIx32 " api=%" PRIx32 + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ptem, ~hash); + pteg_off = get_pteg_offset32(env, ~hash); + pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, &pte); + } + if (pte_offset != -1) { ret = pte_check_hash32(ctx, pte.pte0, pte.pte1, rwx); LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", @@ -396,23 +423,14 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } } - /* We have a TLB that saves 4K pages, so let's - * split a huge page to 4k chunks */ - if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) - & TARGET_PAGE_MASK; - } return ret; } static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong eaddr, int rwx) { - hwaddr hash; - target_ulong vsid; - int pr, target_page_bits; - int ret, ret2; - target_ulong sr, pgidx; + int ret; + target_ulong sr; /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { @@ -444,71 +462,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, if ((rwx == 2) && ctx->nx) { return -3; } - - pr = msr_pr; - ctx->key = (((sr & SR32_KP) && (pr != 0)) || - ((sr & SR32_KS) && (pr == 0))) ? 1 : 0; - - vsid = sr & SR32_VSID; - target_page_bits = TARGET_PAGE_BITS; - LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" - TARGET_FMT_lx " lr=" TARGET_FMT_lx - " ir=%d dr=%d pr=%d %d\n", - eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, rwx); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; - hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); - - LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ctx->nx, vsid); - ret = -1; - - /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte32(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret == -1) { - /* Secondary table lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte32(env, ctx, eaddr, 1, rwx, target_page_bits); - if (ret2 != -1) { - ret = ret2; - } - } -#if defined(DUMP_PAGE_TABLES) - if (qemu_log_enabled()) { - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx - "\n", sdr, mask + 0x80); - for (curaddr = sdr; curaddr < (sdr + mask + 0x80); - curaddr += 16) { - a0 = ldl_phys(curaddr); - a1 = ldl_phys(curaddr + 4); - a2 = ldl_phys(curaddr + 8); - a3 = ldl_phys(curaddr + 12); - if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) { - qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n", - curaddr, a0, a1, a2, a3); - } - } - } -#endif + ret = find_pte32(env, ctx, sr, eaddr, rwx); return ret; } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 66b2d2444b..36e2c84556 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -43,8 +43,6 @@ struct mmu_ctx_hash64 { hwaddr raddr; /* Real address */ int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ int key; /* Access key */ }; @@ -377,15 +375,67 @@ static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, } static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, - target_ulong eaddr, int h, int rwx, int target_page_bits) + ppc_slb_t *slb, target_ulong eaddr, int rwx) { hwaddr pteg_off, pte_offset; ppc_hash_pte64_t pte; + uint64_t vsid, pageaddr, ptem; + hwaddr hash; + int segment_bits, target_page_bits; int ret; ret = -1; /* No entry found */ - pteg_off = (ctx->hash[h] * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, h, ctx->ptem, &pte); + + if (slb->vsid & SLB_VSID_B) { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; + segment_bits = 40; + } else { + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + segment_bits = 28; + } + + target_page_bits = (slb->vsid & SLB_VSID_L) + ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + ctx->key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); + + pageaddr = eaddr & ((1ULL << segment_bits) + - (1ULL << target_page_bits)); + if (slb->vsid & SLB_VSID_B) { + hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); + } else { + hash = vsid ^ (pageaddr >> target_page_bits); + } + /* Only 5 bits of the page index are used in the AVPN */ + ptem = (slb->vsid & SLB_VSID_PTEM) | + ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); + + ret = -1; + + /* Page address translation */ + LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx + " hash " TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, hash); + + + /* Primary PTEG lookup */ + LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", + env->htab_base, env->htab_mask, vsid, ptem, hash); + pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask; + pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, &pte); + if (pte_offset == -1) { + /* Secondary PTEG lookup */ + LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx + " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx + " hash=" TARGET_FMT_plx "\n", env->htab_base, + env->htab_mask, vsid, ptem, ~hash); + + pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask; + pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, &pte); + } + if (pte_offset != -1) { ret = pte64_check(ctx, pte.pte0, pte.pte1, rwx); LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", @@ -408,13 +458,8 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, target_ulong eaddr, int rwx) { - hwaddr hash; - target_ulong vsid; - int pr, target_page_bits; - int ret, ret2; + int ret; ppc_slb_t *slb; - target_ulong pageaddr; - int segment_bits; /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { @@ -437,61 +482,7 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, return -3; } - pr = msr_pr; - - if (slb->vsid & SLB_VSID_B) { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; - segment_bits = 40; - } else { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; - segment_bits = 28; - } - - target_page_bits = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - ctx->key = !!(pr ? (slb->vsid & SLB_VSID_KP) - : (slb->vsid & SLB_VSID_KS)); - - pageaddr = eaddr & ((1ULL << segment_bits) - - (1ULL << target_page_bits)); - if (slb->vsid & SLB_VSID_B) { - hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); - } else { - hash = vsid ^ (pageaddr >> target_page_bits); - } - /* Only 5 bits of the page index are used in the AVPN */ - ctx->ptem = (slb->vsid & SLB_VSID_PTEM) | - ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); - - LOG_MMU("pte segment: key=%d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, !!(slb->vsid & SLB_VSID_N), vsid); - ret = -1; - - /* Page address translation */ - LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx - " hash " TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", - env->htab_base, env->htab_mask, vsid, ctx->ptem, - ctx->hash[0]); - /* Primary table lookup */ - ret = find_pte64(env, ctx, eaddr, 0, rwx, target_page_bits); - if (ret == -1) { - /* Secondary table lookup */ - LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx - " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx - " hash=" TARGET_FMT_plx "\n", env->htab_base, - env->htab_mask, vsid, ctx->ptem, ctx->hash[1]); - ret2 = find_pte64(env, ctx, eaddr, 1, rwx, target_page_bits); - if (ret2 != -1) { - ret = ret2; - } - } + ret = find_pte64(env, ctx, slb, eaddr, rwx); return ret; } From 7f3bdc2d8e17999a26ac0f6649caef92fedfc1c0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:30 +0000 Subject: [PATCH 33/58] mmu-hash*: Remove permission checking from find_pte{32, 64}() find_pte{32,64}() are poorly named, since they both find a PTE and do permissions checking of it. This patch makes them only locate a matching PTE, moving the permission checking and other logic to the caller. We rename the resulting search functions ppc_hash{32,64}_htab_lookup(). Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 45 +++++++++++++++------------- target-ppc/mmu-hash64.c | 65 +++++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index b7a26a2a90..8b403fecb1 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -374,19 +374,15 @@ static hwaddr ppc_hash32_pteg_search(CPUPPCState *env, hwaddr pteg_off, return -1; } -static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong sr, target_ulong eaddr, int rwx) +static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, + target_ulong sr, target_ulong eaddr, + ppc_hash_pte32_t *pte) { hwaddr pteg_off, pte_offset; - ppc_hash_pte32_t pte; hwaddr hash; uint32_t vsid, pgidx, ptem; - int ret; - ret = -1; /* No entry found */ vsid = sr & SR32_VSID; - ctx->key = (((sr & SR32_KP) && (msr_pr != 0)) || - ((sr & SR32_KS) && (msr_pr == 0))) ? 1 : 0; pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; hash = vsid ^ pgidx; ptem = (vsid << 7) | (pgidx >> 10); @@ -402,7 +398,7 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); pteg_off = get_pteg_offset32(env, hash); - pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, &pte); + pte_offset = ppc_hash32_pteg_search(env, pteg_off, 0, ptem, pte); if (pte_offset == -1) { /* Secondary PTEG lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx @@ -410,20 +406,10 @@ static int find_pte32(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); pteg_off = get_pteg_offset32(env, ~hash); - pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, &pte); + pte_offset = ppc_hash32_pteg_search(env, pteg_off, 1, ptem, pte); } - if (pte_offset != -1) { - ret = pte_check_hash32(ctx, pte.pte0, pte.pte1, rwx); - LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", - ctx->raddr, ctx->prot, ret); - /* Update page flags */ - if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { - ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); - } - } - - return ret; + return pte_offset; } static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, @@ -431,6 +417,8 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, { int ret; target_ulong sr; + hwaddr pte_offset; + ppc_hash_pte32_t pte; /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { @@ -462,7 +450,22 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, if ((rwx == 2) && ctx->nx) { return -3; } - ret = find_pte32(env, ctx, sr, eaddr, rwx); + + /* 6. Locate the PTE in the hash table */ + pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); + if (pte_offset == -1) { + return -1; + } + LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + + /* 7. Check access permissions */ + ctx->key = (((sr & SR32_KP) && (msr_pr != 0)) || + ((sr & SR32_KS) && (msr_pr == 0))) ? 1 : 0; + ret = pte_check_hash32(ctx, pte.pte0, pte.pte1, rwx); + /* Update page flags */ + if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { + ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); + } return ret; } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 36e2c84556..a98e008a9b 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -374,17 +374,14 @@ static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, return -1; } -static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, - ppc_slb_t *slb, target_ulong eaddr, int rwx) +static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, + ppc_slb_t *slb, target_ulong eaddr, + ppc_hash_pte64_t *pte) { hwaddr pteg_off, pte_offset; - ppc_hash_pte64_t pte; uint64_t vsid, pageaddr, ptem; hwaddr hash; int segment_bits, target_page_bits; - int ret; - - ret = -1; /* No entry found */ if (slb->vsid & SLB_VSID_B) { vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; @@ -396,8 +393,6 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, target_page_bits = (slb->vsid & SLB_VSID_L) ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - ctx->key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) - : (slb->vsid & SLB_VSID_KS)); pageaddr = eaddr & ((1ULL << segment_bits) - (1ULL << target_page_bits)); @@ -410,21 +405,19 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ptem = (slb->vsid & SLB_VSID_PTEM) | ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); - ret = -1; - /* Page address translation */ LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx " hash " TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, hash); - /* Primary PTEG lookup */ LOG_MMU("0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, &pte); + pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte); + if (pte_offset == -1) { /* Secondary PTEG lookup */ LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx @@ -433,26 +426,10 @@ static int find_pte64(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, env->htab_mask, vsid, ptem, ~hash); pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask; - pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, &pte); + pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte); } - if (pte_offset != -1) { - ret = pte64_check(ctx, pte.pte0, pte.pte1, rwx); - LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", - ctx->raddr, ctx->prot, ret); - /* Update page flags */ - if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { - ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); - } - } - - /* We have a TLB that saves 4K pages, so let's - * split a huge page to 4k chunks */ - if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) - & TARGET_PAGE_MASK; - } - return ret; + return pte_offset; } static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, @@ -460,6 +437,9 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, { int ret; ppc_slb_t *slb; + hwaddr pte_offset; + ppc_hash_pte64_t pte; + int target_page_bits; /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { @@ -482,8 +462,31 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, return -3; } - ret = find_pte64(env, ctx, slb, eaddr, rwx); + /* 4. Locate the PTE in the hash table */ + pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte); + if (pte_offset == -1) { + return -1; + } + LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); + /* 5. Check access permissions */ + ctx->key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); + + ret = pte64_check(ctx, pte.pte0, pte.pte1, rwx); + /* Update page flags */ + if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { + ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); + } + + /* We have a TLB that saves 4K pages, so let's + * split a huge page to 4k chunks */ + target_page_bits = (slb->vsid & SLB_VSID_L) + ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + if (target_page_bits != TARGET_PAGE_BITS) { + ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) + & TARGET_PAGE_MASK; + } return ret; } From 181488987671841407c52b6f958650f68b66f3f4 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:31 +0000 Subject: [PATCH 34/58] mmu-hash64: Clean up ppc_hash64_htab_lookup() This patch makes a general cleanup of the address mangling logic in ppc_hash64_htab_lookup(). In particular it now avoids repeatedly switching on the segment size. The lack of SLB and multiple segment sizes on 32-bit means an analogous cleanup is not needed there. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash64.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index a98e008a9b..10372f0ed8 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -379,31 +379,29 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, ppc_hash_pte64_t *pte) { hwaddr pteg_off, pte_offset; - uint64_t vsid, pageaddr, ptem; hwaddr hash; - int segment_bits, target_page_bits; + uint64_t vsid, epnshift, epnmask, epn, ptem; - if (slb->vsid & SLB_VSID_B) { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; - segment_bits = 40; - } else { - vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; - segment_bits = 28; - } - - target_page_bits = (slb->vsid & SLB_VSID_L) + /* Page size according to the SLB, which we use to generate the + * EPN for hash table lookup.. When we implement more recent MMU + * extensions this might be different from the actual page size + * encoded in the PTE */ + epnshift = (slb->vsid & SLB_VSID_L) ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + epnmask = ~((1ULL << epnshift) - 1); - pageaddr = eaddr & ((1ULL << segment_bits) - - (1ULL << target_page_bits)); if (slb->vsid & SLB_VSID_B) { - hash = vsid ^ (vsid << 25) ^ (pageaddr >> target_page_bits); + /* 1TB segment */ + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; + epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask; + hash = vsid ^ (vsid << 25) ^ (epn >> epnshift); } else { - hash = vsid ^ (pageaddr >> target_page_bits); + /* 256M segment */ + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask; + hash = vsid ^ (epn >> epnshift); } - /* Only 5 bits of the page index are used in the AVPN */ - ptem = (slb->vsid & SLB_VSID_PTEM) | - ((pageaddr >> 16) & ((1ULL << segment_bits) - 0x80)); + ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN); /* Page address translation */ LOG_MMU("htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx From 6a9801106ed90b8817128e15b187a9d5f71a54d9 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:32 +0000 Subject: [PATCH 35/58] mmu-hash*: Fold pte_check*() logic into caller With previous cleanups made, the 32-bit and 64-bit pte_check*() functions are pretty trivial and only have one call site. This patch therefore clarifies the overall code flow by folding those functions into their call site. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 48 ++++++++++++++++++--------------------- target-ppc/mmu-hash64.c | 50 +++++++++++++++++++---------------------- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 8b403fecb1..53e86de991 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -295,31 +295,8 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, } } -static int pte_check_hash32(struct mmu_ctx_hash32 *ctx, target_ulong pte0, - target_ulong pte1, int rwx) -{ - int access, ret, pp; - - pp = pte1 & HPTE32_R_PP; - /* Compute access rights */ - access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte1; - ctx->prot = access; - ret = ppc_hash32_check_prot(ctx->prot, rwx); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { - /* Access right violation */ - LOG_MMU("PTE access rejected\n"); - } - - return ret; -} - -static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, - uint32_t *pte1p, int ret, int rwx) +static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, uint32_t *pte1p, + int ret, int rwx) { int store = 0; @@ -420,6 +397,8 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, hwaddr pte_offset; ppc_hash_pte32_t pte; + assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { /* Translation is off */ @@ -461,7 +440,24 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, /* 7. Check access permissions */ ctx->key = (((sr & SR32_KP) && (msr_pr != 0)) || ((sr & SR32_KS) && (msr_pr == 0))) ? 1 : 0; - ret = pte_check_hash32(ctx, pte.pte0, pte.pte1, rwx); + + int access, pp; + + pp = pte.pte1 & HPTE32_R_PP; + /* Compute access rights */ + access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte.pte1; + ctx->prot = access; + ret = ppc_hash32_check_prot(ctx->prot, rwx); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); + } + /* Update page flags */ if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 10372f0ed8..2e109f4896 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -297,32 +297,6 @@ static int ppc_hash64_check_prot(int prot, int rwx) return ret; } -static int pte64_check(struct mmu_ctx_hash64 *ctx, target_ulong pte0, - target_ulong pte1, int rwx) -{ - int access, ret, pp; - bool nx; - - pp = (pte1 & HPTE64_R_PP) | ((pte1 & HPTE64_R_PP0) >> 61); - /* No execute if either noexec or guarded bits set */ - nx = (pte1 & HPTE64_R_N) || (pte1 & HPTE64_R_G); - /* Compute access rights */ - access = ppc_hash64_pp_check(ctx->key, pp, nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte1; - ctx->prot = access; - ret = ppc_hash64_check_prot(ctx->prot, rwx); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { - /* Access right violation */ - LOG_MMU("PTE access rejected\n"); - } - - return ret; -} - static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, uint64_t *pte1p, int ret, int rw) { @@ -439,6 +413,8 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ppc_hash_pte64_t pte; int target_page_bits; + assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { /* Translation is off */ @@ -471,7 +447,27 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ctx->key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) : (slb->vsid & SLB_VSID_KS)); - ret = pte64_check(ctx, pte.pte0, pte.pte1, rwx); + + int access, pp; + bool nx; + + pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61); + /* No execute if either noexec or guarded bits set */ + nx = (pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G); + /* Compute access rights */ + access = ppc_hash64_pp_check(ctx->key, pp, nx); + /* Keep the matching PTE informations */ + ctx->raddr = pte.pte1; + ctx->prot = access; + ret = ppc_hash64_check_prot(ctx->prot, rwx); + if (ret == 0) { + /* Access granted */ + LOG_MMU("PTE access granted !\n"); + } else { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); + } + /* Update page flags */ if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); From 9986ed1ed07a01bdd236524b1a5cee695e68954a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:33 +0000 Subject: [PATCH 36/58] mmu-hash32: Remove odd pointer usage from BAT code In the code for handling BATs, the hash32_bat_size_prot() and hash32_bat_601_size_prot() functions are passed the BAT contents by reference (pointer) for no clear reason, since they only need the values within. This patch removes this odd usage, and uses the resulting change to clean up the caller slightly. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 47 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 53e86de991..b7f6e8f2be 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -115,19 +115,19 @@ static int ppc_hash32_check_prot(int prot, int rwx) /* Perform BAT hit & translation */ static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, target_ulong *BATu, - target_ulong *BATl) + int *validp, int *protp, + target_ulong batu, target_ulong batl) { target_ulong bl; int pp, valid, prot; - bl = (*BATu & BATU32_BL) << 15; + bl = (batu & BATU32_BL) << 15; valid = 0; prot = 0; - if (((msr_pr == 0) && (*BATu & BATU32_VS)) || - ((msr_pr != 0) && (*BATu & BATU32_VP))) { + if (((msr_pr == 0) && (batu & BATU32_VS)) || + ((msr_pr != 0) && (batu & BATU32_VP))) { valid = 1; - pp = *BATl & BATL32_PP; + pp = batl & BATL32_PP; if (pp != 0) { prot = PAGE_READ | PAGE_EXEC; if (pp == 0x2) { @@ -142,22 +142,22 @@ static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp, static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, int *validp, int *protp, - target_ulong *BATu, target_ulong *BATl) + target_ulong batu, target_ulong batl) { target_ulong bl; int key, pp, valid, prot; - bl = (*BATl & BATL32_601_BL) << 17; + bl = (batl & BATL32_601_BL) << 17; LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", - (uint8_t)(*BATl & BATL32_601_BL), bl, ~bl); + (uint8_t)(batl & BATL32_601_BL), bl, ~bl); prot = 0; - valid = !!(*BATl & BATL32_601_V); + valid = !!(batl & BATL32_601_V); if (valid) { - pp = *BATu & BATU32_601_PP; + pp = batu & BATU32_601_PP; if (msr_pr == 0) { - key = !!(*BATu & BATU32_601_KS); + key = !!(batu & BATU32_601_KS); } else { - key = !!(*BATu & BATU32_601_KP); + key = !!(batu & BATU32_601_KP); } prot = ppc_hash32_pp_check(key, pp, 0); } @@ -169,7 +169,7 @@ static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong virtual, int rwx) { - target_ulong *BATlt, *BATut, *BATu, *BATl; + target_ulong *BATlt, *BATut; target_ulong BEPIl, BEPIu, bl; int i, valid, prot; int ret = -1; @@ -184,25 +184,26 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, BATut = env->DBAT[0]; } for (i = 0; i < env->nb_BATs; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & BATU32_BEPIU; - BEPIl = *BATu & BATU32_BEPIL; + target_ulong batu = BATut[i]; + target_ulong batl = BATlt[i]; + + BEPIu = batu & BATU32_BEPIU; + BEPIl = batu & BATU32_BEPIL; if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - hash32_bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl); + hash32_bat_601_size_prot(env, &bl, &valid, &prot, batu, batl); } else { - hash32_bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); + hash32_bat_size_prot(env, &bl, &valid, &prot, batu, batl); } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); + type == ACCESS_CODE ? 'I' : 'D', i, virtual, batu, batl); if ((virtual & BATU32_BEPIU) == BEPIu && ((virtual & BATU32_BEPIL) & ~bl) == BEPIl) { /* BAT matches */ if (valid != 0) { /* Get physical address */ - ctx->raddr = (*BATl & BATL32_BRPNU) | - ((virtual & BATU32_BEPIL & bl) | (*BATl & BATL32_BRPNL)) | + ctx->raddr = (batl & BATL32_BRPNU) | + ((virtual & BATU32_BEPIL & bl) | (batl & BATL32_BRPNL)) | (virtual & 0x0001F000); /* Compute access rights */ ctx->prot = prot; From e1d4951593426c886c1856211bc6ca81ed7c435d Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:34 +0000 Subject: [PATCH 37/58] mmu-hash32: Split BAT size logic from permissions logic hash32_bat_size_prot() and its 601 variant, as the name suggests, returns both a BAT's size - needed to search for a matching BAT - and its permissions, only relevant once a matching BAT has been located. There's no particular advantage to combining these, so we split these roles into seperate functions for clarity. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 71 +++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index b7f6e8f2be..6c71ebd715 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -113,57 +113,64 @@ static int ppc_hash32_check_prot(int prot, int rwx) return ret; } -/* Perform BAT hit & translation */ -static void hash32_bat_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, - target_ulong batu, target_ulong batl) +static void hash32_bat_size(CPUPPCState *env, target_ulong *blp, int *validp, + target_ulong batu, target_ulong batl) { target_ulong bl; - int pp, valid, prot; + int valid; bl = (batu & BATU32_BL) << 15; valid = 0; - prot = 0; if (((msr_pr == 0) && (batu & BATU32_VS)) || ((msr_pr != 0) && (batu & BATU32_VP))) { valid = 1; - pp = batl & BATL32_PP; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } } *blp = bl; *validp = valid; - *protp = prot; } -static void hash32_bat_601_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, - target_ulong batu, target_ulong batl) +static int hash32_bat_prot(CPUPPCState *env, + target_ulong batu, target_ulong batl) +{ + int pp, prot; + + prot = 0; + pp = batl & BATL32_PP; + if (pp != 0) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + return prot; +} + +static void hash32_bat_601_size(CPUPPCState *env, target_ulong *blp, int *validp, + target_ulong batu, target_ulong batl) { target_ulong bl; - int key, pp, valid, prot; + int valid; bl = (batl & BATL32_601_BL) << 17; LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", (uint8_t)(batl & BATL32_601_BL), bl, ~bl); - prot = 0; valid = !!(batl & BATL32_601_V); - if (valid) { - pp = batu & BATU32_601_PP; - if (msr_pr == 0) { - key = !!(batu & BATU32_601_KS); - } else { - key = !!(batu & BATU32_601_KP); - } - prot = ppc_hash32_pp_check(key, pp, 0); - } *blp = bl; *validp = valid; - *protp = prot; +} + +static int hash32_bat_601_prot(CPUPPCState *env, + target_ulong batu, target_ulong batl) +{ + int key, pp; + + pp = batu & BATU32_601_PP; + if (msr_pr == 0) { + key = !!(batu & BATU32_601_KS); + } else { + key = !!(batu & BATU32_601_KP); + } + return ppc_hash32_pp_check(key, pp, 0); } static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, @@ -190,9 +197,11 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, BEPIu = batu & BATU32_BEPIU; BEPIl = batu & BATU32_BEPIL; if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - hash32_bat_601_size_prot(env, &bl, &valid, &prot, batu, batl); + hash32_bat_601_size(env, &bl, &valid, batu, batl); + prot = hash32_bat_601_prot(env, batu, batl); } else { - hash32_bat_size_prot(env, &bl, &valid, &prot, batu, batl); + hash32_bat_size(env, &bl, &valid, batu, batl); + prot = hash32_bat_prot(env, batu, batl); } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, From 6fc76aa9adc1c8896a97059f12a1e5e6c1820c64 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:35 +0000 Subject: [PATCH 38/58] mmu-hash32: Clean up BAT matching logic The code to search for a matching BAT for a virtual address is somewhat longwinded and awkward. In particular, it relies on seperate size and validity information being returned from the hash32_bat_size() function (and 601 specific variant). We simplify this by having hash32_bat_size() return instead a mask of the virtual address bits to match, and 0 for invalid (since a BAT can never match the entire address space). Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 72 +++++++++++++++++------------------------ target-ppc/mmu-hash32.h | 4 --- 2 files changed, 29 insertions(+), 47 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 6c71ebd715..40d3564626 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -113,20 +113,15 @@ static int ppc_hash32_check_prot(int prot, int rwx) return ret; } -static void hash32_bat_size(CPUPPCState *env, target_ulong *blp, int *validp, - target_ulong batu, target_ulong batl) +static target_ulong hash32_bat_size(CPUPPCState *env, + target_ulong batu, target_ulong batl) { - target_ulong bl; - int valid; - - bl = (batu & BATU32_BL) << 15; - valid = 0; - if (((msr_pr == 0) && (batu & BATU32_VS)) || - ((msr_pr != 0) && (batu & BATU32_VP))) { - valid = 1; + if ((msr_pr && !(batu & BATU32_VP)) + || (!msr_pr && !(batu & BATU32_VS))) { + return 0; } - *blp = bl; - *validp = valid; + + return BATU32_BEPI & ~((batu & BATU32_BL) << 15); } static int hash32_bat_prot(CPUPPCState *env, @@ -145,18 +140,14 @@ static int hash32_bat_prot(CPUPPCState *env, return prot; } -static void hash32_bat_601_size(CPUPPCState *env, target_ulong *blp, int *validp, +static target_ulong hash32_bat_601_size(CPUPPCState *env, target_ulong batu, target_ulong batl) { - target_ulong bl; - int valid; + if (!(batl & BATL32_601_V)) { + return 0; + } - bl = (batl & BATL32_601_BL) << 17; - LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n", - (uint8_t)(batl & BATL32_601_BL), bl, ~bl); - valid = !!(batl & BATL32_601_V); - *blp = bl; - *validp = valid; + return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); } static int hash32_bat_601_prot(CPUPPCState *env, @@ -177,8 +168,7 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong virtual, int rwx) { target_ulong *BATlt, *BATut; - target_ulong BEPIl, BEPIu, bl; - int i, valid, prot; + int i, prot; int ret = -1; LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, @@ -193,37 +183,33 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, for (i = 0; i < env->nb_BATs; i++) { target_ulong batu = BATut[i]; target_ulong batl = BATlt[i]; + target_ulong mask; - BEPIu = batu & BATU32_BEPIU; - BEPIl = batu & BATU32_BEPIL; if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - hash32_bat_601_size(env, &bl, &valid, batu, batl); + mask = hash32_bat_601_size(env, batu, batl); prot = hash32_bat_601_prot(env, batu, batl); } else { - hash32_bat_size(env, &bl, &valid, batu, batl); + mask = hash32_bat_size(env, batu, batl); prot = hash32_bat_prot(env, batu, batl); } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, batu, batl); - if ((virtual & BATU32_BEPIU) == BEPIu && - ((virtual & BATU32_BEPIL) & ~bl) == BEPIl) { + + if (mask && ((virtual & mask) == (batu & BATU32_BEPI))) { /* BAT matches */ - if (valid != 0) { - /* Get physical address */ - ctx->raddr = (batl & BATL32_BRPNU) | - ((virtual & BATU32_BEPIL & bl) | (batl & BATL32_BRPNL)) | - (virtual & 0x0001F000); - /* Compute access rights */ - ctx->prot = prot; - ret = ppc_hash32_check_prot(ctx->prot, rwx); - if (ret == 0) { - LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", - i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); - } - break; + /* Get physical address */ + ctx->raddr = (batl & mask) | (virtual & ~mask); + ctx->raddr &= TARGET_PAGE_MASK; + /* Compute access rights */ + ctx->prot = prot; + ret = ppc_hash32_check_prot(ctx->prot, rwx); + if (ret == 0) { + LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", + i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', + ctx->prot & PAGE_WRITE ? 'W' : '-'); } + break; } } if (ret < 0) { diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h index f990edc3ef..884786b97a 100644 --- a/target-ppc/mmu-hash32.h +++ b/target-ppc/mmu-hash32.h @@ -22,16 +22,12 @@ int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, * Block Address Translation (BAT) definitions */ -#define BATU32_BEPIU 0xf0000000 -#define BATU32_BEPIL 0x0ffe0000 #define BATU32_BEPI 0xfffe0000 #define BATU32_BL 0x00001ffc #define BATU32_VS 0x00000002 #define BATU32_VP 0x00000001 -#define BATL32_BRPNU 0xf0000000 -#define BATL32_BRPNL 0x0ffe0000 #define BATL32_BRPN 0xfffe0000 #define BATL32_WIMG 0x00000078 #define BATL32_PP 0x00000003 From 145e52f31826045d8cccf5b840dc77d21a696651 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:36 +0000 Subject: [PATCH 39/58] mmu-hash32: Cleanup BAT lookup This patch makes a general cleanup of the ppc_hash32_get_bat() function, renaming it to ppc_hash32_bat_lookup(). In particular, the new function only looks for a matching BAT, with the permissions check from the old function moved to the caller. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 84 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 40d3564626..d4d91dd6e2 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -164,15 +164,14 @@ static int hash32_bat_601_prot(CPUPPCState *env, return ppc_hash32_pp_check(key, pp, 0); } -static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong virtual, int rwx) +static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, + int *prot) { target_ulong *BATlt, *BATut; - int i, prot; - int ret = -1; + int i; LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - rwx == 2 ? 'I' : 'D', virtual); + rwx == 2 ? 'I' : 'D', ea); if (rwx == 2) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; @@ -187,52 +186,46 @@ static int ppc_hash32_get_bat(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, if (unlikely(env->mmu_model == POWERPC_MMU_601)) { mask = hash32_bat_601_size(env, batu, batl); - prot = hash32_bat_601_prot(env, batu, batl); } else { mask = hash32_bat_size(env, batu, batl); - prot = hash32_bat_prot(env, batu, batl); } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', i, virtual, batu, batl); + type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl); - if (mask && ((virtual & mask) == (batu & BATU32_BEPI))) { - /* BAT matches */ - /* Get physical address */ - ctx->raddr = (batl & mask) | (virtual & ~mask); - ctx->raddr &= TARGET_PAGE_MASK; - /* Compute access rights */ - ctx->prot = prot; - ret = ppc_hash32_check_prot(ctx->prot, rwx); - if (ret == 0) { - LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", - i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); + if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { + hwaddr raddr = (batl & mask) | (ea & ~mask); + + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + *prot = hash32_bat_601_prot(env, batu, batl); + } else { + *prot = hash32_bat_prot(env, batu, batl); } - break; + + return raddr & TARGET_PAGE_MASK; } } - if (ret < 0) { -#if defined(DEBUG_BATS) - if (qemu_log_enabled()) { - LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual); - for (i = 0; i < 4; i++) { - BATu = &BATut[i]; - BATl = &BATlt[i]; - BEPIu = *BATu & BATU32_BEPIU; - BEPIl = *BATu & BATU32_BEPIL; - bl = (*BATu & 0x00001FFC) << 15; - LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, - *BATu, *BATl, BEPIu, BEPIl, bl); - } - } -#endif - } + /* No hit */ - return ret; +#if defined(DEBUG_BATS) + if (qemu_log_enabled()) { + LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", ea); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & 0x00001FFC) << 15; + LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx + " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " + TARGET_FMT_lx " " TARGET_FMT_lx "\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea, + *BATu, *BATl, BEPIu, BEPIl, bl); + } + } +#endif + + return -1; } static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, @@ -405,9 +398,12 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { - ret = ppc_hash32_get_bat(env, ctx, eaddr, rwx); - if (ret == 0) { - return 0; + ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot); + if (ctx->raddr != -1) { + ret = ppc_hash32_check_prot(ctx->prot, rwx); + if (ret == 0) { + return 0; + } } } From 59acbe28552eb7bd7be75b22b3f3de93d7d40556 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:37 +0000 Subject: [PATCH 40/58] mmu-hash32: Don't look up page tables on BAT permission error BEHAVIOUR CHANGE Currently, on any failure translating an address with BATs, we proceed to normal segment and page table translation. That's incorrect if the BAT error was due to permissions, rather than not finding a matching BAT. We've gotten away with it because a guest would not usually put translations for the same address in both BATs and page table. Nonetheless this patch corrects the logic, only doing page table lookup if no BAT is found. A matching BAT with bad permissions will now correctly trigger an exception. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index d4d91dd6e2..348809244d 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -400,10 +400,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, if (env->nb_BATs != 0) { ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot); if (ctx->raddr != -1) { - ret = ppc_hash32_check_prot(ctx->prot, rwx); - if (ret == 0) { - return 0; - } + return ppc_hash32_check_prot(ctx->prot, rwx); } } From 87dc3fd13e9c573fc435678973d8eb9726d50c3b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:38 +0000 Subject: [PATCH 41/58] mmu-hash*: Don't update PTE flags when permission is denied BEHAVIOUR CHANGE Currently if ppc_hash{32,64}_translate() finds a PTE matching the given virtual address, it will always update the PTE's R & C (Referenced and Changed) bits. This happens even if the PTE's permissions mean we are about to deny the translation. This is clearly a bug, although we get away with it because: a) It will only incorrectly set, never reset the bits, which should not cause guest correctness problems. b) Linux guests never use the R & C bits anyway. This patch fixes the behaviour, only updating R & C when access is granted by the PTE. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 12 +++++++----- target-ppc/mmu-hash64.c | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 348809244d..ae606fdedc 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -439,15 +439,17 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ctx->raddr = pte.pte1; ctx->prot = access; ret = ppc_hash32_check_prot(ctx->prot, rwx); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { + + if (ret) { /* Access right violation */ LOG_MMU("PTE access rejected\n"); + return ret; } - /* Update page flags */ + LOG_MMU("PTE access granted !\n"); + + /* 8. Update PTE referenced and changed bits if necessary */ + if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 2e109f4896..f7aa352a9b 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -460,15 +460,17 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ctx->raddr = pte.pte1; ctx->prot = access; ret = ppc_hash64_check_prot(ctx->prot, rwx); - if (ret == 0) { - /* Access granted */ - LOG_MMU("PTE access granted !\n"); - } else { + + if (ret) { /* Access right violation */ LOG_MMU("PTE access rejected\n"); + return ret; } - /* Update page flags */ + LOG_MMU("PTE access granted !\n"); + + /* 6. Update PTE referenced and changed bits if necessary */ + if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); } From e1a53ba2e0f6dcf4a8b25586cc9d8ec1e408305c Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:39 +0000 Subject: [PATCH 42/58] mmu-hash32: Remove nx from context structure Previous cleanups have meant the nx field of the mmu_ctx_hash32 structure is now only used within ppc_hash32_translate(), and so it can be replaced by a local variable. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index ae606fdedc..2b88b9ff3a 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -45,7 +45,6 @@ struct mmu_ctx_hash32 { hwaddr raddr; /* Real address */ int prot; /* Protection bits */ int key; /* Access key */ - int nx; /* Non-execute area */ }; static int ppc_hash32_pp_check(int key, int pp, int nx) @@ -383,6 +382,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, { int ret; target_ulong sr; + bool nx; hwaddr pte_offset; ppc_hash_pte32_t pte; @@ -414,8 +414,8 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } /* 5. Check for segment level no-execute violation */ - ctx->nx = !!(sr & SR32_NX); - if ((rwx == 2) && ctx->nx) { + nx = !!(sr & SR32_NX); + if ((rwx == 2) && nx) { return -3; } @@ -434,7 +434,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, pp = pte.pte1 & HPTE32_R_PP; /* Compute access rights */ - access = ppc_hash32_pp_check(ctx->key, pp, ctx->nx); + access = ppc_hash32_pp_check(ctx->key, pp, nx); /* Keep the matching PTE informations */ ctx->raddr = pte.pte1; ctx->prot = access; From e01b444523e2b0c663b42b3e8f44ef48a6153051 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:40 +0000 Subject: [PATCH 43/58] mmu-hash*: Clean up permission checking Currently checking of PTE permission bits is split messily amongst ppc_hash{32,64}_pp_check(), ppc_hash{32,64}_check_prot() and their callers. This patch cleans this up to have the new function ppc_hash{32,64}_pte_prot() compute the page permissions from the SLBE (for 64-bit) or segment register (32-bit) and the pte. A greatly simplified version of the actual permissions check is then open coded in the callers. The 32-bit version of ppc_hash32_pte_prot() is implemented in terms of ppc_hash32_pp_prot(), a renamed and slightly cleaned up version of the old ppc_hash32_pp_check(), which is also used for checking BAT permissions on the 601. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 97 +++++++++++++++++------------------------ target-ppc/mmu-hash64.c | 95 ++++++++++++++-------------------------- 2 files changed, 74 insertions(+), 118 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 2b88b9ff3a..6581d0f425 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -47,69 +47,60 @@ struct mmu_ctx_hash32 { int key; /* Access key */ }; -static int ppc_hash32_pp_check(int key, int pp, int nx) +static int ppc_hash32_pp_prot(int key, int pp, int nx) { - int access; + int prot; - /* Compute access rights */ - access = 0; if (key == 0) { switch (pp) { case 0x0: case 0x1: case 0x2: - access |= PAGE_WRITE; - /* No break here */ - case 0x3: - access |= PAGE_READ; + prot = PAGE_READ | PAGE_WRITE; break; + + case 0x3: + prot = PAGE_READ; + break; + + default: + abort(); } } else { switch (pp) { case 0x0: - access = 0; + prot = 0; break; + case 0x1: case 0x3: - access = PAGE_READ; + prot = PAGE_READ; break; + case 0x2: - access = PAGE_READ | PAGE_WRITE; + prot = PAGE_READ | PAGE_WRITE; break; + + default: + abort(); } } if (nx == 0) { - access |= PAGE_EXEC; + prot |= PAGE_EXEC; } - return access; + return prot; } -static int ppc_hash32_check_prot(int prot, int rwx) +static int ppc_hash32_pte_prot(CPUPPCState *env, + target_ulong sr, ppc_hash_pte32_t pte) { - int ret; + unsigned pp, key; - if (rwx == 2) { - if (prot & PAGE_EXEC) { - ret = 0; - } else { - ret = -2; - } - } else if (rwx) { - if (prot & PAGE_WRITE) { - ret = 0; - } else { - ret = -2; - } - } else { - if (prot & PAGE_READ) { - ret = 0; - } else { - ret = -2; - } - } + key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + pp = pte.pte1 & HPTE32_R_PP; - return ret; + return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); } static target_ulong hash32_bat_size(CPUPPCState *env, @@ -160,7 +151,7 @@ static int hash32_bat_601_prot(CPUPPCState *env, } else { key = !!(batu & BATU32_601_KP); } - return ppc_hash32_pp_check(key, pp, 0); + return ppc_hash32_pp_prot(key, pp, 0); } static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, @@ -380,11 +371,10 @@ static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong eaddr, int rwx) { - int ret; target_ulong sr; - bool nx; hwaddr pte_offset; ppc_hash_pte32_t pte; + const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); @@ -400,7 +390,10 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, if (env->nb_BATs != 0) { ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot); if (ctx->raddr != -1) { - return ppc_hash32_check_prot(ctx->prot, rwx); + if (need_prot[rwx] & ~ctx->prot) { + return -2; + } + return 0; } } @@ -414,8 +407,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } /* 5. Check for segment level no-execute violation */ - nx = !!(sr & SR32_NX); - if ((rwx == 2) && nx) { + if ((rwx == 2) && (sr & SR32_NX)) { return -3; } @@ -427,34 +419,27 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 7. Check access permissions */ - ctx->key = (((sr & SR32_KP) && (msr_pr != 0)) || - ((sr & SR32_KS) && (msr_pr == 0))) ? 1 : 0; - int access, pp; + ctx->prot = ppc_hash32_pte_prot(env, sr, pte); - pp = pte.pte1 & HPTE32_R_PP; - /* Compute access rights */ - access = ppc_hash32_pp_check(ctx->key, pp, nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte.pte1; - ctx->prot = access; - ret = ppc_hash32_check_prot(ctx->prot, rwx); - - if (ret) { + if (need_prot[rwx] & ~ctx->prot) { /* Access right violation */ LOG_MMU("PTE access rejected\n"); - return ret; + return -2; } LOG_MMU("PTE access granted !\n"); /* 8. Update PTE referenced and changed bits if necessary */ - if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { + if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, 0, rwx) == 1) { ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); } - return ret; + /* Keep the matching PTE informations */ + ctx->raddr = pte.pte1; + + return 0; } hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index f7aa352a9b..1458f15dd2 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -43,7 +43,6 @@ struct mmu_ctx_hash64 { hwaddr raddr; /* Real address */ int prot; /* Protection bits */ - int key; /* Access key */ }; /* @@ -229,72 +228,55 @@ target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb) * 64-bit hash table MMU handling */ -static int ppc_hash64_pp_check(int key, int pp, bool nx) +static int ppc_hash64_pte_prot(CPUPPCState *env, + ppc_slb_t *slb, ppc_hash_pte64_t pte) { - int access; + unsigned pp, key; + /* Some pp bit combinations have undefined behaviour, so default + * to no access in those cases */ + int prot = 0; + + key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) + : (slb->vsid & SLB_VSID_KS)); + pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61); - /* Compute access rights */ - /* When pp is 4, 5 or 7, the result is undefined. Set it to noaccess */ - access = 0; if (key == 0) { switch (pp) { case 0x0: case 0x1: case 0x2: - access |= PAGE_WRITE; - /* No break here */ + prot = PAGE_READ | PAGE_WRITE; + break; + case 0x3: case 0x6: - access |= PAGE_READ; + prot = PAGE_READ; break; } } else { switch (pp) { case 0x0: case 0x6: - access = 0; + prot = 0; break; + case 0x1: case 0x3: - access = PAGE_READ; + prot = PAGE_READ; break; + case 0x2: - access = PAGE_READ | PAGE_WRITE; + prot = PAGE_READ | PAGE_WRITE; break; } } - if (!nx) { - access |= PAGE_EXEC; + + /* No execute if either noexec or guarded bits set */ + if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G)) { + prot |= PAGE_EXEC; } - return access; -} - -static int ppc_hash64_check_prot(int prot, int rwx) -{ - int ret; - - if (rwx == 2) { - if (prot & PAGE_EXEC) { - ret = 0; - } else { - ret = -2; - } - } else if (rwx == 1) { - if (prot & PAGE_WRITE) { - ret = 0; - } else { - ret = -2; - } - } else { - if (prot & PAGE_READ) { - ret = 0; - } else { - ret = -2; - } - } - - return ret; + return prot; } static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, @@ -407,11 +389,11 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, target_ulong eaddr, int rwx) { - int ret; ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; int target_page_bits; + const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); @@ -444,37 +426,26 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 5. Check access permissions */ - ctx->key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP) - : (slb->vsid & SLB_VSID_KS)); + ctx->prot = ppc_hash64_pte_prot(env, slb, pte); - int access, pp; - bool nx; - - pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61); - /* No execute if either noexec or guarded bits set */ - nx = (pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G); - /* Compute access rights */ - access = ppc_hash64_pp_check(ctx->key, pp, nx); - /* Keep the matching PTE informations */ - ctx->raddr = pte.pte1; - ctx->prot = access; - ret = ppc_hash64_check_prot(ctx->prot, rwx); - - if (ret) { + if ((need_prot[rwx] & ~ctx->prot) != 0) { /* Access right violation */ LOG_MMU("PTE access rejected\n"); - return ret; + return -2; } LOG_MMU("PTE access granted !\n"); /* 6. Update PTE referenced and changed bits if necessary */ - if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, ret, rwx) == 1) { + if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, 0, rwx) == 1) { ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); } + /* Keep the matching PTE informations */ + ctx->raddr = pte.pte1; + /* We have a TLB that saves 4K pages, so let's * split a huge page to 4k chunks */ target_page_bits = (slb->vsid & SLB_VSID_L) @@ -483,7 +454,7 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) & TARGET_PAGE_MASK; } - return ret; + return 0; } hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) From 57d0a39d98b0d0e4712e736084667bcb5aed3474 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:41 +0000 Subject: [PATCH 44/58] mmu-hash64: Factor SLB N bit into permissions bits BEHAVIOUR CHANGE Currently, for 64-bit hash mmu, the execute protection bit placed into the qemu tlb is based only on the N (No execute) bit from the PTE. However, No Execute can also be set at the segment level. We do check this on execute faults, but this still means we could incorrectly allow execution of code from a No Execute segment, if a prior read or write fault caused the page to be loaded into the qemu tlb with PROT_EXEC set. To correct this, we (re-)check the segment level no execute permission when generating the protection bits for the qemu tlb. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 1458f15dd2..5607ce8dec 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -272,7 +272,8 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, } /* No execute if either noexec or guarded bits set */ - if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G)) { + if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G) + || (slb->vsid & SLB_VSID_N)) { prot |= PAGE_EXEC; } From b344074642e58fc83635c38105f38b85fc086666 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:42 +0000 Subject: [PATCH 45/58] mmu-hash*: Clean up PTE flags update Currently the ppc_hash{32,64}_pte_update_flags() helper functions update a PTE's referenced and changed bits as necessary to reflect the access. It is somewhat long winded, though. This patch open codes them in their (single) callers, in a simpler way. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 39 ++++++++++++--------------------------- target-ppc/mmu-hash64.c | 39 ++++++++++++--------------------------- 2 files changed, 24 insertions(+), 54 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 6581d0f425..314b7d1634 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -274,31 +274,6 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, } } -static int ppc_hash32_pte_update_flags(struct mmu_ctx_hash32 *ctx, uint32_t *pte1p, - int ret, int rwx) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & HPTE32_R_R)) { - /* Update accessed flag */ - *pte1p |= HPTE32_R_R; - store = 1; - } - if (!(*pte1p & HPTE32_R_C)) { - if (rwx == 1 && ret == 0) { - /* Update changed flag */ - *pte1p |= HPTE32_R_C; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) { return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; @@ -374,6 +349,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong sr; hwaddr pte_offset; ppc_hash_pte32_t pte; + uint32_t new_pte1; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); @@ -432,8 +408,17 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, /* 8. Update PTE referenced and changed bits if necessary */ - if (ppc_hash32_pte_update_flags(ctx, &pte.pte1, 0, rwx) == 1) { - ppc_hash32_store_hpte1(env, pte_offset, pte.pte1); + new_pte1 = pte.pte1 | HPTE32_R_R; /* set referenced bit */ + if (rwx == 1) { + new_pte1 |= HPTE32_R_C; /* set changed (dirty) bit */ + } else { + /* Treat the page as read-only for now, so that a later write + * will pass through this function again to set the C bit */ + ctx->prot &= ~PAGE_WRITE; + } + + if (new_pte1 != pte.pte1) { + ppc_hash32_store_hpte1(env, pte_offset, new_pte1); } /* Keep the matching PTE informations */ diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 5607ce8dec..c76abb7d22 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -280,31 +280,6 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, return prot; } -static int ppc_hash64_pte_update_flags(struct mmu_ctx_hash64 *ctx, - uint64_t *pte1p, int ret, int rw) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & HPTE64_R_R)) { - /* Update accessed flag */ - *pte1p |= HPTE64_R_R; - store = 1; - } - if (!(*pte1p & HPTE64_R_C)) { - if (rw == 1 && ret == 0) { - /* Update changed flag */ - *pte1p |= HPTE64_R_C; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte64_t *pte) @@ -393,6 +368,7 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; + uint64_t new_pte1; int target_page_bits; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; @@ -440,8 +416,17 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, /* 6. Update PTE referenced and changed bits if necessary */ - if (ppc_hash64_pte_update_flags(ctx, &pte.pte1, 0, rwx) == 1) { - ppc_hash64_store_hpte1(env, pte_offset, pte.pte1); + new_pte1 = pte.pte1 | HPTE64_R_R; /* set referenced bit */ + if (rwx == 1) { + new_pte1 |= HPTE64_R_C; /* set changed (dirty) bit */ + } else { + /* Treat the page as read-only for now, so that a later write + * will pass through this function again to set the C bit */ + ctx->prot &= ~PAGE_WRITE; + } + + if (new_pte1 != pte.pte1) { + ppc_hash64_store_hpte1(env, pte_offset, new_pte1); } /* Keep the matching PTE informations */ From 6d11d998bb866c92b0f81eb3cea2f7a3e617feb8 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:43 +0000 Subject: [PATCH 46/58] mmu-hash*: Clean up real address calculation More recent 64-bit hash MMUs support multiple page sizes, and PTEs for large pages only include the offset of the whole large page. But the qemu tlb only handles pages of the base size (4k) so we need to break up the large pages into 4k pieces for the qemu tlb. To do that we have a somewhat awkward piece of code that adds the folds address bits 4k and the page size from the virtual address into the real address from the pte. This patch simplifies this redefining the raddr output of ppc_hash64_translate() to be the full real address of the faulting address, rather than just the (4k) page offset. Computing that turns out to be simpler, and is fine for the caller, since it already masks with TARGET_PAGE_MASK before inserting into the qemu tlb. The multiple page size complication doesn't exist for 32-bit hash mmus, but we make an analogous cleanup there for consistency. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 14 ++++++++++++-- target-ppc/mmu-hash64.c | 26 +++++++++++++++----------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 314b7d1634..e5ee29bfc7 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -343,6 +343,15 @@ static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, return pte_offset; } +static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, + target_ulong eaddr) +{ + hwaddr rpn = pte.pte1; + hwaddr mask = ~TARGET_PAGE_MASK; + + return (rpn & ~mask) | (eaddr & mask); +} + static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, target_ulong eaddr, int rwx) { @@ -421,8 +430,9 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, ppc_hash32_store_hpte1(env, pte_offset, new_pte1); } - /* Keep the matching PTE informations */ - ctx->raddr = pte.pte1; + /* 9. Determine the real address from the PTE */ + + ctx->raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); return 0; } diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index c76abb7d22..5e168d5e8d 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -362,6 +362,18 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, return pte_offset; } +static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, + target_ulong eaddr) +{ + hwaddr rpn = pte.pte1; + /* FIXME: Add support for SLLP extended page sizes */ + int target_page_bits = (slb->vsid & SLB_VSID_L) + ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; + hwaddr mask = (1ULL << target_page_bits) - 1; + + return (rpn & ~mask) | (eaddr & mask); +} + static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, target_ulong eaddr, int rwx) { @@ -369,7 +381,6 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, hwaddr pte_offset; ppc_hash_pte64_t pte; uint64_t new_pte1; - int target_page_bits; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); @@ -429,17 +440,10 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, ppc_hash64_store_hpte1(env, pte_offset, new_pte1); } - /* Keep the matching PTE informations */ - ctx->raddr = pte.pte1; + /* 7. Determine the real address from the PTE */ + + ctx->raddr = ppc_hash64_pte_raddr(slb, pte, eaddr); - /* We have a TLB that saves 4K pages, so let's - * split a huge page to 4k chunks */ - target_page_bits = (slb->vsid & SLB_VSID_L) - ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; - if (target_page_bits != TARGET_PAGE_BITS) { - ctx->raddr |= (eaddr & ((1 << target_page_bits) - 1)) - & TARGET_PAGE_MASK; - } return 0; } From 75d5ec89c03cb2f1a2bd0d9912e624ceb6fd1999 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:44 +0000 Subject: [PATCH 47/58] mmu-hash*: Correctly mask RPN from hash PTE BEHAVIOUR CHANGE At present we take the whole of word 1 of the hash PTE as the real page number used to calculate the translated address. This is incorrect, because it leaves the flags from the low bits of PTE word 1 in place in the rpm. We mostly get away with that because the value is later masked by TARGET_PAGE_MASK. More recent 64-bit CPUs also have a small number of flag bits (PP0 and KEY) in the top bits of PTE word 1. Any guest which used those bits would fail with the current code. This patch fixes the problem by correctly masking out the RPN field of PTE word 1. This is safe, even for older CPUs which didn't have PP0 and KEY, because although the RPN notionally extended to the very top of PTE word 1, none of those CPUs actually implemented that many real address bits. We add analogous masking to the 32-bit code, even though it also doesn't have the high flag bits, for consistency and clarity. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 2 +- target-ppc/mmu-hash64.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index e5ee29bfc7..07e9b8c85f 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -346,7 +346,7 @@ static hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, target_ulong eaddr) { - hwaddr rpn = pte.pte1; + hwaddr rpn = pte.pte1 & HPTE32_R_RPN; hwaddr mask = ~TARGET_PAGE_MASK; return (rpn & ~mask) | (eaddr & mask); diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 5e168d5e8d..d986c0fd57 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -365,7 +365,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, target_ulong eaddr) { - hwaddr rpn = pte.pte1; + hwaddr rpn = pte.pte1 & HPTE64_R_RPN; /* FIXME: Add support for SLLP extended page sizes */ int target_page_bits = (slb->vsid & SLB_VSID_L) ? TARGET_PAGE_BITS_16M : TARGET_PAGE_BITS; From 5883d8b29691e15c72e338a586509abfc65c3106 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:45 +0000 Subject: [PATCH 48/58] mmu-hash*: Don't use full ppc_hash{32, 64}_translate() path for get_phys_page_debug() Currently the hash mmu versionsof get_phys_page_debug() use the same ppc64_hash64_translate() function to do the translation logic as the normal mm fault handler code. That sounds like a good idea, but has some complications. The debug path doesn't need, or even want some parts of the full translation path, like permissions checking. Furthermore, the pte flags update included in the normal path means that the debug call is not quite side effect free. This patch, therefore, reimplements get_phys_page_debug as the minimal required subset of the full translation path. Signed-off-by: David Gibson `z Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 34 +++++++++++++++++++++++++++------- target-ppc/mmu-hash64.c | 19 ++++++++++++++++--- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 07e9b8c85f..5ec1a0942c 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -437,18 +437,38 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, return 0; } -hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong addr) +hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) { - struct mmu_ctx_hash32 ctx; + target_ulong sr; + hwaddr pte_offset; + ppc_hash_pte32_t pte; + int prot; - /* FIXME: Will not behave sanely for direct store segments, but - * they're almost never used */ - if (unlikely(ppc_hash32_translate(env, &ctx, addr, 0) - != 0)) { + if (msr_dr == 0) { + /* Translation is off */ + return eaddr; + } + + if (env->nb_BATs != 0) { + hwaddr raddr = ppc_hash32_bat_lookup(env, eaddr, 0, &prot); + if (raddr != -1) { + return raddr; + } + } + + sr = env->sr[eaddr >> 28]; + + if (sr & SR32_T) { + /* FIXME: Add suitable debug support for Direct Store segments */ return -1; } - return ctx.raddr & TARGET_PAGE_MASK; + pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); + if (pte_offset == -1) { + return -1; + } + + return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK; } int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index d986c0fd57..4a7dbbb95b 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -449,13 +449,26 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) { - struct mmu_ctx_hash64 ctx; + ppc_slb_t *slb; + hwaddr pte_offset; + ppc_hash_pte64_t pte; - if (unlikely(ppc_hash64_translate(env, &ctx, addr, 0) != 0)) { + if (msr_dr == 0) { + /* In real mode the top 4 effective address bits are ignored */ + return addr & 0x0FFFFFFFFFFFFFFFULL; + } + + slb = slb_lookup(env, addr); + if (!slb) { return -1; } - return ctx.raddr & TARGET_PAGE_MASK; + pte_offset = ppc_hash64_htab_lookup(env, slb, addr, &pte); + if (pte_offset == -1) { + return -1; + } + + return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; } int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, From caa597bd9f5439cb16653119f362ad85a9f02b55 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:46 +0000 Subject: [PATCH 49/58] mmu-hash*: Merge translate and fault handling functions ppc_hash{32,64}_handle_mmu_fault() is now the only caller of ppc_hash{32,64{_translate(), so this patch combines them together. This means that instead of one returning a variety of non-obvious error codes which then get translated into the various mmu exception conditions, we can just generate the exceptions as we discover problems in the translation path. This also removes the last usage of mmu_ctx_hash{32,64}. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/mmu-hash32.c | 250 ++++++++++++++++++---------------------- target-ppc/mmu-hash64.c | 150 +++++++++--------------- 2 files changed, 167 insertions(+), 233 deletions(-) diff --git a/target-ppc/mmu-hash32.c b/target-ppc/mmu-hash32.c index 5ec1a0942c..f6adf2245e 100644 --- a/target-ppc/mmu-hash32.c +++ b/target-ppc/mmu-hash32.c @@ -238,7 +238,9 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, if (rwx == 2) { /* No code fetch is allowed in direct-store areas */ - return -4; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + return 1; } switch (env->access_type) { @@ -247,10 +249,20 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, break; case ACCESS_FLOAT: /* Floating point load/store */ - return -4; + env->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = eaddr; + return 1; case ACCESS_RES: /* lwarx, ldarx or srwcx. */ - return -4; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x06000000; + } else { + env->spr[SPR_DSISR] = 0x04000000; + } + return 1; case ACCESS_CACHE: /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ /* Should make the instruction do no-op. @@ -260,17 +272,33 @@ static int ppc_hash32_direct_store(CPUPPCState *env, target_ulong sr, return 0; case ACCESS_EXT: /* eciwx or ecowx */ - return -4; + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; + } + return 1; default: qemu_log("ERROR: instruction should not need " "address translation\n"); - return -4; + abort(); } if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { *raddr = eaddr; - return 2; + return 0; } else { - return -2; + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + return 1; } } @@ -352,32 +380,53 @@ static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, return (rpn & ~mask) | (eaddr & mask); } -static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, - target_ulong eaddr, int rwx) +int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, int rwx, + int mmu_idx) { target_ulong sr; hwaddr pte_offset; ppc_hash_pte32_t pte; + int prot; uint32_t new_pte1; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; + hwaddr raddr; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { /* Translation is off */ - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; + raddr = eaddr; + tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, + TARGET_PAGE_SIZE); return 0; } /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { - ctx->raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &ctx->prot); - if (ctx->raddr != -1) { - if (need_prot[rwx] & ~ctx->prot) { - return -2; + raddr = ppc_hash32_bat_lookup(env, eaddr, rwx, &prot); + if (raddr != -1) { + if (need_prot[rwx] & ~prot) { + if (rwx == 2) { + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + } else { + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + return 1; } + + tlb_set_page(env, eaddr & TARGET_PAGE_MASK, + raddr & TARGET_PAGE_MASK, prot, mmu_idx, + TARGET_PAGE_SIZE); return 0; } } @@ -387,30 +436,66 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, /* 4. Handle direct store segments */ if (sr & SR32_T) { - return ppc_hash32_direct_store(env, sr, eaddr, rwx, - &ctx->raddr, &ctx->prot); + if (ppc_hash32_direct_store(env, sr, eaddr, rwx, + &raddr, &prot) == 0) { + tlb_set_page(env, eaddr & TARGET_PAGE_MASK, + raddr & TARGET_PAGE_MASK, prot, mmu_idx, + TARGET_PAGE_SIZE); + return 0; + } else { + return 1; + } } /* 5. Check for segment level no-execute violation */ if ((rwx == 2) && (sr & SR32_NX)) { - return -3; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + return 1; } /* 6. Locate the PTE in the hash table */ pte_offset = ppc_hash32_htab_lookup(env, sr, eaddr, &pte); if (pte_offset == -1) { - return -1; + if (rwx == 2) { + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + } else { + env->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; + } + } + + return 1; } LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 7. Check access permissions */ - ctx->prot = ppc_hash32_pte_prot(env, sr, pte); + prot = ppc_hash32_pte_prot(env, sr, pte); - if (need_prot[rwx] & ~ctx->prot) { + if (need_prot[rwx] & ~prot) { /* Access right violation */ LOG_MMU("PTE access rejected\n"); - return -2; + if (rwx == 2) { + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + } else { + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0a000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + return 1; } LOG_MMU("PTE access granted !\n"); @@ -423,7 +508,7 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, } else { /* Treat the page as read-only for now, so that a later write * will pass through this function again to set the C bit */ - ctx->prot &= ~PAGE_WRITE; + prot &= ~PAGE_WRITE; } if (new_pte1 != pte.pte1) { @@ -432,7 +517,10 @@ static int ppc_hash32_translate(CPUPPCState *env, struct mmu_ctx_hash32 *ctx, /* 9. Determine the real address from the PTE */ - ctx->raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); + raddr = ppc_hash32_pte_raddr(sr, pte, eaddr); + + tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); return 0; } @@ -470,117 +558,3 @@ hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) return ppc_hash32_pte_raddr(sr, pte, eaddr) & TARGET_PAGE_MASK; } - -int ppc_hash32_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, - int mmu_idx) -{ - struct mmu_ctx_hash32 ctx; - int ret = 0; - - ret = ppc_hash32_translate(env, &ctx, address, rwx); - if (ret == 0) { - tlb_set_page(env, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { - LOG_MMU_STATE(env); - if (rwx == 2) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x40000000; - break; - case -2: - /* Access rights violation */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; - break; - case -3: - /* No execute protection violation */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; - case -4: - /* Direct store exception */ - /* No code fetch is allowed in direct-store areas */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; - } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x42000000; - } else { - env->spr[SPR_DSISR] = 0x40000000; - } - break; - case -2: - /* Access rights violation */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } - break; - case -4: - /* Direct store exception */ - switch (env->access_type) { - case ACCESS_FLOAT: - /* Floating point load/store */ - env->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = address; - break; - case ACCESS_RES: - /* lwarx, ldarx or stwcx. */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x06000000; - } else { - env->spr[SPR_DSISR] = 0x04000000; - } - break; - case ACCESS_EXT: - /* eciwx or ecowx */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x06100000; - } else { - env->spr[SPR_DSISR] = 0x04100000; - } - break; - default: - printf("DSI: invalid exception (%d)\n", ret); - env->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = address; - break; - } - break; - } - } -#if 0 - printf("%s: set exception to %d %02x\n", __func__, - env->exception, env->error_code); -#endif - ret = 1; - } - - return ret; -} diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 4a7dbbb95b..f18c98f021 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -40,11 +40,6 @@ # define LOG_SLB(...) do { } while (0) #endif -struct mmu_ctx_hash64 { - hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ -}; - /* * SLB handling */ @@ -374,14 +369,16 @@ static hwaddr ppc_hash64_pte_raddr(ppc_slb_t *slb, ppc_hash_pte64_t pte, return (rpn & ~mask) | (eaddr & mask); } -static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, - target_ulong eaddr, int rwx) +int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, + int rwx, int mmu_idx) { ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; + int prot; uint64_t new_pte1; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; + hwaddr raddr; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); @@ -389,8 +386,10 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, 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 */ - ctx->raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; - ctx->prot = PAGE_READ | PAGE_EXEC | PAGE_WRITE; + raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, + TARGET_PAGE_SIZE); return 0; } @@ -398,29 +397,65 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, slb = slb_lookup(env, eaddr); if (!slb) { - return -5; + if (rwx == 2) { + env->exception_index = POWERPC_EXCP_ISEG; + env->error_code = 0; + } else { + env->exception_index = POWERPC_EXCP_DSEG; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + } + return 1; } /* 3. Check for segment level no-execute violation */ if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { - return -3; + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + return 1; } /* 4. Locate the PTE in the hash table */ pte_offset = ppc_hash64_htab_lookup(env, slb, eaddr, &pte); if (pte_offset == -1) { - return -1; + if (rwx == 2) { + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x40000000; + } else { + env->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; + } + } + return 1; } LOG_MMU("found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 5. Check access permissions */ - ctx->prot = ppc_hash64_pte_prot(env, slb, pte); + prot = ppc_hash64_pte_prot(env, slb, pte); - if ((need_prot[rwx] & ~ctx->prot) != 0) { + if ((need_prot[rwx] & ~prot) != 0) { /* Access right violation */ LOG_MMU("PTE access rejected\n"); - return -2; + if (rwx == 2) { + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + } else { + env->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (rwx == 1) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } + return 1; } LOG_MMU("PTE access granted !\n"); @@ -433,7 +468,7 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, } else { /* Treat the page as read-only for now, so that a later write * will pass through this function again to set the C bit */ - ctx->prot &= ~PAGE_WRITE; + prot &= ~PAGE_WRITE; } if (new_pte1 != pte.pte1) { @@ -442,7 +477,10 @@ static int ppc_hash64_translate(CPUPPCState *env, struct mmu_ctx_hash64 *ctx, /* 7. Determine the real address from the PTE */ - ctx->raddr = ppc_hash64_pte_raddr(slb, pte, eaddr); + raddr = ppc_hash64_pte_raddr(slb, pte, eaddr); + + tlb_set_page(env, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); return 0; } @@ -470,81 +508,3 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK; } - -int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rwx, - int mmu_idx) -{ - struct mmu_ctx_hash64 ctx; - int ret = 0; - - ret = ppc_hash64_translate(env, &ctx, address, rwx); - if (ret == 0) { - tlb_set_page(env, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { - LOG_MMU_STATE(env); - if (rwx == 2) { - switch (ret) { - case -1: - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x40000000; - break; - case -2: - /* Access rights violation */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; - break; - case -3: - /* No execute protection violation */ - env->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; - break; - case -5: - /* No match in segment table */ - env->exception_index = POWERPC_EXCP_ISEG; - env->error_code = 0; - break; - } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x42000000; - } else { - env->spr[SPR_DSISR] = 0x40000000; - } - break; - case -2: - /* Access rights violation */ - env->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } - break; - case -5: - /* No match in segment table */ - env->exception_index = POWERPC_EXCP_DSEG; - env->error_code = 0; - env->spr[SPR_DAR] = address; - break; - } - } -#if 0 - printf("%s: set exception to %d %02x\n", __func__, - env->exception, env->error_code); -#endif - ret = 1; - } - - return ret; -} From f80872e21c07edd06eb343eeeefc8af404b518a6 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:47 +0000 Subject: [PATCH 50/58] mmu-hash64: Implement Virtual Page Class Key Protection Version 2.06 of the Power architecture describes an additional page protection mechanism. Each virtual page has a "class" (0-31) recorded in the PTE. The AMR register contains bits which can prohibit reads and/or writes on a class by class basis. Interestingly, the AMR is userspace readable and writable, however user mode writes are masked by the contents of the UAMOR which is privileged. This patch implements this protection mechanism, along with the AMR and UAMOR SPRs. The architecture also specifies a hypervisor-privileged AMOR register which masks user and supervisor writes to the AMR and UAMOR. We leave this out for now, since we don't at present model hypervisor mode correctly in any case. Signed-off-by: David Gibson [agraf: fix 32-bit hosts] Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 8 ++++-- target-ppc/mmu-hash64.c | 48 +++++++++++++++++++++++++++++++----- target-ppc/mmu-hash64.h | 2 ++ target-ppc/translate_init.c | 49 +++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index e96afa61ec..eb793e03c0 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -113,11 +113,13 @@ enum powerpc_mmu_t { #if defined(TARGET_PPC64) #define POWERPC_MMU_64 0x00010000 #define POWERPC_MMU_1TSEG 0x00020000 +#define POWERPC_MMU_AMR 0x00040000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* Architecture 2.06 variant */ - POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003, - /* Architecture 2.06 "degraded" (no 1T segments) */ + POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG + | POWERPC_MMU_AMR | 0x00000003, + /* Architecture 2.06 "degraded" (no 1T segments or AMR) */ POWERPC_MMU_2_06d = POWERPC_MMU_64 | 0x00000003, #endif /* defined(TARGET_PPC64) */ }; @@ -1223,6 +1225,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_601_UDECR (0x006) #define SPR_LR (0x008) #define SPR_CTR (0x009) +#define SPR_UAMR (0x00C) #define SPR_DSCR (0x011) #define SPR_DSISR (0x012) #define SPR_DAR (0x013) /* DAE for PowerPC 601 */ @@ -1260,6 +1263,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define SPR_MPC_CMPH (0x09B) #define SPR_MPC_LCTRL1 (0x09C) #define SPR_MPC_LCTRL2 (0x09D) +#define SPR_UAMOR (0x09D) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) #define SPR_VRSAVE (0x100) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index f18c98f021..43ccf456e3 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -275,6 +275,33 @@ static int ppc_hash64_pte_prot(CPUPPCState *env, return prot; } +static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte) +{ + int key, amrbits; + int prot = PAGE_EXEC; + + + /* Only recent MMUs implement Virtual Page Class Key Protection */ + if (!(env->mmu_model & POWERPC_MMU_AMR)) { + return PAGE_READ | PAGE_WRITE | PAGE_EXEC; + } + + key = HPTE64_R_KEY(pte.pte1); + amrbits = (env->spr[SPR_AMR] >> 2*(31 - key)) & 0x3; + + /* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */ + /* env->spr[SPR_AMR]); */ + + if (amrbits & 0x2) { + prot |= PAGE_WRITE; + } + if (amrbits & 0x1) { + prot |= PAGE_READ; + } + + return prot; +} + static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte64_t *pte) @@ -375,7 +402,7 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, ppc_slb_t *slb; hwaddr pte_offset; ppc_hash_pte64_t pte; - int prot; + int pp_prot, amr_prot, prot; uint64_t new_pte1; const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; hwaddr raddr; @@ -437,7 +464,9 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, /* 5. Check access permissions */ - prot = ppc_hash64_pte_prot(env, slb, pte); + pp_prot = ppc_hash64_pte_prot(env, slb, pte); + amr_prot = ppc_hash64_amr_prot(env, pte); + prot = pp_prot & amr_prot; if ((need_prot[rwx] & ~prot) != 0) { /* Access right violation */ @@ -446,14 +475,21 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr, env->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; } else { + target_ulong dsisr = 0; + env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; + if (need_prot[rwx] & ~pp_prot) { + dsisr |= 0x08000000; } + if (rwx == 1) { + dsisr |= 0x02000000; + } + if (need_prot[rwx] & ~amr_prot) { + dsisr |= 0x00200000; + } + env->spr[SPR_DSISR] = dsisr; } return 1; } diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 37ed7ca4ef..55f5a230fd 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -69,6 +69,8 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, #define HPTE64_R_C 0x0000000000000080ULL #define HPTE64_R_R 0x0000000000000100ULL #define HPTE64_R_KEY_LO 0x0000000000000e00ULL +#define HPTE64_R_KEY(x) ((((x) & HPTE64_R_KEY_HI) >> 60) | \ + (((x) & HPTE64_R_KEY_LO) >> 9)) #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 09fb29ec97..2780f92188 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -1017,6 +1017,54 @@ static void gen_spr_7xx (CPUPPCState *env) 0x00000000); } +#ifdef TARGET_PPC64 +#ifndef CONFIG_USER_ONLY +static void spr_read_uamr (void *opaque, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], SPR_AMR); + spr_load_dump_spr(SPR_AMR); +} + +static void spr_write_uamr (void *opaque, int sprn, int gprn) +{ + gen_store_spr(SPR_AMR, cpu_gpr[gprn]); + spr_store_dump_spr(SPR_AMR); +} + +static void spr_write_uamr_pr (void *opaque, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + gen_load_spr(t0, SPR_UAMOR); + tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); + gen_store_spr(SPR_AMR, t0); + spr_store_dump_spr(SPR_AMR); +} +#endif /* CONFIG_USER_ONLY */ + +static void gen_spr_amr (CPUPPCState *env) +{ +#ifndef CONFIG_USER_ONLY + /* Virtual Page Class Key protection */ + /* The AMR is accessible either via SPR 13 or SPR 29. 13 is + * userspace accessible, 29 is privileged. So we only need to set + * the kvm ONE_REG id on one of them, we use 29 */ + spr_register(env, SPR_UAMR, "UAMR", + &spr_read_uamr, &spr_write_uamr_pr, + &spr_read_uamr, &spr_write_uamr, + 0); + spr_register_kvm(env, SPR_AMR, "AMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_AMR, 0xffffffffffffffffULL); + spr_register_kvm(env, SPR_UAMOR, "UAMOR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_UAMOR, 0); +#endif /* !CONFIG_USER_ONLY */ +} +#endif /* TARGET_PPC64 */ + static void gen_spr_thrm (CPUPPCState *env) { /* Thermal management */ @@ -6872,6 +6920,7 @@ static void init_proc_POWER7 (CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, 0x00000000); /* TOFIX */ + gen_spr_amr(env); /* XXX : not implemented */ spr_register(env, SPR_CTRL, "SPR_CTRLT", SPR_NOACCESS, SPR_NOACCESS, From cc8eae8ac7a493b6968238cf8aa5a21026858bae Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:48 +0000 Subject: [PATCH 51/58] target-ppc: Split user only code out of mmu_helper.c mmu_helper.c is, for obvious reasons, almost entirely concerned with softmmu builds of qemu. However, it does contain one stub function which is used when CONFIG_USER_ONLY=y - the user only versoin of cpu_ppc_handle_mmu_fault, which always triggers an exception. The entire rest of the file is surrounded by #if !defined(CONFIG_USER_ONLY). We clean this up by moving the user only stub into its own new file, removing the ifdefs and building mmu_helper.c only when CONFIG_SOFTMMU is set. This also lets us remove the #define of cpu_handle_mmu_fault to cpu_ppc_handle_mmu_fault - that name is only used from generic code for user only - so we just name our split user version by the generic name. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/Makefile.objs | 4 ++-- target-ppc/cpu.h | 5 +++- target-ppc/mmu_helper.c | 28 ---------------------- target-ppc/user_only_helper.c | 44 +++++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 target-ppc/user_only_helper.c diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index c80911f2c7..2c43c34a5c 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -1,14 +1,14 @@ obj-y += cpu-models.o obj-y += translate.o ifeq ($(CONFIG_SOFTMMU),y) -obj-y += machine.o mmu-hash32.o +obj-y += machine.o mmu_helper.o mmu-hash32.o obj-$(TARGET_PPC64) += mmu-hash64.o endif obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o obj-y += excp_helper.o obj-y += fpu_helper.o obj-y += int_helper.o -obj-y += mmu_helper.o obj-y += timebase_helper.o obj-y += misc_helper.o obj-y += mem_helper.o +obj-$(CONFIG_USER_ONLY) += user_only_helper.o diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index eb793e03c0..17774386c0 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1092,8 +1092,11 @@ int cpu_ppc_signal_handler (int host_signum, void *pinfo, void *puc); int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw, int mmu_idx); -#define cpu_handle_mmu_fault cpu_ppc_handle_mmu_fault void ppc_hw_interrupt (CPUPPCState *env); +#if defined(CONFIG_USER_ONLY) +int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx); +#endif #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index c780ce1f1c..525eb6fee4 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -52,31 +52,6 @@ /*****************************************************************************/ /* PowerPC MMU emulation */ -#if defined(CONFIG_USER_ONLY) -int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, - int mmu_idx) -{ - int exception, error_code; - - if (rw == 2) { - exception = POWERPC_EXCP_ISI; - error_code = 0x40000000; - } else { - exception = POWERPC_EXCP_DSI; - error_code = 0x40000000; - if (rw) { - error_code |= 0x02000000; - } - env->spr[SPR_DAR] = address; - env->spr[SPR_DSISR] = error_code; - } - env->exception_index = exception; - env->error_code = error_code; - - return 1; -} - -#else /* Context used internally during MMU translations */ typedef struct mmu_ctx_t mmu_ctx_t; @@ -2027,9 +2002,7 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) #endif } } -#endif /* !defined(CONFIG_USER_ONLY) */ -#if !defined(CONFIG_USER_ONLY) /* TLB management */ void helper_tlbia(CPUPPCState *env) { @@ -2804,4 +2777,3 @@ void helper_booke206_tlbflush(CPUPPCState *env, uint32_t type) booke206_flush_tlb(env, flags, 1); } -#endif diff --git a/target-ppc/user_only_helper.c b/target-ppc/user_only_helper.c new file mode 100644 index 0000000000..56e686efd1 --- /dev/null +++ b/target-ppc/user_only_helper.c @@ -0,0 +1,44 @@ +/* + * PowerPC MMU stub handling for user mode emulation + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright (c) 2013 David Gibson, 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 "cpu.h" + +int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, + int mmu_idx) +{ + int exception, error_code; + + if (rw == 2) { + exception = POWERPC_EXCP_ISI; + error_code = 0x40000000; + } else { + exception = POWERPC_EXCP_DSI; + error_code = 0x40000000; + if (rw) { + error_code |= 0x02000000; + } + env->spr[SPR_DAR] = address; + env->spr[SPR_DSISR] = error_code; + } + env->exception_index = exception; + env->error_code = error_code; + + return 1; +} From eb20c1c6da60c8c75f08def03b0822a48af620ac Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 12 Mar 2013 00:31:49 +0000 Subject: [PATCH 52/58] target-ppc: Move ppc tlb_fill implementation into mmu_helper.c For softmmu builds the interface from the generic code to the target specific MMU implementation is through the tlb_fill() function. For ppc this is currently in mem_helper.c, whereas it would make more sense in mmu_helper.c. This patch moves it, which also allows cpu_ppc_handle_mmu_fault() to become a local function in mmu_helper.c Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 2 -- target-ppc/mem_helper.c | 38 -------------------------------------- target-ppc/mmu_helper.c | 40 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 17774386c0..42c36e2829 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1090,8 +1090,6 @@ int cpu_ppc_exec (CPUPPCState *s); is returned if the signal was handled by the virtual CPU. */ int cpu_ppc_signal_handler (int host_signum, void *pinfo, void *puc); -int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw, - int mmu_idx); void ppc_hw_interrupt (CPUPPCState *env); #if defined(CONFIG_USER_ONLY) int cpu_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index ba383c8f11..9783e52b0c 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -252,41 +252,3 @@ STVE(stvewx, cpu_stl_data, bswap32, u32) #undef HI_IDX #undef LO_IDX - -/*****************************************************************************/ -/* Softmmu support */ -#if !defined(CONFIG_USER_ONLY) - -#define MMUSUFFIX _mmu - -#define SHIFT 0 -#include "exec/softmmu_template.h" - -#define SHIFT 1 -#include "exec/softmmu_template.h" - -#define SHIFT 2 -#include "exec/softmmu_template.h" - -#define SHIFT 3 -#include "exec/softmmu_template.h" - -/* try to fill the TLB and return an exception if error. If retaddr is - NULL, it means that the function was called in C code (i.e. not - from generated code or from helper.c) */ -/* XXX: fix it to restore all registers */ -void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx, - uintptr_t retaddr) -{ - int ret; - - ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); - if (unlikely(ret != 0)) { - if (likely(retaddr)) { - /* now we have a real cpu fault */ - cpu_restore_state(env, retaddr); - } - helper_raise_exception_err(env, env->exception_index, env->error_code); - } -} -#endif /* !CONFIG_USER_ONLY */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 525eb6fee4..4c41673383 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1384,8 +1384,8 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, } /* Perform address translation */ -int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw, - int mmu_idx) +static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, + int rw, int mmu_idx) { mmu_ctx_t ctx; int access_type; @@ -2777,3 +2777,39 @@ void helper_booke206_tlbflush(CPUPPCState *env, uint32_t type) booke206_flush_tlb(env, flags, 1); } + + +/*****************************************************************************/ + +#define MMUSUFFIX _mmu + +#define SHIFT 0 +#include "exec/softmmu_template.h" + +#define SHIFT 1 +#include "exec/softmmu_template.h" + +#define SHIFT 2 +#include "exec/softmmu_template.h" + +#define SHIFT 3 +#include "exec/softmmu_template.h" + +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx, + uintptr_t retaddr) +{ + int ret; + + ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); + if (unlikely(ret != 0)) { + if (likely(retaddr)) { + /* now we have a real cpu fault */ + cpu_restore_state(env, retaddr); + } + helper_raise_exception_err(env, env->exception_index, env->error_code); + } +} From b632a148b677b773ff155f9de840b37a653567b9 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 13 Mar 2013 11:40:33 +1100 Subject: [PATCH 53/58] target-ppc: Use QOM method dispatch for MMU fault handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After previous cleanups, the many scattered checks of env->mmu_model in the ppc MMU implementation have, at least for "classic" hash MMUs been reduced (almost) to a single switch at the top of cpu_ppc_handle_mmu_fault(). An explicit switch is still a pretty ugly way of handling this though. Now that Andreas Färber's CPU QOM cleanups for ppc have gone in, it's quite straightforward to instead make the handle_mmu_fault function a QOM method on the CPU object. This patch implements such a scheme, initializing the method pointer at the same time as the mmu_model variable. We need to keep the latter around for now, because of the MMU types (BookE, 4xx, et al) which haven't been converted to the new scheme yet, and also for a few other uses. It would be good to clean those up eventually. Signed-off-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/cpu-qom.h | 4 +++ target-ppc/mmu_helper.c | 24 +++++------------ target-ppc/translate_init.c | 54 ++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 09bfae3d54..c27cef7e32 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -68,6 +68,10 @@ typedef struct PowerPCCPUClass { #endif void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); +#if defined(CONFIG_SOFTMMU) + int (*handle_mmu_fault)(CPUPPCState *env, target_ulong eaddr, int rwx, + int mmu_idx); +#endif } PowerPCCPUClass; /** diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 4c41673383..acf01331f1 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1391,22 +1391,6 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, int access_type; int ret = 0; - switch (env->mmu_model) { -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_06d: - return ppc_hash64_handle_mmu_fault(env, address, rw, mmu_idx); -#endif - - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - return ppc_hash32_handle_mmu_fault(env, address, rw, mmu_idx); - - default: - ; /* Otherwise fall through to the general code below */ - } - if (rw == 2) { /* code access */ rw = 0; @@ -2802,9 +2786,15 @@ void helper_booke206_tlbflush(CPUPPCState *env, uint32_t type) void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx, uintptr_t retaddr) { + CPUState *cpu = ENV_GET_CPU(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); int ret; - ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); + if (pcc->handle_mmu_fault) { + ret = pcc->handle_mmu_fault(env, addr, is_write, mmu_idx); + } else { + ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); + } if (unlikely(ret != 0)) { if (likely(retaddr)) { /* now we have a real cpu fault */ diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 2780f92188..781170fb05 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -25,6 +25,8 @@ #include "sysemu/arch_init.h" #include "sysemu/cpus.h" #include "cpu-models.h" +#include "mmu-hash32.h" +#include "mmu-hash64.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -4796,6 +4798,9 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000000FD70ULL; pcc->mmu_model = POWERPC_MMU_601; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_601; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_601; @@ -4830,7 +4835,9 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000000FD70ULL; pcc->mmu_model = POWERPC_MMU_601; - pcc->excp_model = POWERPC_EXCP_601; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_601; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK; @@ -5037,6 +5044,9 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_604; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_604; @@ -5103,6 +5113,9 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_604; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_604; @@ -5156,6 +5169,9 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5217,6 +5233,9 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5401,6 +5420,9 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5466,6 +5488,9 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5536,6 +5561,9 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5606,6 +5634,9 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000005FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_7x0; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; @@ -5798,6 +5829,9 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000205FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_74xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_7400; @@ -5864,6 +5898,9 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x000000000205FF77ULL; pcc->mmu_model = POWERPC_MMU_32B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash32_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_74xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_7400; @@ -6570,6 +6607,9 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x900000000204FF36ULL; pcc->mmu_model = POWERPC_MMU_64B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; pcc->bfd_mach = bfd_mach_ppc64; @@ -6680,6 +6720,9 @@ POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x800000000204FF36ULL; pcc->mmu_model = POWERPC_MMU_64B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; pcc->bfd_mach = bfd_mach_ppc64; @@ -6778,6 +6821,9 @@ POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x800000000204FF36ULL; pcc->mmu_model = POWERPC_MMU_64B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; pcc->bfd_mach = bfd_mach_ppc64; @@ -6876,6 +6922,9 @@ POWERPC_FAMILY(970MP)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC_NONE; pcc->msr_mask = 0x900000000204FF36ULL; pcc->mmu_model = POWERPC_MMU_64B; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; pcc->bfd_mach = bfd_mach_ppc64; @@ -6968,6 +7017,9 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX; pcc->msr_mask = 0x800000000204FF36ULL; pcc->mmu_model = POWERPC_MMU_2_06; +#if defined(CONFIG_SOFTMMU) + pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; +#endif pcc->excp_model = POWERPC_EXCP_POWER7; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; pcc->bfd_mach = bfd_mach_ppc64; From 79482e5ab38a05ca8869040b0d8b8f451f16ff62 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2013 10:01:45 +0000 Subject: [PATCH 54/58] target-ppc: Fix add and subf carry generation in narrow mode The set of computations used in b5a73f8d8a57e940f9bbeb399a9e47897522ee9a are only valid if the current word size == target_long size. This failed to take ppc64 in 32-bit (narrow) mode into account. Add a NARROW_MODE macro to avoid conditional compilation. Signed-off-by: Richard Henderson Signed-off-by: Alexander Graf --- target-ppc/translate.c | 66 +++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index bb690f817a..47efcd7091 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -204,6 +204,13 @@ typedef struct DisasContext { int singlestep_enabled; } DisasContext; +/* True when active word size < size of target_long. */ +#ifdef TARGET_PPC64 +# define NARROW_MODE(C) (!(C)->sf_mode) +#else +# define NARROW_MODE(C) 0 +#endif + struct opc_handler_t { /* invalid bits for instruction 1 (Rc(opcode) == 0) */ uint32_t inval1; @@ -778,14 +785,26 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, } if (compute_ca) { - TCGv zero = tcg_const_tl(0); - if (add_ca) { - tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero); - tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero); + if (NARROW_MODE(ctx)) { + TCGv t1 = tcg_temp_new(); + tcg_gen_ext32u_tl(t1, arg2); + tcg_gen_ext32u_tl(t0, arg1); + tcg_gen_add_tl(t0, t0, t1); + tcg_temp_free(t1); + if (add_ca) { + tcg_gen_add_tl(t0, t0, cpu_ca); + } + tcg_gen_shri_tl(cpu_ca, t0, 32); } else { - tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero); + TCGv zero = tcg_const_tl(0); + if (add_ca) { + tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero); + tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero); + } else { + tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero); + } + tcg_temp_free(zero); } - tcg_temp_free(zero); } else { tcg_gen_add_tl(t0, arg1, arg2); if (add_ca) { @@ -1114,14 +1133,25 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, { TCGv t0 = ret; - if (((add_ca && compute_ca) || compute_ov) - && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) { + if (compute_ov && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) { t0 = tcg_temp_new(); } - if (add_ca) { - /* dest = ~arg1 + arg2 + ca. */ - if (compute_ca) { + if (compute_ca) { + /* dest = ~arg1 + arg2 [+ ca]. */ + if (NARROW_MODE(ctx)) { + TCGv inv1 = tcg_temp_new(); + tcg_gen_not_tl(inv1, arg1); + tcg_gen_ext32u_tl(t0, arg2); + tcg_gen_ext32u_tl(inv1, inv1); + if (add_ca) { + tcg_gen_add_tl(t0, t0, cpu_ca); + } else { + tcg_gen_addi_tl(t0, t0, 1); + } + tcg_gen_add_tl(t0, t0, inv1); + tcg_gen_shri_tl(cpu_ca, t0, 32); + } else if (add_ca) { TCGv zero, inv1 = tcg_temp_new(); tcg_gen_not_tl(inv1, arg1); zero = tcg_const_tl(0); @@ -1130,14 +1160,16 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_temp_free(zero); tcg_temp_free(inv1); } else { - tcg_gen_sub_tl(t0, arg2, arg1); - tcg_gen_add_tl(t0, t0, cpu_ca); - tcg_gen_subi_tl(t0, t0, 1); - } - } else { - if (compute_ca) { tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); + tcg_gen_sub_tl(t0, arg2, arg1); } + } else if (add_ca) { + /* Since we're ignoring carry-out, we can simplify the + standard ~arg1 + arg2 + ca to arg2 - arg1 + ca - 1. */ + tcg_gen_sub_tl(t0, arg2, arg1); + tcg_gen_add_tl(t0, t0, cpu_ca); + tcg_gen_subi_tl(t0, t0, 1); + } else { tcg_gen_sub_tl(t0, arg2, arg1); } From e0c8f9ce85b360668a7c8cc77cb3b85fac0bd057 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2013 10:01:46 +0000 Subject: [PATCH 55/58] target-ppc: Use NARROW_MODE macro for branches Removing conditional compilation in the process. Signed-off-by: Richard Henderson Signed-off-by: Alexander Graf --- target-ppc/translate.c | 62 ++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 47efcd7091..63d4dbc571 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -267,12 +267,10 @@ static inline void gen_set_access_type(DisasContext *ctx, int access_type) static inline void gen_update_nip(DisasContext *ctx, target_ulong nip) { -#if defined(TARGET_PPC64) - if (ctx->sf_mode) - tcg_gen_movi_tl(cpu_nip, nip); - else -#endif - tcg_gen_movi_tl(cpu_nip, (uint32_t)nip); + if (NARROW_MODE(ctx)) { + nip = (uint32_t)nip; + } + tcg_gen_movi_tl(cpu_nip, nip); } static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) @@ -3352,10 +3350,9 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { TranslationBlock *tb; tb = ctx->tb; -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) + if (NARROW_MODE(ctx)) { dest = (uint32_t) dest; -#endif + } if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) && likely(!ctx->singlestep_enabled)) { tcg_gen_goto_tb(n); @@ -3383,12 +3380,10 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) static inline void gen_setlr(DisasContext *ctx, target_ulong nip) { -#if defined(TARGET_PPC64) - if (ctx->sf_mode == 0) - tcg_gen_movi_tl(cpu_lr, (uint32_t)nip); - else -#endif - tcg_gen_movi_tl(cpu_lr, nip); + if (NARROW_MODE(ctx)) { + nip = (uint32_t)nip; + } + tcg_gen_movi_tl(cpu_lr, nip); } /* b ba bl bla */ @@ -3398,18 +3393,16 @@ static void gen_b(DisasContext *ctx) ctx->exception = POWERPC_EXCP_BRANCH; /* sign extend LI */ -#if defined(TARGET_PPC64) - if (ctx->sf_mode) - li = ((int64_t)LI(ctx->opcode) << 38) >> 38; - else -#endif - li = ((int32_t)LI(ctx->opcode) << 6) >> 6; - if (likely(AA(ctx->opcode) == 0)) + li = LI(ctx->opcode); + li = (li ^ 0x02000000) - 0x02000000; + if (likely(AA(ctx->opcode) == 0)) { target = ctx->nip + li - 4; - else + } else { target = li; - if (LK(ctx->opcode)) + } + if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->nip); + } gen_update_cfar(ctx, ctx->nip); gen_goto_tb(ctx, 0, target); } @@ -3445,12 +3438,11 @@ static inline void gen_bcond(DisasContext *ctx, int type) return; } tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(temp, cpu_ctr); - else -#endif + } else { tcg_gen_mov_tl(temp, cpu_ctr); + } if (bo & 0x2) { tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1); } else { @@ -3484,20 +3476,14 @@ static inline void gen_bcond(DisasContext *ctx, int type) gen_set_label(l1); gen_goto_tb(ctx, 1, ctx->nip); } else { -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode)) + if (NARROW_MODE(ctx)) { tcg_gen_andi_tl(cpu_nip, target, (uint32_t)~3); - else -#endif + } else { tcg_gen_andi_tl(cpu_nip, target, ~3); + } tcg_gen_exit_tb(0); gen_set_label(l1); -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode)) - tcg_gen_movi_tl(cpu_nip, (uint32_t)ctx->nip); - else -#endif - tcg_gen_movi_tl(cpu_nip, ctx->nip); + gen_update_nip(ctx, ctx->nip); tcg_gen_exit_tb(0); } } From 02765534f7aff83a975072e8a6fcc85364351ad5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2013 10:01:47 +0000 Subject: [PATCH 56/58] target-ppc: Use NARROW_MODE macro for comparisons Removing conditional compilation in the process. Signed-off-by: Richard Henderson Signed-off-by: Alexander Graf --- target-ppc/translate.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 63d4dbc571..bd68ffc7bf 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -632,7 +632,6 @@ static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf) tcg_temp_free(t0); } -#if defined(TARGET_PPC64) static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf) { TCGv t0, t1; @@ -656,68 +655,62 @@ static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf) gen_op_cmp32(arg0, t0, s, crf); tcg_temp_free(t0); } -#endif static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) { -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode)) + if (NARROW_MODE(ctx)) { gen_op_cmpi32(reg, 0, 1, 0); - else -#endif + } else { gen_op_cmpi(reg, 0, 1, 0); + } } /* cmp */ static void gen_cmp(DisasContext *ctx) { -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode && (ctx->opcode & 0x00200000))) + if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) { gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], 1, crfD(ctx->opcode)); - else -#endif + } else { gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], 1, crfD(ctx->opcode)); + } } /* cmpi */ static void gen_cmpi(DisasContext *ctx) { -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode && (ctx->opcode & 0x00200000))) + if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) { gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode), 1, crfD(ctx->opcode)); - else -#endif + } else { gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode), 1, crfD(ctx->opcode)); + } } /* cmpl */ static void gen_cmpl(DisasContext *ctx) { -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode && (ctx->opcode & 0x00200000))) + if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) { gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], 0, crfD(ctx->opcode)); - else -#endif + } else { gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], 0, crfD(ctx->opcode)); + } } /* cmpli */ static void gen_cmpli(DisasContext *ctx) { -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode && (ctx->opcode & 0x00200000))) + if (NARROW_MODE(ctx) || !(ctx->opcode & 0x00200000)) { gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode), 0, crfD(ctx->opcode)); - else -#endif + } else { gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode), 0, crfD(ctx->opcode)); + } } /* isel (PowerPC 2.03 specification) */ @@ -761,11 +754,9 @@ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, tcg_gen_andc_tl(cpu_ov, cpu_ov, t0); } tcg_temp_free(t0); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32s_tl(cpu_ov, cpu_ov); } -#endif tcg_gen_shri_tl(cpu_ov, cpu_ov, TARGET_LONG_BITS - 1); tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } From c791fe8436cf6b93ddb035a5b7a87487da5f7b30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2013 10:01:48 +0000 Subject: [PATCH 57/58] target-ppc: Use NARROW_MODE macro for addresses Removing conditional compilation in the process. Signed-off-by: Richard Henderson Signed-off-by: Alexander Graf --- target-ppc/translate.c | 51 +++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index bd68ffc7bf..3b47553878 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -2332,45 +2332,37 @@ static inline void gen_addr_imm_index(DisasContext *ctx, TCGv EA, simm &= ~maskl; if (rA(ctx->opcode) == 0) { -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { - tcg_gen_movi_tl(EA, (uint32_t)simm); - } else -#endif + if (NARROW_MODE(ctx)) { + simm = (uint32_t)simm; + } tcg_gen_movi_tl(EA, simm); } else if (likely(simm != 0)) { tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], simm); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(EA, EA); } -#endif } else { -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]); - } else -#endif - tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]); + } } } static inline void gen_addr_reg_index(DisasContext *ctx, TCGv EA) { if (rA(ctx->opcode) == 0) { -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(EA, cpu_gpr[rB(ctx->opcode)]); - } else -#endif - tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]); + } } else { tcg_gen_add_tl(EA, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(EA, EA); } -#endif } } @@ -2378,13 +2370,10 @@ static inline void gen_addr_register(DisasContext *ctx, TCGv EA) { if (rA(ctx->opcode) == 0) { tcg_gen_movi_tl(EA, 0); + } else if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]); } else { -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { - tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]); - } else -#endif - tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]); } } @@ -2392,11 +2381,9 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1, target_long val) { tcg_gen_addi_tl(ret, arg1, val); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(ret, ret); } -#endif } static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask) @@ -7586,11 +7573,9 @@ static inline void gen_addr_spe_imm_index(DisasContext *ctx, TCGv EA, int sh) tcg_gen_movi_tl(EA, uimm << sh); } else { tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], uimm << sh); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { tcg_gen_ext32u_tl(EA, EA); } -#endif } } From 9ca3f7f3160365de9030e1a6128a871625abe346 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2013 10:01:49 +0000 Subject: [PATCH 58/58] target-ppc: Use NARROW_MODE macro for tlbie Removing conditional compilation in the process. Signed-off-by: Richard Henderson Signed-off-by: Alexander Graf --- target-ppc/translate.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 3b47553878..5e741d1ab4 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4320,15 +4320,14 @@ static void gen_tlbie(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) { + if (NARROW_MODE(ctx)) { TCGv t0 = tcg_temp_new(); tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]); gen_helper_tlbie(cpu_env, t0); tcg_temp_free(t0); - } else -#endif + } else { gen_helper_tlbie(cpu_env, cpu_gpr[rB(ctx->opcode)]); + } #endif }