fix trap handling if illegal fetch (PMP) and U-mode CSRs
This commit is contained in:
parent
af887c286f
commit
d95846a849
|
@ -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 ARCH>
|
||||
typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t instr){
|
||||
for(auto& e: qlut[instr&0x3]){
|
||||
|
@ -307,7 +311,6 @@ typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t inst
|
|||
template <typename ARCH>
|
||||
typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::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<ARCH>::virt_addr_t vm_impl<ARCH>::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<unsigned>::max());
|
||||
pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::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;
|
||||
}
|
||||
|
|
|
@ -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::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
|
||||
|
@ -355,6 +355,8 @@ riscv_hart_m_p<BASE>::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<BASE>::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 <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
|
|||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_mtvec(unsigned addr, reg_t &val) {
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_tvec(unsigned addr, reg_t &val) {
|
||||
val = csr[mtvec] & ~2;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
@ -753,7 +753,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ip(unsigned add
|
|||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_mepc(unsigned addr, reg_t val) {
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_epc(unsigned addr, reg_t val) {
|
||||
csr[addr] = val & get_pc_mask();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
|
|
@ -478,6 +478,12 @@ riscv_hart_msu_vp<BASE>::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<BASE>::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 <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
|
|||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_mtvec(unsigned addr, reg_t &val) {
|
||||
val = csr[mtvec] & ~2;
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_tvec(unsigned addr, reg_t &val) {
|
||||
val = csr[addr] & ~2;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
|
@ -928,9 +932,8 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ip(unsigned
|
|||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_mepc(unsigned addr, reg_t val) {
|
||||
auto mask = get_pc_mask();
|
||||
csr[addr] = val;//(csr[addr] & ~mask) | (val & mask);
|
||||
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_epc(unsigned addr, reg_t val) {
|
||||
csr[addr] = val & get_pc_mask();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
|
|
|
@ -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::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_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 <typename BASE, features_e FEAT>
|
||||
|
@ -326,6 +327,7 @@ riscv_hart_mu_p<BASE, FEAT>::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<BASE, FEAT>::riscv_hart_mu_p()
|
|||
//csr_wr_cb[addr] = &this_class::write_reg;
|
||||
}
|
||||
// common regs
|
||||
const std::array<unsigned, 10> addrs{{misa, mvendorid, marchid, mimpid, mepc, mtvec, mscratch, mcause, mtval, mscratch}};
|
||||
const std::array<unsigned, 14> 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<BASE, FEAT>::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<BASE, FEAT>::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<BASE, FEAT>::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 <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m
|
|||
throw std::runtime_error("memory load file not found");
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT> bool riscv_hart_mu_p<BASE, FEAT>::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 <typename BASE, features_e FEAT>
|
||||
iss::status riscv_hart_mu_p<BASE, FEAT>::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<BASE, FEAT>::read(const address_type type, const acc
|
|||
try {
|
||||
switch (space) {
|
||||
case traits<BASE>::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<BASE, FEAT>::write(const address_type type, const ac
|
|||
try {
|
||||
switch (space) {
|
||||
case traits<BASE>::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 <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_mtvec(unsigned addr, reg_t &val) {
|
||||
val = csr[mtvec] & ~2;
|
||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_tvec(unsigned addr, reg_t &val) {
|
||||
val = csr[addr] & ~2;
|
||||
return iss::Ok;
|
||||
}
|
||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_status(unsigned addr, reg_t &val) {
|
||||
|
@ -815,9 +887,8 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
|
|||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_mepc(unsigned addr, reg_t val) {
|
||||
auto mask = get_pc_mask();
|
||||
csr[addr] = val;//(csr[addr] & ~mask) | (val & mask);
|
||||
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_epc(unsigned addr, reg_t val) {
|
||||
csr[addr] = val & get_pc_mask();
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
|
@ -954,6 +1025,7 @@ template <typename BASE, features_e FEAT> void riscv_hart_mu_p<BASE, FEAT>::chec
|
|||
template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::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<uint64_t>::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
|
||||
|
|
Loading…
Reference in New Issue