aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2021-11-29 14:37:47 -0800
committerGitHub <noreply@github.com>2021-11-29 14:37:47 -0800
commit2b261c9782310c08da2ba112792105f3ea8125b0 (patch)
treecf5ef13d35437fa6e380bd0a3febbf90f4bc1505
parent6b0f6f46ee79929a0e358f645705c3e5ec7adab7 (diff)
parentf5dd47acbf7d49ceff8974883a94c7b72c837c08 (diff)
downloadspike-2b261c9782310c08da2ba112792105f3ea8125b0.zip
spike-2b261c9782310c08da2ba112792105f3ea8125b0.tar.gz
spike-2b261c9782310c08da2ba112792105f3ea8125b0.tar.bz2
Merge pull request #869 from scottj97/badgpa
Take guest page fault if guest PA out of bounds
-rw-r--r--riscv/mmu.cc101
1 files changed, 53 insertions, 48 deletions
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index 906694c..eed656a 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -267,65 +267,70 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty
if (vm.levels == 0)
return gpa;
+ int maxgpabits = vm.levels * vm.idxbits + vm.widenbits + PGSHIFT;
+ reg_t maxgpa = (1ULL << maxgpabits) - 1;
+
bool mxr = proc->state.sstatus->readvirt(false) & MSTATUS_MXR;
reg_t base = vm.ptbase;
- for (int i = vm.levels - 1; i >= 0; i--) {
- int ptshift = i * vm.idxbits;
- int idxbits = (i == (vm.levels - 1)) ? vm.idxbits + vm.widenbits : vm.idxbits;
- reg_t idx = (gpa >> (PGSHIFT + ptshift)) & ((reg_t(1) << idxbits) - 1);
-
- // check that physical address of PTE is legal
- auto pte_paddr = base + idx * vm.ptesize;
- auto ppte = sim->addr_to_mem(pte_paddr);
- if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S)) {
- throw_access_exception(virt, gva, trap_type);
- }
+ if ((gpa & ~maxgpa) == 0) {
+ for (int i = vm.levels - 1; i >= 0; i--) {
+ int ptshift = i * vm.idxbits;
+ int idxbits = (i == (vm.levels - 1)) ? vm.idxbits + vm.widenbits : vm.idxbits;
+ reg_t idx = (gpa >> (PGSHIFT + ptshift)) & ((reg_t(1) << idxbits) - 1);
+
+ // check that physical address of PTE is legal
+ auto pte_paddr = base + idx * vm.ptesize;
+ auto ppte = sim->addr_to_mem(pte_paddr);
+ if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S)) {
+ throw_access_exception(virt, gva, trap_type);
+ }
- reg_t pte = vm.ptesize == 4 ? from_target(*(target_endian<uint32_t>*)ppte) : from_target(*(target_endian<uint64_t>*)ppte);
- reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT;
+ reg_t pte = vm.ptesize == 4 ? from_target(*(target_endian<uint32_t>*)ppte) : from_target(*(target_endian<uint64_t>*)ppte);
+ reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT;
- if (pte & PTE_RSVD) {
- break;
- } else if (PTE_TABLE(pte)) { // next level of page table
- if (pte & (PTE_D | PTE_A | PTE_U | PTE_N | PTE_PBMT))
+ if (pte & PTE_RSVD) {
break;
- base = ppn << PGSHIFT;
- } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
- break;
- } else if (!(pte & PTE_U)) {
- break;
- } else if (type == FETCH || hlvx ? !(pte & PTE_X) :
- type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
- !((pte & PTE_R) && (pte & PTE_W))) {
- break;
- } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
- break;
- } else {
- reg_t ad = PTE_A | ((type == STORE) * PTE_D);
+ } else if (PTE_TABLE(pte)) { // next level of page table
+ if (pte & (PTE_D | PTE_A | PTE_U | PTE_N | PTE_PBMT))
+ break;
+ base = ppn << PGSHIFT;
+ } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
+ break;
+ } else if (!(pte & PTE_U)) {
+ break;
+ } else if (type == FETCH || hlvx ? !(pte & PTE_X) :
+ type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) :
+ !((pte & PTE_R) && (pte & PTE_W))) {
+ break;
+ } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
+ break;
+ } else {
+ reg_t ad = PTE_A | ((type == STORE) * PTE_D);
#ifdef RISCV_ENABLE_DIRTY
- // set accessed and possibly dirty bits.
- if ((pte & ad) != ad) {
- if (!pmp_ok(pte_paddr, vm.ptesize, STORE, PRV_S))
- throw_access_exception(virt, gva, trap_type);
- *(target_endian<uint32_t>*)ppte |= to_target((uint32_t)ad);
- }
+ // set accessed and possibly dirty bits.
+ if ((pte & ad) != ad) {
+ if (!pmp_ok(pte_paddr, vm.ptesize, STORE, PRV_S))
+ throw_access_exception(virt, gva, trap_type);
+ *(target_endian<uint32_t>*)ppte |= to_target((uint32_t)ad);
+ }
#else
- // take exception if access or possibly dirty bit is not set.
- if ((pte & ad) != ad)
- break;
+ // take exception if access or possibly dirty bit is not set.
+ if ((pte & ad) != ad)
+ break;
#endif
- reg_t vpn = gpa >> PGSHIFT;
- reg_t page_mask = (reg_t(1) << PGSHIFT) - 1;
+ reg_t vpn = gpa >> PGSHIFT;
+ reg_t page_mask = (reg_t(1) << PGSHIFT) - 1;
- int napot_bits = ((pte & PTE_N) ? (ctz(ppn) + 1) : 0);
- if (((pte & PTE_N) && (ppn == 0 || i != 0)) || (napot_bits != 0 && napot_bits != 4))
- break;
+ int napot_bits = ((pte & PTE_N) ? (ctz(ppn) + 1) : 0);
+ if (((pte & PTE_N) && (ppn == 0 || i != 0)) || (napot_bits != 0 && napot_bits != 4))
+ break;
- reg_t page_base = ((ppn & ~((reg_t(1) << napot_bits) - 1))
- | (vpn & ((reg_t(1) << napot_bits) - 1))
- | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
- return page_base | (gpa & page_mask);
+ reg_t page_base = ((ppn & ~((reg_t(1) << napot_bits) - 1))
+ | (vpn & ((reg_t(1) << napot_bits) - 1))
+ | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
+ return page_base | (gpa & page_mask);
+ }
}
}