summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target-ppc/mmu-hash64.c63
1 files changed, 60 insertions, 3 deletions
diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c
index 9ad02f3a8f..f4c25b7d14 100644
--- a/target-ppc/mmu-hash64.c
+++ b/target-ppc/mmu-hash64.c
@@ -22,6 +22,7 @@
#include "exec/helper-proto.h"
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
+#include "qemu/error-report.h"
#include "kvm_ppc.h"
#include "mmu-hash64.h"
@@ -475,12 +476,50 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu,
return pte_offset;
}
+static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps,
+ uint64_t pte0, uint64_t pte1)
+{
+ int i;
+
+ if (!(pte0 & HPTE64_V_LARGE)) {
+ if (sps->page_shift != 12) {
+ /* 4kiB page in a non 4kiB segment */
+ return 0;
+ }
+ /* Normal 4kiB page */
+ return 12;
+ }
+
+ for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
+ const struct ppc_one_page_size *ps = &sps->enc[i];
+ uint64_t mask;
+
+ if (!ps->page_shift) {
+ break;
+ }
+
+ if (ps->page_shift == 12) {
+ /* L bit is set so this can't be a 4kiB page */
+ continue;
+ }
+
+ mask = ((1ULL << ps->page_shift) - 1) & HPTE64_R_RPN;
+
+ if ((pte1 & mask) == (ps->pte_enc << HPTE64_R_RPN_SHIFT)) {
+ return ps->page_shift;
+ }
+ }
+
+ return 0; /* Bad page size encoding */
+}
+
int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
int rwx, int mmu_idx)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
ppc_slb_t *slb;
+ unsigned apshift;
hwaddr pte_offset;
ppc_hash_pte64_t pte;
int pp_prot, amr_prot, prot;
@@ -544,6 +583,18 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
qemu_log_mask(CPU_LOG_MMU,
"found PTE at offset %08" HWADDR_PRIx "\n", pte_offset);
+ /* Validate page size encoding */
+ apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1);
+ if (!apshift) {
+ error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64
+ " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset);
+ /* Not entirely sure what the right action here, but machine
+ * check seems reasonable */
+ cs->exception_index = POWERPC_EXCP_MCHECK;
+ env->error_code = 0;
+ return 1;
+ }
+
/* 5. Check access permissions */
pp_prot = ppc_hash64_pte_prot(cpu, slb, pte);
@@ -596,10 +647,10 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, target_ulong eaddr,
/* 7. Determine the real address from the PTE */
- raddr = deposit64(pte.pte1 & HPTE64_R_RPN, 0, slb->sps->page_shift, eaddr);
+ 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, TARGET_PAGE_SIZE);
+ prot, mmu_idx, 1ULL << apshift);
return 0;
}
@@ -610,6 +661,7 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
ppc_slb_t *slb;
hwaddr pte_offset;
ppc_hash_pte64_t pte;
+ unsigned apshift;
if (msr_dr == 0) {
/* In real mode the top 4 effective address bits are ignored */
@@ -626,7 +678,12 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr)
return -1;
}
- return deposit64(pte.pte1 & HPTE64_R_RPN, 0, slb->sps->page_shift, addr)
+ apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1);
+ if (!apshift) {
+ return -1;
+ }
+
+ return deposit64(pte.pte1 & HPTE64_R_RPN, 0, apshift, addr)
& TARGET_PAGE_MASK;
}