From d95846a849461c7eb0bf6c54479c823045a405fd Mon Sep 17 00:00:00 2001 From: Eyck Jentzsch Date: Sun, 1 Aug 2021 17:23:22 +0200 Subject: [PATCH] fix trap handling if illegal fetch (PMP) and U-mode CSRs --- gen_input/templates/interp/CORENAME.cpp.gtl | 19 +++-- incl/iss/arch/riscv_hart_m_p.h | 12 +-- incl/iss/arch/riscv_hart_msu_vp.h | 17 ++-- incl/iss/arch/riscv_hart_mu_p.h | 94 ++++++++++++++++++--- 4 files changed, 110 insertions(+), 32 deletions(-) diff --git a/gen_input/templates/interp/CORENAME.cpp.gtl b/gen_input/templates/interp/CORENAME.cpp.gtl index 0351533..65d3b5b 100644 --- a/gen_input/templates/interp/CORENAME.cpp.gtl +++ b/gen_input/templates/interp/CORENAME.cpp.gtl @@ -296,6 +296,10 @@ inline bool is_count_limit_enabled(finish_cond_e cond){ return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT; } +inline bool is_jump_to_self_enabled(finish_cond_e cond){ + return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF; +} + template typename vm_impl::compile_func vm_impl::decode_inst(code_word_t instr){ for(auto& e: qlut[instr&0x3]){ @@ -307,7 +311,6 @@ typename vm_impl::compile_func vm_impl::decode_inst(code_word_t inst template typename vm_base::virt_addr_t vm_impl::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){ // we fetch at max 4 byte, alignment is 2 - enum {TRAP_ID=1<<16}; code_word_t insn = 0; auto *const data = (uint8_t *)&insn; auto pc=start; @@ -315,14 +318,14 @@ typename vm_base::virt_addr_t vm_impl::execute_inst(finish_cond_e co !(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){ auto res = fetch_ins(pc, data); if(res!=iss::Ok){ - auto new_pc = super::core.enter_trap(TRAP_ID, pc.val, 0); - res = fetch_ins(virt_addr_t{access_type::FETCH, new_pc}, data); - if(res!=iss::Ok) throw simulation_stopped(0); + this->do_sync(POST_SYNC, std::numeric_limits::max()); + pc.val = super::core.enter_trap(std::numeric_limits::max(), pc.val, 0); + } else { + if (is_jump_to_self_enabled(cond) && + (insn == 0x0000006f || (insn&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' + auto f = decode_inst(insn); + pc = (this->*f)(pc, insn); } - if ((cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF && - (insn == 0x0000006f || (insn&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' - auto f = decode_inst(insn); - pc = (this->*f)(pc, insn); } return pc; } diff --git a/incl/iss/arch/riscv_hart_m_p.h b/incl/iss/arch/riscv_hart_m_p.h index b266bae..0c4bdaa 100644 --- a/incl/iss/arch/riscv_hart_m_p.h +++ b/incl/iss/arch/riscv_hart_m_p.h @@ -280,7 +280,7 @@ private: iss::status write_cycle(unsigned addr, reg_t val); iss::status read_instret(unsigned addr, reg_t &val); iss::status write_instret(unsigned addr, reg_t val); - iss::status read_mtvec(unsigned addr, reg_t &val); + iss::status read_tvec(unsigned addr, reg_t &val); iss::status read_time(unsigned addr, reg_t &val); iss::status read_status(unsigned addr, reg_t &val); iss::status write_status(unsigned addr, reg_t val); @@ -290,7 +290,7 @@ private: iss::status read_ip(unsigned addr, reg_t &val); iss::status write_ip(unsigned addr, reg_t val); iss::status read_hartid(unsigned addr, reg_t &val); - iss::status write_mepc(unsigned addr, reg_t val); + iss::status write_epc(unsigned addr, reg_t val); reg_t mhartid_reg{0x0}; std::functionmem_read_cb; @@ -355,6 +355,8 @@ riscv_hart_m_p::riscv_hart_m_p() csr_rd_cb[mstatus] = &this_class::read_status; csr_wr_cb[mstatus] = &this_class::write_status; csr_wr_cb[mcause] = &this_class::write_cause; + csr_rd_cb[mtvec] = &this_class::read_tvec; + csr_wr_cb[mepc] = &this_class::write_epc; csr_rd_cb[mip] = &this_class::read_ip; csr_wr_cb[mip] = &this_class::write_ip; csr_rd_cb[mie] = &this_class::read_ie; @@ -362,8 +364,6 @@ riscv_hart_m_p::riscv_hart_m_p() csr_rd_cb[mhartid] = &this_class::read_hartid; csr_rd_cb[mcounteren] = &this_class::read_null; csr_wr_cb[mcounteren] = &this_class::write_null; - csr_rd_cb[mtvec] = &this_class::read_mtvec; - csr_wr_cb[mepc] = &this_class::write_mepc; csr_wr_cb[misa] = &this_class::write_null; csr_wr_cb[mvendorid] = &this_class::write_null; csr_wr_cb[marchid] = &this_class::write_null; @@ -702,7 +702,7 @@ template iss::status riscv_hart_m_p::read_time(unsigned ad return iss::Ok; } -template iss::status riscv_hart_m_p::read_mtvec(unsigned addr, reg_t &val) { +template iss::status riscv_hart_m_p::read_tvec(unsigned addr, reg_t &val) { val = csr[mtvec] & ~2; return iss::Ok; } @@ -753,7 +753,7 @@ template iss::status riscv_hart_m_p::write_ip(unsigned add return iss::Ok; } -template iss::status riscv_hart_m_p::write_mepc(unsigned addr, reg_t val) { +template iss::status riscv_hart_m_p::write_epc(unsigned addr, reg_t val) { csr[addr] = val & get_pc_mask(); return iss::Ok; } diff --git a/incl/iss/arch/riscv_hart_msu_vp.h b/incl/iss/arch/riscv_hart_msu_vp.h index 853a519..9de36f3 100644 --- a/incl/iss/arch/riscv_hart_msu_vp.h +++ b/incl/iss/arch/riscv_hart_msu_vp.h @@ -478,6 +478,12 @@ riscv_hart_msu_vp::riscv_hart_msu_vp() csr_rd_cb[ustatus] = &this_class::read_status; csr_wr_cb[ustatus] = &this_class::write_status; csr_wr_cb[ucause] = &this_class::write_cause; + csr_rd_cb[mtvec] = &this_class::read_tvec; + csr_rd_cb[stvec] = &this_class::read_tvec; + csr_rd_cb[utvec] = &this_class::read_tvec; + csr_wr_cb[mepc] = &this_class::write_epc; + csr_wr_cb[sepc] = &this_class::write_epc; + csr_wr_cb[uepc] = &this_class::write_epc; csr_rd_cb[mip] = &this_class::read_ip; csr_wr_cb[mip] = &this_class::write_ip; csr_rd_cb[sip] = &this_class::read_ip; @@ -493,8 +499,6 @@ riscv_hart_msu_vp::riscv_hart_msu_vp() csr_rd_cb[mhartid] = &this_class::read_hartid; csr_rd_cb[mcounteren] = &this_class::read_null; csr_wr_cb[mcounteren] = &this_class::write_null; - csr_rd_cb[mtvec] = &this_class::read_mtvec; - csr_wr_cb[mepc] = &this_class::write_mepc; csr_wr_cb[misa] = &this_class::write_null; csr_wr_cb[mvendorid] = &this_class::write_null; csr_wr_cb[marchid] = &this_class::write_null; @@ -868,8 +872,8 @@ template iss::status riscv_hart_m_p::read_time(unsigned ad return iss::Ok; } -template iss::status riscv_hart_m_p::read_mtvec(unsigned addr, reg_t &val) { - val = csr[mtvec] & ~2; +template iss::status riscv_hart_m_p::read_tvec(unsigned addr, reg_t &val) { + val = csr[addr] & ~2; return iss::Ok; } @@ -928,9 +932,8 @@ template iss::status riscv_hart_msu_vp::write_ip(unsigned return iss::Ok; } -template iss::status riscv_hart_m_p::write_mepc(unsigned addr, reg_t val) { - auto mask = get_pc_mask(); - csr[addr] = val;//(csr[addr] & ~mask) | (val & mask); +template iss::status riscv_hart_m_p::write_epc(unsigned addr, reg_t val) { + csr[addr] = val & get_pc_mask(); return iss::Ok; } diff --git a/incl/iss/arch/riscv_hart_mu_p.h b/incl/iss/arch/riscv_hart_mu_p.h index b388afe..457d64e 100644 --- a/incl/iss/arch/riscv_hart_mu_p.h +++ b/incl/iss/arch/riscv_hart_mu_p.h @@ -297,7 +297,7 @@ private: iss::status write_cycle(unsigned addr, reg_t val); iss::status read_instret(unsigned addr, reg_t &val); iss::status write_instret(unsigned addr, reg_t val); - iss::status read_mtvec(unsigned addr, reg_t &val); + iss::status read_tvec(unsigned addr, reg_t &val); iss::status read_time(unsigned addr, reg_t &val); iss::status read_status(unsigned addr, reg_t &val); iss::status write_status(unsigned addr, reg_t val); @@ -307,7 +307,7 @@ private: iss::status read_ip(unsigned addr, reg_t &val); iss::status write_ip(unsigned addr, reg_t val); iss::status read_hartid(unsigned addr, reg_t &val); - iss::status write_mepc(unsigned addr, reg_t val); + iss::status write_epc(unsigned addr, reg_t val); reg_t mhartid_reg{0x0}; std::functionmem_read_cb; @@ -315,6 +315,7 @@ private: protected: void check_interrupt(); + bool pmp_check(const access_type type, const uint64_t addr, const unsigned len); }; template @@ -326,6 +327,7 @@ riscv_hart_mu_p::riscv_hart_mu_p() csr[mvendorid] = 0x669; csr[marchid] = 0x80000003; csr[mimpid] = 1; + csr[mclicbase] = 0xc0000000; // TODO: should be taken from YAML file uart_buf.str(""); for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){ @@ -348,7 +350,11 @@ riscv_hart_mu_p::riscv_hart_mu_p() //csr_wr_cb[addr] = &this_class::write_reg; } // common regs - const std::array addrs{{misa, mvendorid, marchid, mimpid, mepc, mtvec, mscratch, mcause, mtval, mscratch}}; + const std::array addrs{{ + misa, mvendorid, marchid, mimpid, + mepc, mtvec, mscratch, mcause, mtval, + uepc, utvec, uscratch, ucause, utval, + }}; for(auto addr: addrs) { csr_rd_cb[addr] = &this_class::read_reg; csr_wr_cb[addr] = &this_class::write_reg; @@ -375,6 +381,10 @@ riscv_hart_mu_p::riscv_hart_mu_p() csr_rd_cb[ustatus] = &this_class::read_status; csr_wr_cb[ustatus] = &this_class::write_status; csr_wr_cb[ucause] = &this_class::write_cause; + csr_rd_cb[mtvec] = &this_class::read_tvec; + csr_rd_cb[utvec] = &this_class::read_tvec; + csr_wr_cb[mepc] = &this_class::write_epc; + csr_wr_cb[uepc] = &this_class::write_epc; csr_rd_cb[mip] = &this_class::read_ip; csr_wr_cb[mip] = &this_class::write_ip; csr_rd_cb[uip] = &this_class::read_ip; @@ -386,8 +396,6 @@ riscv_hart_mu_p::riscv_hart_mu_p() csr_rd_cb[mhartid] = &this_class::read_hartid; csr_rd_cb[mcounteren] = &this_class::read_null; csr_wr_cb[mcounteren] = &this_class::write_null; - csr_rd_cb[mtvec] = &this_class::read_mtvec; - csr_wr_cb[mepc] = &this_class::write_mepc; csr_wr_cb[misa] = &this_class::write_null; csr_wr_cb[mvendorid] = &this_class::write_null; csr_wr_cb[marchid] = &this_class::write_null; @@ -423,7 +431,7 @@ riscv_hart_mu_p::riscv_hart_mu_p() csr_rd_cb[mintthresh] = &this_class::read_reg; csr_wr_cb[mintthresh] = &this_class::write_reg; csr_rd_cb[mclicbase] = &this_class::read_reg; - csr_wr_cb[mclicbase] = &this_class::write_reg; + csr_wr_cb[mclicbase] = &this_class::write_null; } } @@ -471,6 +479,54 @@ template std::pair riscv_hart_m throw std::runtime_error("memory load file not found"); } +template bool riscv_hart_mu_p::pmp_check(const access_type type, const uint64_t addr, const unsigned len) { + constexpr auto PMP_SHIFT=2U; + constexpr auto PMP_R = 0x1U; + constexpr auto PMP_W = 0x2U; + constexpr auto PMP_X = 0x4U; + constexpr auto PMP_A = 0x18U; + constexpr auto PMP_L = 0x80U; + constexpr auto PMP_TOR =0x1U; + constexpr auto PMP_NA4 =0x2U; + constexpr auto PMP_NAPOT =0x3U; + reg_t base = 0; + for (size_t i = 0; i < 16; i++) { + reg_t tor = csr[pmpaddr0+i] << PMP_SHIFT; + uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4); + if (cfg & PMP_A) { + bool is_tor = (cfg & PMP_A) == PMP_TOR; + bool is_na4 = (cfg & PMP_A) == PMP_NA4; + + reg_t mask = (csr[pmpaddr0+i] << 1) | (!is_na4); + mask = ~(mask & ~(mask + 1)) << PMP_SHIFT; + + // Check each 4-byte sector of the access + bool any_match = false; + bool all_match = true; + for (reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) { + reg_t cur_addr = addr + offset; + bool napot_match = ((cur_addr ^ tor) & mask) == 0; + bool tor_match = base <= cur_addr && cur_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 (this->reg.PRIV == PRIV_M && !(cfg & PMP_L)) || + (type == access_type::READ && (cfg & PMP_R)) || + (type == access_type::WRITE && (cfg & PMP_W)) || + (type == access_type::FETCH && (cfg & PMP_X)); + } + } + base = tor; + } + return this->reg.PRIV == PRIV_M; +} + + template iss::status riscv_hart_mu_p::read(const address_type type, const access_type access, const uint32_t space, const uint64_t addr, const unsigned length, uint8_t *const data) { @@ -486,6 +542,14 @@ iss::status riscv_hart_mu_p::read(const address_type type, const acc try { switch (space) { case traits::MEM: { + if(FEAT & FEAT_PMP){ + if(!pmp_check(access, addr, length)) { + fault_data = addr; + if (access && iss::access_type::DEBUG) throw trap_access(0, addr); + this->reg.trap_state = (1 << 31) | (1 << 16); // issue trap 1 + return iss::Err; + } + } if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) { fault_data = addr; if (access && iss::access_type::DEBUG) throw trap_access(0, addr); @@ -569,6 +633,14 @@ iss::status riscv_hart_mu_p::write(const address_type type, const ac try { switch (space) { case traits::MEM: { + if(FEAT & FEAT_PMP){ + if(!pmp_check(access, addr, length)) { + fault_data = addr; + if (access && iss::access_type::DEBUG) throw trap_access(0, addr); + this->reg.trap_state = (1 << 31) | (1 << 16); // issue trap 1 + return iss::Err; + } + } if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) { fault_data = addr; if (access && iss::access_type::DEBUG) throw trap_access(0, addr); @@ -759,8 +831,8 @@ template iss::status riscv_hart_mu_p iss::status riscv_hart_mu_p::read_mtvec(unsigned addr, reg_t &val) { - val = csr[mtvec] & ~2; +template iss::status riscv_hart_mu_p::read_tvec(unsigned addr, reg_t &val) { + val = csr[addr] & ~2; return iss::Ok; } template iss::status riscv_hart_mu_p::read_status(unsigned addr, reg_t &val) { @@ -815,9 +887,8 @@ template iss::status riscv_hart_mu_p iss::status riscv_hart_mu_p::write_mepc(unsigned addr, reg_t val) { - auto mask = get_pc_mask(); - csr[addr] = val;//(csr[addr] & ~mask) | (val & mask); +template iss::status riscv_hart_mu_p::write_epc(unsigned addr, reg_t val) { + csr[addr] = val & get_pc_mask(); return iss::Ok; } @@ -954,6 +1025,7 @@ template void riscv_hart_mu_p::chec template uint64_t riscv_hart_mu_p::enter_trap(uint64_t flags, uint64_t addr, uint64_t instr) { // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0] // calculate and write mcause val + if(flags==std::numeric_limits::max()) flags=this->reg.trap_state; auto trap_id = bit_sub<0, 16>(flags); auto cause = bit_sub<16, 15>(flags); if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause