* 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.
+ * version 2.1 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
#include "qemu/units.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "exec/helper-proto.h"
#include "qemu/error-report.h"
#include "qemu/qemu-print.h"
#include "sysemu/hw_accel.h"
#include "mmu-hash64.h"
#include "exec/log.h"
#include "hw/hw.h"
+#include "internal.h"
#include "mmu-book3s-v3.h"
+#include "helper_regs.h"
+
+#ifdef CONFIG_TCG
+#include "exec/helper-proto.h"
+#endif
/* #define DEBUG_SLB */
}
}
-void helper_slbia(CPUPPCState *env, uint32_t ih)
+#ifdef CONFIG_TCG
+void helper_SLBIA(CPUPPCState *env, uint32_t ih)
{
PowerPCCPU *cpu = env_archcpu(env);
int starting_entry;
}
}
+#if defined(TARGET_PPC64)
+void helper_SLBIAG(CPUPPCState *env, target_ulong rs, uint32_t l)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ int n;
+
+ /*
+ * slbiag must always flush all TLB (which is equivalent to ERAT in ppc
+ * architecture). Matching on SLB_ESID_V is not good enough, because slbmte
+ * can overwrite a valid SLB without flushing its lookaside information.
+ *
+ * It would be possible to keep the TLB in synch with the SLB by flushing
+ * when a valid entry is overwritten by slbmte, and therefore slbiag would
+ * not have to flush unless it evicts a valid SLB entry. However it is
+ * expected that slbmte is more common than slbiag, and slbiag is usually
+ * going to evict valid SLB entries, so that tradeoff is unlikely to be a
+ * good one.
+ */
+ env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
+
+ for (n = 0; n < cpu->hash64_opts->slb_size; n++) {
+ ppc_slb_t *slb = &env->slb[n];
+ slb->esid &= ~SLB_ESID_V;
+ }
+}
+#endif
+
static void __helper_slbie(CPUPPCState *env, target_ulong addr,
target_ulong global)
{
}
}
-void helper_slbie(CPUPPCState *env, target_ulong addr)
+void helper_SLBIE(CPUPPCState *env, target_ulong addr)
{
__helper_slbie(env, addr, false);
}
-void helper_slbieg(CPUPPCState *env, target_ulong addr)
+void helper_SLBIEG(CPUPPCState *env, target_ulong addr)
{
__helper_slbie(env, addr, true);
}
+#endif
int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot,
target_ulong esid, target_ulong vsid)
return 0;
}
+#ifdef CONFIG_TCG
static int ppc_load_slb_esid(PowerPCCPU *cpu, target_ulong rb,
target_ulong *rt)
{
return 0;
}
-void helper_store_slb(CPUPPCState *env, target_ulong rb, target_ulong rs)
+void helper_SLBMTE(CPUPPCState *env, target_ulong rb, target_ulong rs)
{
PowerPCCPU *cpu = env_archcpu(env);
}
}
-target_ulong helper_load_slb_esid(CPUPPCState *env, target_ulong rb)
+target_ulong helper_SLBMFEE(CPUPPCState *env, target_ulong rb)
{
PowerPCCPU *cpu = env_archcpu(env);
target_ulong rt = 0;
return rt;
}
-target_ulong helper_find_slb_vsid(CPUPPCState *env, target_ulong rb)
+target_ulong helper_SLBFEE(CPUPPCState *env, target_ulong rb)
{
PowerPCCPU *cpu = env_archcpu(env);
target_ulong rt = 0;
return rt;
}
-target_ulong helper_load_slb_vsid(CPUPPCState *env, target_ulong rb)
+target_ulong helper_SLBMFEV(CPUPPCState *env, target_ulong rb)
{
PowerPCCPU *cpu = env_archcpu(env);
target_ulong rt = 0;
}
return rt;
}
+#endif
/* Check No-Execute or Guarded Storage */
static inline int ppc_hash64_pte_noexec_guard(PowerPCCPU *cpu,
}
/* Check Basic Storage Protection */
-static int ppc_hash64_pte_prot(PowerPCCPU *cpu,
+static int ppc_hash64_pte_prot(int mmu_idx,
ppc_slb_t *slb, ppc_hash_pte64_t pte)
{
- CPUPPCState *env = &cpu->env;
unsigned pp, key;
/*
* Some pp bit combinations have undefined behaviour, so default
*/
int prot = 0;
- key = !!(msr_pr ? (slb->vsid & SLB_VSID_KP)
+ key = !!(mmuidx_pr(mmu_idx) ? (slb->vsid & SLB_VSID_KP)
: (slb->vsid & SLB_VSID_KS));
pp = (pte.pte1 & HPTE64_R_PP) | ((pte.pte1 & HPTE64_R_PP0) >> 61);
/* Page address translation */
qemu_log_mask(CPU_LOG_MMU,
- "htab_base " TARGET_FMT_plx " htab_mask " TARGET_FMT_plx
- " hash " TARGET_FMT_plx "\n",
+ "htab_base " HWADDR_FMT_plx " htab_mask " HWADDR_FMT_plx
+ " hash " HWADDR_FMT_plx "\n",
ppc_hash64_hpt_base(cpu), ppc_hash64_hpt_mask(cpu), hash);
/* Primary PTEG lookup */
qemu_log_mask(CPU_LOG_MMU,
- "0 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+ "0 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx
" vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
- " hash=" TARGET_FMT_plx "\n",
+ " hash=" HWADDR_FMT_plx "\n",
ppc_hash64_hpt_base(cpu), ppc_hash64_hpt_mask(cpu),
vsid, ptem, hash);
ptex = ppc_hash64_pteg_search(cpu, hash, sps, ptem, pte, pshift);
/* Secondary PTEG lookup */
ptem |= HPTE64_V_SECONDARY;
qemu_log_mask(CPU_LOG_MMU,
- "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
+ "1 htab=" HWADDR_FMT_plx "/" HWADDR_FMT_plx
" vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
- " hash=" TARGET_FMT_plx "\n", ppc_hash64_hpt_base(cpu),
+ " hash=" HWADDR_FMT_plx "\n", ppc_hash64_hpt_base(cpu),
ppc_hash64_hpt_mask(cpu), vsid, ptem, ~hash);
ptex = ppc_hash64_pteg_search(cpu, ~hash, sps, ptem, pte, pshift);
}
}
-static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code)
+static void ppc_hash64_set_isi(CPUState *cs, int mmu_idx, uint64_t error_code)
{
CPUPPCState *env = &POWERPC_CPU(cs)->env;
bool vpm;
- if (msr_ir) {
+ if (!mmuidx_real(mmu_idx)) {
vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
} else {
vpm = ppc_hash64_use_vrma(env);
}
- if (vpm && !msr_hv) {
+ if (vpm && !mmuidx_hv(mmu_idx)) {
cs->exception_index = POWERPC_EXCP_HISI;
} else {
cs->exception_index = POWERPC_EXCP_ISI;
env->error_code = error_code;
}
-static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr)
+static void ppc_hash64_set_dsi(CPUState *cs, int mmu_idx, uint64_t dar, uint64_t dsisr)
{
CPUPPCState *env = &POWERPC_CPU(cs)->env;
bool vpm;
- if (msr_dr) {
+ if (!mmuidx_real(mmu_idx)) {
vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1);
} else {
vpm = ppc_hash64_use_vrma(env);
}
- if (vpm && !msr_hv) {
+ if (vpm && !mmuidx_hv(mmu_idx)) {
cs->exception_index = POWERPC_EXCP_HDSI;
env->spr[SPR_HDAR] = dar;
env->spr[SPR_HDSISR] = dsisr;
static void ppc_hash64_set_r(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1)
{
- hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + 16;
+ hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_R;
if (cpu->vhyp) {
PPCVirtualHypervisorClass *vhc =
static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1)
{
- hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + 15;
+ hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_C;
if (cpu->vhyp) {
PPCVirtualHypervisorClass *vhc =
return -1;
}
-int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr,
- int rwx, int mmu_idx)
+bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type,
+ hwaddr *raddrp, int *psizep, int *protp, int mmu_idx,
+ bool guest_visible)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
hwaddr ptex;
ppc_hash_pte64_t pte;
int exec_prot, pp_prot, amr_prot, prot;
- const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC};
+ int need_prot;
hwaddr raddr;
- assert((rwx == 0) || (rwx == 1) || (rwx == 2));
-
/*
* Note on LPCR usage: 970 uses HID4, but our special variant of
* store_spr copies relevant fields into env->spr[SPR_LPCR].
*/
/* 1. Handle real mode accesses */
- if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
+ if (mmuidx_real(mmu_idx)) {
/*
* Translation is supposedly "off", but in real mode the top 4
* effective address bits are (mostly) ignored
* In virtual hypervisor mode, there's nothing to do:
* EA == GPA == qemu guest address
*/
- } else if (msr_hv || !env->has_hv_mode) {
+ } else if (mmuidx_hv(mmu_idx) || !env->has_hv_mode) {
/* In HV mode, add HRMOR if top EA bit is clear */
if (!(eaddr >> 63)) {
raddr |= env->spr[SPR_HRMOR];
slb = &vrma_slbe;
if (build_vrma_slbe(cpu, slb) != 0) {
/* Invalid VRMA setup, machine check */
- cs->exception_index = POWERPC_EXCP_MCHECK;
- env->error_code = 0;
- return 1;
+ if (guest_visible) {
+ cs->exception_index = POWERPC_EXCP_MCHECK;
+ env->error_code = 0;
+ }
+ return false;
}
goto skip_slb_search;
/* Emulated old-style RMO mode, bounds check against RMLS */
if (raddr >= limit) {
- if (rwx == 2) {
- ppc_hash64_set_isi(cs, SRR1_PROTFAULT);
- } else {
- int dsisr = DSISR_PROTFAULT;
- if (rwx == 1) {
- dsisr |= DSISR_ISSTORE;
- }
- ppc_hash64_set_dsi(cs, eaddr, dsisr);
+ if (!guest_visible) {
+ return false;
+ }
+ switch (access_type) {
+ case MMU_INST_FETCH:
+ ppc_hash64_set_isi(cs, mmu_idx, SRR1_PROTFAULT);
+ break;
+ case MMU_DATA_LOAD:
+ ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_PROTFAULT);
+ break;
+ case MMU_DATA_STORE:
+ ppc_hash64_set_dsi(cs, mmu_idx, eaddr,
+ DSISR_PROTFAULT | DSISR_ISSTORE);
+ break;
+ default:
+ g_assert_not_reached();
}
- return 1;
+ return false;
}
raddr |= env->spr[SPR_RMOR];
}
- tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
- PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
- TARGET_PAGE_SIZE);
- return 0;
+
+ *raddrp = raddr;
+ *protp = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ *psizep = TARGET_PAGE_BITS;
+ return true;
}
/* 2. Translation is on, so look up the SLB */
exit(1);
}
/* Segment still not found, generate the appropriate interrupt */
- if (rwx == 2) {
+ if (!guest_visible) {
+ return false;
+ }
+ switch (access_type) {
+ case MMU_INST_FETCH:
cs->exception_index = POWERPC_EXCP_ISEG;
env->error_code = 0;
- } else {
+ break;
+ case MMU_DATA_LOAD:
+ case MMU_DATA_STORE:
cs->exception_index = POWERPC_EXCP_DSEG;
env->error_code = 0;
env->spr[SPR_DAR] = eaddr;
+ break;
+ default:
+ g_assert_not_reached();
}
- return 1;
+ return false;
}
-skip_slb_search:
+ skip_slb_search:
/* 3. Check for segment level no-execute violation */
- if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) {
- ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD);
- return 1;
+ if (access_type == MMU_INST_FETCH && (slb->vsid & SLB_VSID_N)) {
+ if (guest_visible) {
+ ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOEXEC_GUARD);
+ }
+ return false;
}
/* 4. Locate the PTE in the hash table */
ptex = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift);
if (ptex == -1) {
- if (rwx == 2) {
- ppc_hash64_set_isi(cs, SRR1_NOPTE);
- } else {
- int dsisr = DSISR_NOPTE;
- if (rwx == 1) {
- dsisr |= DSISR_ISSTORE;
- }
- ppc_hash64_set_dsi(cs, eaddr, dsisr);
+ if (!guest_visible) {
+ return false;
+ }
+ switch (access_type) {
+ case MMU_INST_FETCH:
+ ppc_hash64_set_isi(cs, mmu_idx, SRR1_NOPTE);
+ break;
+ case MMU_DATA_LOAD:
+ ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE);
+ break;
+ case MMU_DATA_STORE:
+ ppc_hash64_set_dsi(cs, mmu_idx, eaddr, DSISR_NOPTE | DSISR_ISSTORE);
+ break;
+ default:
+ g_assert_not_reached();
}
- return 1;
+ return false;
}
qemu_log_mask(CPU_LOG_MMU,
"found PTE at index %08" HWADDR_PRIx "\n", ptex);
/* 5. Check access permissions */
exec_prot = ppc_hash64_pte_noexec_guard(cpu, pte);
- pp_prot = ppc_hash64_pte_prot(cpu, slb, pte);
+ pp_prot = ppc_hash64_pte_prot(mmu_idx, slb, pte);
amr_prot = ppc_hash64_amr_prot(cpu, pte);
prot = exec_prot & pp_prot & amr_prot;
- if ((need_prot[rwx] & ~prot) != 0) {
+ need_prot = prot_for_access_type(access_type);
+ if (need_prot & ~prot) {
/* Access right violation */
qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n");
- if (rwx == 2) {
+ if (!guest_visible) {
+ return false;
+ }
+ if (access_type == MMU_INST_FETCH) {
int srr1 = 0;
if (PAGE_EXEC & ~exec_prot) {
srr1 |= SRR1_NOEXEC_GUARD; /* Access violates noexec or guard */
if (PAGE_EXEC & ~amr_prot) {
srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */
}
- ppc_hash64_set_isi(cs, srr1);
+ ppc_hash64_set_isi(cs, mmu_idx, srr1);
} else {
int dsisr = 0;
- if (need_prot[rwx] & ~pp_prot) {
+ if (need_prot & ~pp_prot) {
dsisr |= DSISR_PROTFAULT;
}
- if (rwx == 1) {
+ if (access_type == MMU_DATA_STORE) {
dsisr |= DSISR_ISSTORE;
}
- if (need_prot[rwx] & ~amr_prot) {
+ if (need_prot & ~amr_prot) {
dsisr |= DSISR_AMR;
}
- ppc_hash64_set_dsi(cs, eaddr, dsisr);
+ ppc_hash64_set_dsi(cs, mmu_idx, eaddr, dsisr);
}
- return 1;
+ return false;
}
qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n");
ppc_hash64_set_r(cpu, ptex, pte.pte1);
}
if (!(pte.pte1 & HPTE64_R_C)) {
- if (rwx == 1) {
+ if (access_type == MMU_DATA_STORE) {
ppc_hash64_set_c(cpu, ptex, pte.pte1);
} else {
/*
/* 7. Determine the real address from the PTE */
- raddr = deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, eaddr);
-
- tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
- prot, mmu_idx, 1ULL << apshift);
-
- return 0;
-}
-
-hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
-{
- CPUPPCState *env = &cpu->env;
- ppc_slb_t vrma_slbe;
- ppc_slb_t *slb;
- hwaddr ptex, raddr;
- ppc_hash_pte64_t pte;
- unsigned apshift;
-
- /* Handle real mode */
- if (msr_dr == 0) {
- /* In real mode the top 4 effective address bits are ignored */
- raddr = addr & 0x0FFFFFFFFFFFFFFFULL;
-
- if (cpu->vhyp) {
- /*
- * In virtual hypervisor mode, there's nothing to do:
- * EA == GPA == qemu guest address
- */
- return raddr;
- } else if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) {
- /* In HV mode, add HRMOR if top EA bit is clear */
- return raddr | env->spr[SPR_HRMOR];
- } else if (ppc_hash64_use_vrma(env)) {
- /* Emulated VRMA mode */
- slb = &vrma_slbe;
- if (build_vrma_slbe(cpu, slb) != 0) {
- return -1;
- }
- } else {
- target_ulong limit = rmls_limit(cpu);
-
- /* Emulated old-style RMO mode, bounds check against RMLS */
- if (raddr >= limit) {
- return -1;
- }
- return raddr | env->spr[SPR_RMOR];
- }
- } else {
- slb = slb_lookup(cpu, addr);
- if (!slb) {
- return -1;
- }
- }
-
- ptex = ppc_hash64_htab_lookup(cpu, slb, addr, &pte, &apshift);
- if (ptex == -1) {
- return -1;
- }
-
- return deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, addr)
- & TARGET_PAGE_MASK;
+ *raddrp = deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, eaddr);
+ *protp = prot;
+ *psizep = apshift;
+ return true;
}
void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex,
cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH;
}
-void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
-{
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
- CPUPPCState *env = &cpu->env;
-
- env->spr[SPR_LPCR] = val & pcc->lpcr_mask;
-}
-
+#ifdef CONFIG_TCG
void helper_store_lpcr(CPUPPCState *env, target_ulong val)
{
PowerPCCPU *cpu = env_archcpu(env);
ppc_store_lpcr(cpu, val);
}
+#endif
void ppc_hash64_init(PowerPCCPU *cpu)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
if (!pcc->hash64_opts) {
- assert(!(env->mmu_model & POWERPC_MMU_64));
+ assert(!mmu_is_64bit(env->mmu_model));
return;
}
}
};
-void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu,
- bool (*cb)(void *, uint32_t, uint32_t),
- void *opaque)
-{
- PPCHash64Options *opts = cpu->hash64_opts;
- int i;
- int n = 0;
- bool ci_largepage = false;
-
- assert(opts);
-
- n = 0;
- for (i = 0; i < ARRAY_SIZE(opts->sps); i++) {
- PPCHash64SegmentPageSizes *sps = &opts->sps[i];
- int j;
- int m = 0;
-
- assert(n <= i);
-
- if (!sps->page_shift) {
- break;
- }
-
- for (j = 0; j < ARRAY_SIZE(sps->enc); j++) {
- PPCHash64PageSize *ps = &sps->enc[j];
- assert(m <= j);
- if (!ps->page_shift) {
- break;
- }
-
- if (cb(opaque, sps->page_shift, ps->page_shift)) {
- if (ps->page_shift >= 16) {
- ci_largepage = true;
- }
- sps->enc[m++] = *ps;
- }
- }
-
- /* Clear rest of the row */
- for (j = m; j < ARRAY_SIZE(sps->enc); j++) {
- memset(&sps->enc[j], 0, sizeof(sps->enc[j]));
- }
-
- if (m) {
- n++;
- }
- }
-
- /* Clear the rest of the table */
- for (i = n; i < ARRAY_SIZE(opts->sps); i++) {
- memset(&opts->sps[i], 0, sizeof(opts->sps[i]));
- }
-
- if (!ci_largepage) {
- opts->flags &= ~PPC_HASH64_CI_LARGEPAGE;
- }
-}