From a4b5a4808033cd63c0584feefd308def32ba48a4 Mon Sep 17 00:00:00 2001 From: Andrew Young Date: Tue, 21 Oct 2025 15:27:37 -0700 Subject: [PATCH] Add Svukte extension support This adds support for the Svukte extension, which adds support for address-independent latency of user-mode faults to supervisor addresses. --- disasm/isa_parser.cc | 2 ++ riscv/csr_init.cc | 1 + riscv/csrs.cc | 3 ++- riscv/isa_parser.h | 1 + riscv/mmu.cc | 44 ++++++++++++++++++++++++++++++++++++++------ riscv/mmu.h | 5 +++++ 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/disasm/isa_parser.cc b/disasm/isa_parser.cc index 32acb0d047..bfe3590b91 100644 --- a/disasm/isa_parser.cc +++ b/disasm/isa_parser.cc @@ -265,6 +265,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) extension_table[EXT_SVPBMT] = true; } else if (ext_str == "svinval") { extension_table[EXT_SVINVAL] = true; + } else if (ext_str == "svukte") { + extension_table[EXT_SVUKTE] = true; } else if (ext_str == "zfa") { extension_table[EXT_ZFA] = true; } else if (ext_str == "zicbom") { diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index 24889a91e2..2fb9a90eef 100644 --- a/riscv/csr_init.cc +++ b/riscv/csr_init.cc @@ -329,6 +329,7 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) } const reg_t senvcfg_mask = (proc->extension_enabled(EXT_ZICBOM) ? SENVCFG_CBCFE | SENVCFG_CBIE : 0) | (proc->extension_enabled(EXT_ZICBOZ) ? SENVCFG_CBZE : 0) | + (proc->extension_enabled(EXT_SVUKTE) ? SENVCFG_UKTE : 0) | (proc->extension_enabled(EXT_SSNPM) ? SENVCFG_PMM : 0) | (proc->extension_enabled(EXT_ZICFILP) ? SENVCFG_LPE : 0) | (proc->extension_enabled(EXT_ZICFISS) ? SENVCFG_SSE : 0); diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 608c6a6333..be351fc8b5 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -2020,7 +2020,8 @@ hstatus_csr_t::hstatus_csr_t(processor_t* const proc, const reg_t addr): } bool hstatus_csr_t::unlogged_write(const reg_t val) noexcept { - const reg_t mask = HSTATUS_VTSR | HSTATUS_VTW + const reg_t mask = (proc->extension_enabled(EXT_SVUKTE) ? HSTATUS_HUKTE : 0) + | HSTATUS_VTSR | HSTATUS_VTW | (proc->supports_impl(IMPL_MMU) ? HSTATUS_VTVM : 0) | (proc->extension_enabled(EXT_SSNPM) ? HSTATUS_HUPMM : 0) | HSTATUS_HU | HSTATUS_SPVP | HSTATUS_SPV | HSTATUS_GVA; diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index 6e927006ce..21285c86a9 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -47,6 +47,7 @@ typedef enum { EXT_SVNAPOT, EXT_SVPBMT, EXT_SVINVAL, + EXT_SVUKTE, EXT_ZDINX, EXT_ZFA, EXT_ZFBFMIN, diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 4eb8f51a8d..6ab4007088 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -53,6 +53,16 @@ void throw_access_exception(bool virt, reg_t addr, access_type type) } } +[[noreturn]] void throw_page_access_exception(bool virt, reg_t addr, access_type type) +{ + switch (type) { + case FETCH: throw trap_instruction_page_fault(virt, addr, 0, 0); + case LOAD: throw trap_load_page_fault(virt, addr, 0, 0); + case STORE: throw trap_store_page_fault(virt, addr, 0, 0); + default: abort(); + } +} + reg_t mmu_t::translate(mem_access_info_t access_info, reg_t len) { reg_t addr = access_info.transformed_vaddr; @@ -560,6 +570,29 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty } } +bool mmu_t::check_svukte_qualified(reg_t addr, reg_t mode, bool forced_virt) +{ + state_t* state = proc->get_state(); + + if (mode != PRV_U) + return true; + + if (proc->extension_enabled('S') && get_field(state->senvcfg->read(), SENVCFG_UKTE)) { + if (forced_virt && state->prv == PRV_U) { + bool hstatus_hukte = proc->extension_enabled('H') && (get_field(state->hstatus->read(), HSTATUS_HUKTE) == 1); + return !hstatus_hukte; + } + + if (((addr >> SYS_MEM_HEADING_BIT) & 0x1)) { + return ((state->v || forced_virt) && + ((proc->get_xlen() == 64) ? ((state->vsatp->read() & SATP64_MODE) == 0) : + ((state->vsatp->read() & SATP32_MODE) == 0))); + } + } + + return true; +} + reg_t mmu_t::walk(mem_access_info_t access_info) { access_type type = access_info.type; @@ -582,6 +615,10 @@ reg_t mmu_t::walk(mem_access_info_t access_info) if (vm.levels == 0) return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, type, virt, hlvx, false) & ~page_mask; // zero-extend from xlen + if (proc->extension_enabled(EXT_SVUKTE) && !check_svukte_qualified(addr, mode, access_info.flags.forced_virt)) { + throw_page_access_exception(virt, addr, type); + } + bool s_mode = mode == PRV_S; bool sum = proc->state.sstatus->readvirt(virt) & MSTATUS_SUM; bool mxr = (proc->state.sstatus->readvirt(false) | proc->state.sstatus->readvirt(virt)) & MSTATUS_MXR; @@ -672,12 +709,7 @@ reg_t mmu_t::walk(mem_access_info_t access_info) } } - switch (type) { - case FETCH: throw trap_instruction_page_fault(virt, addr, 0, 0); - case LOAD: throw trap_load_page_fault(virt, addr, 0, 0); - case STORE: throw trap_store_page_fault(virt, addr, 0, 0); - default: abort(); - } + throw_page_access_exception(virt, addr, type); } void mmu_t::register_memtracer(memtracer_t* t) diff --git a/riscv/mmu.h b/riscv/mmu.h index 48340cfa41..01ced6efb3 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -34,6 +34,9 @@ const reg_t PGSIZE = 1 << PGSHIFT; #define MMU_OBSERVE_STORE(addr, data, length) #endif +// Svukte extension +#define SYS_MEM_HEADING_BIT 63 + struct insn_fetch_t { insn_func_t func; @@ -78,6 +81,7 @@ struct mem_access_info_t { }; void throw_access_exception(bool virt, reg_t addr, access_type type); +[[noreturn]] void throw_page_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. @@ -447,6 +451,7 @@ class mmu_t check_triggers(operation, address, virt, address, data); } void check_triggers(triggers::operation_t operation, reg_t address, bool virt, reg_t tval, std::optional data); + bool check_svukte_qualified(reg_t addr, reg_t mode, bool forced_virt); reg_t translate(mem_access_info_t access_info, reg_t len); reg_t pte_load(reg_t pte_paddr, reg_t addr, bool virt, access_type trap_type, size_t ptesize) {