diff options
author | Andrew Waterman <aswaterman@gmail.com> | 2019-01-28 10:55:05 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-28 10:55:05 -0800 |
commit | ee6fe6501a21ea8d167b6a5048527ba9eb924878 (patch) | |
tree | e6d70e728afddf7a612669280fad5ffc9373ddd0 | |
parent | c544846020608d8ae471b53c8558c61e1702671f (diff) | |
download | riscv-isa-sim-ee6fe6501a21ea8d167b6a5048527ba9eb924878.zip riscv-isa-sim-ee6fe6501a21ea8d167b6a5048527ba9eb924878.tar.gz riscv-isa-sim-ee6fe6501a21ea8d167b6a5048527ba9eb924878.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.
-rw-r--r-- | riscv/mmu.cc | 27 | ||||
-rw-r--r-- | riscv/mmu.h | 2 |
2 files changed, 21 insertions, 8 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; } diff --git a/riscv/mmu.h b/riscv/mmu.h index 7617367..5fa93ff 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -354,7 +354,7 @@ private: } reg_t pmp_homogeneous(reg_t addr, reg_t len); - reg_t pmp_ok(reg_t addr, access_type type, reg_t mode); + reg_t pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode); bool check_triggers_fetch; bool check_triggers_load; |