aboutsummaryrefslogtreecommitdiff
path: root/riscv/mmu.cc
diff options
context:
space:
mode:
authorAndrew Waterman <aswaterman@gmail.com>2019-01-28 10:55:05 -0800
committerDave.Wen <dave.wen@sifive.com>2019-04-06 00:03:30 +0800
commit024fcba5696bd3560db16d4e6ee36f170876281f (patch)
tree443938756d449154a2b3c69fd0e61ad373621571 /riscv/mmu.cc
parent86f5029d6ced0c72243bbf2c384fff42df11d97d (diff)
downloadspike-024fcba5696bd3560db16d4e6ee36f170876281f.zip
spike-024fcba5696bd3560db16d4e6ee36f170876281f.tar.gz
spike-024fcba5696bd3560db16d4e6ee36f170876281f.tar.bz2
Fix PMP checks for partially-matching accesses (#270)
PMP checks should unconditionally fail if the PMP matches part of, but not all of, an access. We got this right, but went too far: we checked whether _any_ PMP matches in this manner. In fact, only the first PMP that maches any of the bytes should be checked in this manner.
Diffstat (limited to 'riscv/mmu.cc')
-rw-r--r--riscv/mmu.cc27
1 files changed, 20 insertions, 7 deletions
diff --git a/riscv/mmu.cc b/riscv/mmu.cc
index 3e1fc25..78d00f9 100644
--- a/riscv/mmu.cc
+++ b/riscv/mmu.cc
@@ -56,7 +56,7 @@ reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type)
}
reg_t paddr = walk(addr, type, mode) | (addr & (PGSIZE-1));
- if (!pmp_ok(paddr, type, mode) || !pmp_homogeneous(paddr, len))
+ if (!pmp_ok(paddr, len, type, mode))
throw_access_exception(addr, type);
return paddr;
}
@@ -173,7 +173,7 @@ tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_
return entry;
}
-reg_t mmu_t::pmp_ok(reg_t addr, access_type type, reg_t mode)
+reg_t mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode)
{
if (!proc)
return true;
@@ -189,10 +189,23 @@ reg_t mmu_t::pmp_ok(reg_t addr, access_type type, reg_t mode)
reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4);
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
- bool napot_match = ((addr ^ tor) & mask) == 0;
- bool tor_match = base <= addr && addr < tor;
- if (is_tor ? tor_match : napot_match) {
+ // Check each 4-byte sector of the access
+ bool any_match = false;
+ bool all_match = true;
+ for (reg_t i = 0; i < len; i += (1 << PMP_SHIFT)) {
+ bool napot_match = ((addr ^ tor) & mask) == 0;
+ bool tor_match = base <= addr && addr < tor;
+ bool match = is_tor ? tor_match : napot_match;
+ any_match |= match;
+ all_match &= match;
+ }
+
+ if (any_match) {
+ // If the PMP matches only a strict subset of the access, fail it
+ if (!all_match)
+ return false;
+
return
(mode == PRV_M && !(cfg & PMP_L)) ||
(type == LOAD && (cfg & PMP_R)) ||
@@ -271,7 +284,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
// 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, LOAD, PRV_S))
+ if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S))
throw_access_exception(addr, type);
reg_t pte = vm.ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte;
@@ -294,7 +307,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode)
#ifdef RISCV_ENABLE_DIRTY
// set accessed and possibly dirty bits.
if ((pte & ad) != ad) {
- if (!pmp_ok(pte_paddr, STORE, PRV_S))
+ if (!pmp_ok(pte_paddr, vm.ptesize, STORE, PRV_S))
throw_access_exception(addr, type);
*(uint32_t*)ppte |= ad;
}