diff options
author | Andrew Waterman <andrew@sifive.com> | 2023-01-03 16:42:42 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-03 16:42:42 -0800 |
commit | 07647a9b53f482bb299ab783090af53995b4817e (patch) | |
tree | 26ccaf640ac85260490942c7b36c3968a36429a9 | |
parent | 85f7869bf58a4d9b3d4a09f9f312c2deb7b3c81a (diff) | |
parent | 43474ddc63ff52ef6a92e32889312609a770941c (diff) | |
download | riscv-isa-sim-07647a9b53f482bb299ab783090af53995b4817e.zip riscv-isa-sim-07647a9b53f482bb299ab783090af53995b4817e.tar.gz riscv-isa-sim-07647a9b53f482bb299ab783090af53995b4817e.tar.bz2 |
Merge pull request #1200 from riscv-software-src/mmio_pte
Support accessing PTEs through mmio_load/mmio_store
-rw-r--r-- | riscv/mmu.cc | 23 | ||||
-rw-r--r-- | riscv/mmu.h | 49 |
2 files changed, 54 insertions, 18 deletions
diff --git a/riscv/mmu.cc b/riscv/mmu.cc index ce3e5ca..b33e383 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -42,7 +42,7 @@ void mmu_t::flush_tlb() flush_icache(); } -static void throw_access_exception(bool virt, reg_t addr, access_type type) +void throw_access_exception(bool virt, reg_t addr, access_type type) { switch (type) { case FETCH: throw trap_instruction_access_fault(virt, addr, 0, 0); @@ -387,12 +387,7 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty // 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 pte = pte_load(pte_paddr, gva, virt, trap_type, vm.ptesize); reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT; bool pbmte = proc->get_state()->menvcfg->read() & MENVCFG_PBMTE; @@ -423,9 +418,7 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty #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); + pte_store(pte_paddr, pte | ad, gva, virt, type, vm.ptesize); } #else // take exception if access or possibly dirty bit is not set. @@ -481,11 +474,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool hlvx // check that physical address of PTE is legal auto pte_paddr = s2xlate(addr, base + idx * vm.ptesize, LOAD, type, virt, false); - auto ppte = sim->addr_to_mem(pte_paddr); - if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S)) - throw_access_exception(virt, addr, type); - - reg_t pte = vm.ptesize == 4 ? from_target(*(target_endian<uint32_t>*)ppte) : from_target(*(target_endian<uint64_t>*)ppte); + reg_t pte = pte_load(pte_paddr, addr, virt, type, vm.ptesize); reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT; bool pbmte = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_PBMTE) : (proc->get_state()->menvcfg->read() & MENVCFG_PBMTE); @@ -516,9 +505,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool hlvx #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, addr, type); - *(target_endian<uint32_t>*)ppte |= to_target((uint32_t)ad); + pte_store(pte_paddr, pte | ad, addr, virt, type, vm.ptesize); } #else // take exception if access or possibly dirty bit is not set. diff --git a/riscv/mmu.h b/riscv/mmu.h index 723b08e..4cc141f 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -38,6 +38,8 @@ struct tlb_entry_t { reg_t target_offset; }; +void throw_access_exception(bool virt, reg_t addr, access_type type); + // this class implements a processor's port into the virtual memory system. // an MMU and instruction cache are maintained for simulator performance. class mmu_t @@ -354,6 +356,53 @@ private: void check_triggers(triggers::operation_t operation, reg_t address, std::optional<reg_t> data = std::nullopt); reg_t translate(reg_t addr, reg_t len, access_type type, uint32_t xlate_flags); + reg_t pte_load(reg_t pte_paddr, reg_t addr, bool virt, access_type trap_type, size_t ptesize) { + if (ptesize == 4) + return pte_load<uint32_t>(pte_paddr, addr, virt, trap_type); + else + return pte_load<uint64_t>(pte_paddr, addr, virt, trap_type); + } + + void pte_store(reg_t pte_paddr, reg_t new_pte, reg_t addr, bool virt, access_type trap_type, size_t ptesize) { + if (ptesize == 4) + return pte_store<uint32_t>(pte_paddr, new_pte, addr, virt, trap_type); + else + return pte_store<uint64_t>(pte_paddr, new_pte, addr, virt, trap_type); + } + + template<typename T> inline reg_t pte_load(reg_t pte_paddr, reg_t addr, bool virt, access_type trap_type) + { + const size_t ptesize = sizeof(T); + + if (!pmp_ok(pte_paddr, ptesize, LOAD, PRV_S)) + throw_access_exception(virt, addr, trap_type); + + void* host_pte_paddr = sim->addr_to_mem(pte_paddr); + target_endian<T> target_pte; + if (host_pte_paddr) { + memcpy(&target_pte, host_pte_paddr, ptesize); + } else if (!mmio_load(pte_paddr, ptesize, (uint8_t*)&target_pte)) { + throw_access_exception(virt, addr, trap_type); + } + return from_target(target_pte); + } + + template<typename T> inline void pte_store(reg_t pte_paddr, reg_t new_pte, reg_t addr, bool virt, access_type trap_type) + { + const size_t ptesize = sizeof(T); + + if (!pmp_ok(pte_paddr, ptesize, STORE, PRV_S)) + throw_access_exception(virt, addr, trap_type); + + void* host_pte_paddr = sim->addr_to_mem(pte_paddr); + target_endian<T> target_pte = to_target((T)new_pte); + if (host_pte_paddr) { + memcpy(host_pte_paddr, &target_pte, ptesize); + } else if (!mmio_store(pte_paddr, ptesize, (uint8_t*)&target_pte)) { + throw_access_exception(virt, addr, trap_type); + } + } + // ITLB lookup inline tlb_entry_t translate_insn_addr(reg_t addr) { reg_t vpn = addr >> PGSHIFT; |