diff options
author | Andrew Waterman <aswaterman@gmail.com> | 2019-01-28 10:55:05 -0800 |
---|---|---|
committer | Dave.Wen <dave.wen@sifive.com> | 2019-04-06 00:03:30 +0800 |
commit | 024fcba5696bd3560db16d4e6ee36f170876281f (patch) | |
tree | 443938756d449154a2b3c69fd0e61ad373621571 /riscv/mmu.cc | |
parent | 86f5029d6ced0c72243bbf2c384fff42df11d97d (diff) | |
download | spike-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.cc | 27 |
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; } |