diff options
-rw-r--r-- | gdbstub.c | 3 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 16 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 102 | ||||
-rw-r--r-- | hw/ppc/xics.c | 57 | ||||
-rw-r--r-- | hw/spapr_pci.c | 30 | ||||
-rw-r--r-- | hw/spapr_pci.h | 4 | ||||
-rw-r--r-- | hw/xics.h | 3 | ||||
-rw-r--r-- | monitor.c | 4 | ||||
-rw-r--r-- | target-ppc/Makefile.objs | 7 | ||||
-rw-r--r-- | target-ppc/cpu-models.c | 2 | ||||
-rw-r--r-- | target-ppc/cpu-qom.h | 4 | ||||
-rw-r--r-- | target-ppc/cpu.h | 91 | ||||
-rw-r--r-- | target-ppc/fpu_helper.c | 5 | ||||
-rw-r--r-- | target-ppc/helper.h | 1 | ||||
-rw-r--r-- | target-ppc/kvm.c | 3 | ||||
-rw-r--r-- | target-ppc/machine.c | 4 | ||||
-rw-r--r-- | target-ppc/mem_helper.c | 38 | ||||
-rw-r--r-- | target-ppc/misc_helper.c | 6 | ||||
-rw-r--r-- | target-ppc/mmu-hash32.c | 560 | ||||
-rw-r--r-- | target-ppc/mmu-hash32.h | 102 | ||||
-rw-r--r-- | target-ppc/mmu-hash64.c | 546 | ||||
-rw-r--r-- | target-ppc/mmu-hash64.h | 124 | ||||
-rw-r--r-- | target-ppc/mmu_helper.c | 835 | ||||
-rw-r--r-- | target-ppc/translate.c | 226 | ||||
-rw-r--r-- | target-ppc/translate_init.c | 360 | ||||
-rw-r--r-- | target-ppc/user_only_helper.c | 44 |
26 files changed, 1873 insertions, 1304 deletions
@@ -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/hw/ppc/spapr.c b/hw/ppc/spapr.c index f355a9b..7b2a11f 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; @@ -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); @@ -856,7 +858,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/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index dd72743..22cfb7e 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) { @@ -84,10 +52,10 @@ 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 & 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; } } @@ -129,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) & HPTE_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) & HPTE_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; @@ -166,26 +134,26 @@ 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 & 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; } *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; @@ -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: @@ -292,34 +260,34 @@ 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 & 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); + 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/hw/ppc/xics.c b/hw/ppc/xics.c index c3ef12f..374da5b 100644 --- a/hw/ppc/xics.c +++ b/hw/ppc/xics.c @@ -521,45 +521,38 @@ 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) { - CPUPPCState *env; - CPUState *cpu; - int max_server_num; - struct icp_state *icp; - struct ics_state *ics; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + struct icp_server_state *ss = &icp->ss[cs->cpu_index]; - 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; - } - } + assert(cs->cpu_index < icp->nr_servers); - icp = g_malloc0(sizeof(*icp)); - icp->nr_servers = max_server_num + 1; - icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; - 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]; + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; + default: + fprintf(stderr, "XICS interrupt controller does not support this CPU " + "bus model\n"); + abort(); + } +} - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; +struct icp_state *xics_system_init(int nr_servers, int nr_irqs) +{ + struct icp_state *icp; + struct ics_state *ics; - default: - hw_error("XICS interrupt model does not support this CPU bus " - "model\n"); - exit(1); - } - } + icp = g_malloc0(sizeof(*icp)); + icp->nr_servers = nr_servers; + icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); ics = g_malloc0(sizeof(*ics)); ics->nr_irqs = nr_irqs; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 36adbc5..42c8b61 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 8bb3c62..8bd8a66 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, @@ -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__ */ @@ -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/Makefile.objs b/target-ppc/Makefile.objs index 00ac4ad..2c43c34 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -1,11 +1,14 @@ obj-y += cpu-models.o obj-y += translate.o -obj-$(CONFIG_SOFTMMU) += machine.o +ifeq ($(CONFIG_SOFTMMU),y) +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-models.c b/target-ppc/cpu-models.c index 20ca84e..17f56b7 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-qom.h b/target-ppc/cpu-qom.h index 09bfae3..c27cef7 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/cpu.h b/target-ppc/cpu.h index 6886666..42c36e2 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -113,13 +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, - /* 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) */ + 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) */ }; @@ -396,36 +396,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)) @@ -965,8 +941,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; @@ -1105,20 +1079,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" /*****************************************************************************/ @@ -1130,17 +1090,14 @@ 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); -#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); -#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) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); @@ -1172,14 +1129,13 @@ 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 #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; @@ -1270,6 +1226,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 */ @@ -1307,6 +1264,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) @@ -1489,11 +1447,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) @@ -1509,7 +1465,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) @@ -1524,9 +1479,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) @@ -1534,7 +1487,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) @@ -1606,49 +1558,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) @@ -1716,15 +1652,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/fpu_helper.c b/target-ppc/fpu_helper.c index 9d67926..9e779ea 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/helper.h b/target-ppc/helper.h index fcf372a..d33ee66 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/kvm.c b/target-ppc/kvm.c index e663ff0..597066f 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" @@ -1077,7 +1078,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, diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 708a840..235b0d5 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/mem_helper.c b/target-ppc/mem_helper.c index ba383c8..9783e52 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/misc_helper.c b/target-ppc/misc_helper.c index 26edcca..616aab6 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-hash32.c b/target-ppc/mmu-hash32.c new file mode 100644 index 0000000..f6adf22 --- /dev/null +++ b/target-ppc/mmu-hash32.c @@ -0,0 +1,560 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "helper.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "mmu-hash32.h" + +//#define DEBUG_MMU +//#define DEBUG_BAT + +#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_BATS +# define LOG_BATS(...) qemu_log(__VA_ARGS__) +#else +# define LOG_BATS(...) do { } while (0) +#endif + +struct mmu_ctx_hash32 { + hwaddr raddr; /* Real address */ + int prot; /* Protection bits */ + int key; /* Access key */ +}; + +static int ppc_hash32_pp_prot(int key, int pp, int nx) +{ + int prot; + + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + case 0x3: + prot = PAGE_READ; + break; + + default: + abort(); + } + } else { + switch (pp) { + case 0x0: + prot = 0; + break; + + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + default: + abort(); + } + } + if (nx == 0) { + prot |= PAGE_EXEC; + } + + return prot; +} + +static int ppc_hash32_pte_prot(CPUPPCState *env, + target_ulong sr, ppc_hash_pte32_t pte) +{ + unsigned pp, key; + + key = !!(msr_pr ? (sr & SR32_KP) : (sr & SR32_KS)); + pp = pte.pte1 & HPTE32_R_PP; + + return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); +} + +static target_ulong hash32_bat_size(CPUPPCState *env, + target_ulong batu, target_ulong batl) +{ + if ((msr_pr && !(batu & BATU32_VP)) + || (!msr_pr && !(batu & BATU32_VS))) { + return 0; + } + + return BATU32_BEPI & ~((batu & BATU32_BL) << 15); +} + +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 target_ulong hash32_bat_601_size(CPUPPCState *env, + target_ulong batu, target_ulong batl) +{ + if (!(batl & BATL32_601_V)) { + return 0; + } + + return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); +} + +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_prot(key, pp, 0); +} + +static hwaddr ppc_hash32_bat_lookup(CPUPPCState *env, target_ulong ea, int rwx, + int *prot) +{ + target_ulong *BATlt, *BATut; + int i; + + LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + rwx == 2 ? 'I' : 'D', ea); + if (rwx == 2) { + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + } else { + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + } + for (i = 0; i < env->nb_BATs; i++) { + target_ulong batu = BATut[i]; + target_ulong batl = BATlt[i]; + target_ulong mask; + + if (unlikely(env->mmu_model == POWERPC_MMU_601)) { + mask = hash32_bat_601_size(env, batu, batl); + } else { + mask = hash32_bat_size(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, ea, batu, batl); + + 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); + } + + return raddr & TARGET_PAGE_MASK; + } + } + + /* No hit */ +#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, + 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 */ + env->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + return 1; + } + + switch (env->access_type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_FLOAT: + /* Floating point load/store */ + 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. */ + 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. + * As it already do no-op, it's quite easy :-) + */ + *raddr = eaddr; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + 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"); + abort(); + } + if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { + *raddr = eaddr; + return 0; + } 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; + } +} + +hwaddr get_pteg_offset32(CPUPPCState *env, hwaddr hash) +{ + return (hash * HASH_PTEG_SIZE_32) & env->htab_mask; +} + +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 hwaddr ppc_hash32_htab_lookup(CPUPPCState *env, + target_ulong sr, target_ulong eaddr, + ppc_hash_pte32_t *pte) +{ + hwaddr pteg_off, pte_offset; + hwaddr hash; + uint32_t vsid, pgidx, ptem; + + vsid = sr & SR32_VSID; + 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); + } + + return pte_offset; +} + +static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, + target_ulong eaddr) +{ + hwaddr rpn = pte.pte1 & HPTE32_R_RPN; + hwaddr mask = ~TARGET_PAGE_MASK; + + return (rpn & ~mask) | (eaddr & mask); +} + +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 */ + 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) { + 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; + } + } + + /* 3. Look up the Segment Register */ + sr = env->sr[eaddr >> 28]; + + /* 4. Handle direct store segments */ + if (sr & SR32_T) { + 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)) { + 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) { + 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 */ + + prot = ppc_hash32_pte_prot(env, sr, pte); + + if (need_prot[rwx] & ~prot) { + /* Access right violation */ + LOG_MMU("PTE access rejected\n"); + 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"); + + /* 8. Update PTE referenced and changed bits if necessary */ + + 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 */ + prot &= ~PAGE_WRITE; + } + + if (new_pte1 != pte.pte1) { + ppc_hash32_store_hpte1(env, pte_offset, new_pte1); + } + + /* 9. Determine the real address from the PTE */ + + 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; +} + +hwaddr ppc_hash32_get_phys_page_debug(CPUPPCState *env, target_ulong eaddr) +{ + target_ulong sr; + hwaddr pte_offset; + ppc_hash_pte32_t pte; + int prot; + + 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; + } + + 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; +} diff --git a/target-ppc/mmu-hash32.h b/target-ppc/mmu-hash32.h new file mode 100644 index 0000000..884786b --- /dev/null +++ b/target-ppc/mmu-hash32.h @@ -0,0 +1,102 @@ +#if !defined (__MMU_HASH32_H__) +#define __MMU_HASH32_H__ + +#ifndef CONFIG_USER_ONLY + +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); + +/* + * 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_BEPI 0xfffe0000 +#define BATU32_BL 0x00001ffc +#define BATU32_VS 0x00000002 +#define BATU32_VP 0x00000001 + + +#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 + +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); +} + +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 new file mode 100644 index 0000000..43ccf45 --- /dev/null +++ b/target-ppc/mmu-hash64.c @@ -0,0 +1,546 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ +#include "cpu.h" +#include "helper.h" +#include "sysemu/kvm.h" +#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 +# define LOG_SLB(...) do { } while (0) +#endif + +/* + * SLB handling + */ + +static 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; +} + +/* + * 64-bit hash table MMU handling + */ + +static int ppc_hash64_pte_prot(CPUPPCState *env, + ppc_slb_t *slb, ppc_hash_pte64_t pte) +{ + 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); + + if (key == 0) { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + + case 0x3: + case 0x6: + prot = PAGE_READ; + break; + } + } else { + switch (pp) { + case 0x0: + case 0x6: + prot = 0; + break; + + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + } + } + + /* No execute if either noexec or guarded bits set */ + if (!(pte.pte1 & HPTE64_R_N) || (pte.pte1 & HPTE64_R_G) + || (slb->vsid & SLB_VSID_N)) { + prot |= PAGE_EXEC; + } + + 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) +{ + 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 hwaddr ppc_hash64_htab_lookup(CPUPPCState *env, + ppc_slb_t *slb, target_ulong eaddr, + ppc_hash_pte64_t *pte) +{ + hwaddr pteg_off, pte_offset; + hwaddr hash; + uint64_t vsid, epnshift, epnmask, epn, ptem; + + /* 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); + + if (slb->vsid & SLB_VSID_B) { + /* 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 { + /* 256M segment */ + vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; + epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask; + hash = vsid ^ (epn >> epnshift); + } + 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 + " 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); + } + + 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 & 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; + hwaddr mask = (1ULL << target_page_bits) - 1; + + return (rpn & ~mask) | (eaddr & mask); +} + +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 pp_prot, amr_prot, prot; + uint64_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 */ + /* In real mode the top 4 effective address bits are ignored */ + 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; + } + + /* 2. Translation is on, so look up the SLB */ + slb = slb_lookup(env, eaddr); + + if (!slb) { + 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)) { + 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) { + 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 */ + + 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 */ + LOG_MMU("PTE access rejected\n"); + if (rwx == 2) { + 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 (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; + } + + LOG_MMU("PTE access granted !\n"); + + /* 6. Update PTE referenced and changed bits if necessary */ + + 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 */ + prot &= ~PAGE_WRITE; + } + + if (new_pte1 != pte.pte1) { + ppc_hash64_store_hpte1(env, pte_offset, new_pte1); + } + + /* 7. Determine the real address from the PTE */ + + 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; +} + +hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr) +{ + ppc_slb_t *slb; + hwaddr pte_offset; + ppc_hash_pte64_t pte; + + 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; + } + + 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; +} diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h new file mode 100644 index 0000000..55f5a23 --- /dev/null +++ b/target-ppc/mmu-hash64.h @@ -0,0 +1,124 @@ +#if !defined (__MMU_HASH64_H__) +#define __MMU_HASH64_H__ + +#ifndef CONFIG_USER_ONLY + +#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); +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 + +/* + * 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_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 + +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); + } +} + +typedef struct { + uint64_t pte0, pte1; +} ppc_hash_pte64_t; + +#endif /* CONFIG_USER_ONLY */ + +#endif /* !defined (__MMU_HASH64_H__) */ diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 1cc1c16..acf0133 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -20,10 +20,11 @@ #include "helper.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" +#include "mmu-hash64.h" +#include "mmu-hash32.h" //#define DEBUG_MMU //#define DEBUG_BATS -//#define DEBUG_SLB //#define DEBUG_SOFTWARE_TLB //#define DUMP_PAGE_TABLES //#define DEBUG_SOFTWARE_TLB @@ -49,39 +50,21 @@ # 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) -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; -} +/* 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 */ +}; -#else /* Common routines used by software and hardware TLBs emulation */ static inline int pte_is_valid(target_ulong pte0) { @@ -93,31 +76,14 @@ 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; -} - -static inline void pte64_invalidate(target_ulong *pte0) -{ - *pte0 &= ~0x0000000000000001ULL; -} -#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) +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) { @@ -127,14 +93,12 @@ static inline 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: @@ -153,7 +117,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) +static int check_prot(int prot, int rw, int access_type) { int ret; @@ -180,40 +144,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 */ @@ -241,22 +186,8 @@ 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) +static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, + int ret, int rw) { int store = 0; @@ -392,7 +323,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; @@ -454,34 +385,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; -} - -static inline 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; @@ -505,11 +410,7 @@ static inline 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); @@ -556,332 +457,35 @@ 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) -{ - 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 = pte32_check(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); - } -#endif - - 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) +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; - } - - 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); + 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); - 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; @@ -897,33 +501,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 */ - 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]); - } - 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; @@ -1309,8 +888,8 @@ 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, +static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddrp, target_ulong address, uint32_t pid) { target_ulong mask; @@ -1597,28 +1176,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) { @@ -1632,7 +1189,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: @@ -1649,8 +1206,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: @@ -1658,16 +1213,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_620: - 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, @@ -1692,15 +1238,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; } @@ -1710,71 +1251,62 @@ 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_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: + 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: - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: + } 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 defined(TARGET_PPC64) - case POWERPC_MMU_620: - 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_segment_6xx_tlb(env, ctx, eaddr, rw, access_type); } - break; - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_SOFT_4xx_Z: + } + break; + + 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, + } + 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: + 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; } + 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", @@ -1788,6 +1320,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; } @@ -1836,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; @@ -1880,17 +1428,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_620: - 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 */ @@ -1932,19 +1469,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 */ - 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; - } - break; -#endif } } else { switch (ret) { @@ -1964,9 +1488,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) { @@ -1992,23 +1516,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_620: - 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"); @@ -2094,26 +1601,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 */ - 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; - } - break; -#endif } } #if 0 @@ -2326,7 +1813,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 +1882,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 +1905,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); @@ -2511,41 +1986,6 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) #endif } } -#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) @@ -3321,4 +2761,45 @@ void helper_booke206_tlbflush(CPUPPCState *env, uint32_t type) booke206_flush_tlb(env, flags, 1); } -#endif + + +/*****************************************************************************/ + +#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) +{ + CPUState *cpu = ENV_GET_CPU(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + int ret; + + 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 */ + cpu_restore_state(env, retaddr); + } + helper_raise_exception_err(env, env->exception_index, env->error_code); + } +} diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 380a884..5e741d1 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; @@ -260,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) @@ -627,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; @@ -651,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) */ @@ -756,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); } @@ -778,14 +774,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 +1122,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 +1149,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_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); tcg_gen_sub_tl(t0, arg2, arg1); - tcg_gen_add_tl(t0, t0, cpu_ca); - tcg_gen_subi_tl(t0, t0, 1); } + } 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 { - if (compute_ca) { - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); - } tcg_gen_sub_tl(t0, arg2, arg1); } @@ -2311,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 } } @@ -2357,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)]); } } @@ -2371,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) @@ -3320,10 +3328,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); @@ -3351,12 +3358,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 */ @@ -3366,18 +3371,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); } @@ -3413,12 +3416,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 { @@ -3452,20 +3454,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); } } @@ -4324,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 } @@ -7577,11 +7572,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 } } @@ -9428,7 +9421,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 15eebe9..781170f 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 @@ -365,7 +367,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 +380,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 @@ -1028,6 +1019,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 */ @@ -2151,173 +2190,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 +2865,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) @@ -4951,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; @@ -4985,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; @@ -5192,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; @@ -5258,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; @@ -5311,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; @@ -5372,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; @@ -5556,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; @@ -5621,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; @@ -5691,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; @@ -5761,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; @@ -5953,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; @@ -6019,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; @@ -6725,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; @@ -6835,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; @@ -6933,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; @@ -7031,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; @@ -7075,6 +6969,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, @@ -7122,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; @@ -7129,55 +7027,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) */ @@ -7693,7 +7542,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; @@ -7915,9 +7764,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"; diff --git a/target-ppc/user_only_helper.c b/target-ppc/user_only_helper.c new file mode 100644 index 0000000..56e686e --- /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 <http://www.gnu.org/licenses/>. + */ + +#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; +} |