fix trap handling if illegal fetch (PMP) and U-mode CSRs

This commit is contained in:
Eyck Jentzsch 2021-08-01 17:23:22 +02:00
parent af887c286f
commit d95846a849
4 changed files with 110 additions and 32 deletions

View File

@ -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; 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> template <typename ARCH>
typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t instr){ typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t instr){
for(auto& e: qlut[instr&0x3]){ 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> 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){ 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 // we fetch at max 4 byte, alignment is 2
enum {TRAP_ID=1<<16};
code_word_t insn = 0; code_word_t insn = 0;
auto *const data = (uint8_t *)&insn; auto *const data = (uint8_t *)&insn;
auto pc=start; auto pc=start;
@ -315,15 +318,15 @@ 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)){ !(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){
auto res = fetch_ins(pc, data); auto res = fetch_ins(pc, data);
if(res!=iss::Ok){ if(res!=iss::Ok){
auto new_pc = super::core.enter_trap(TRAP_ID, pc.val, 0); this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max());
res = fetch_ins(virt_addr_t{access_type::FETCH, new_pc}, data); pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0);
if(res!=iss::Ok) throw simulation_stopped(0); } else {
} if (is_jump_to_self_enabled(cond) &&
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' (insn == 0x0000006f || (insn&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
auto f = decode_inst(insn); auto f = decode_inst(insn);
pc = (this->*f)(pc, insn); pc = (this->*f)(pc, insn);
} }
}
return pc; return pc;
} }

View File

@ -280,7 +280,7 @@ private:
iss::status write_cycle(unsigned addr, reg_t val); iss::status write_cycle(unsigned addr, reg_t val);
iss::status read_instret(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 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_time(unsigned addr, reg_t &val);
iss::status read_status(unsigned addr, reg_t &val); iss::status read_status(unsigned addr, reg_t &val);
iss::status write_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 read_ip(unsigned addr, reg_t &val);
iss::status write_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 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}; reg_t mhartid_reg{0x0};
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb; 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_rd_cb[mstatus] = &this_class::read_status;
csr_wr_cb[mstatus] = &this_class::write_status; csr_wr_cb[mstatus] = &this_class::write_status;
csr_wr_cb[mcause] = &this_class::write_cause; 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_rd_cb[mip] = &this_class::read_ip;
csr_wr_cb[mip] = &this_class::write_ip; csr_wr_cb[mip] = &this_class::write_ip;
csr_rd_cb[mie] = &this_class::read_ie; 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[mhartid] = &this_class::read_hartid;
csr_rd_cb[mcounteren] = &this_class::read_null; csr_rd_cb[mcounteren] = &this_class::read_null;
csr_wr_cb[mcounteren] = &this_class::write_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[misa] = &this_class::write_null;
csr_wr_cb[mvendorid] = &this_class::write_null; csr_wr_cb[mvendorid] = &this_class::write_null;
csr_wr_cb[marchid] = &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; 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; val = csr[mtvec] & ~2;
return iss::Ok; return iss::Ok;
} }
@ -753,7 +753,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ip(unsigned add
return iss::Ok; 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(); csr[addr] = val & get_pc_mask();
return iss::Ok; return iss::Ok;
} }

View File

@ -478,6 +478,12 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
csr_rd_cb[ustatus] = &this_class::read_status; csr_rd_cb[ustatus] = &this_class::read_status;
csr_wr_cb[ustatus] = &this_class::write_status; csr_wr_cb[ustatus] = &this_class::write_status;
csr_wr_cb[ucause] = &this_class::write_cause; 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_rd_cb[mip] = &this_class::read_ip;
csr_wr_cb[mip] = &this_class::write_ip; csr_wr_cb[mip] = &this_class::write_ip;
csr_rd_cb[sip] = &this_class::read_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[mhartid] = &this_class::read_hartid;
csr_rd_cb[mcounteren] = &this_class::read_null; csr_rd_cb[mcounteren] = &this_class::read_null;
csr_wr_cb[mcounteren] = &this_class::write_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[misa] = &this_class::write_null;
csr_wr_cb[mvendorid] = &this_class::write_null; csr_wr_cb[mvendorid] = &this_class::write_null;
csr_wr_cb[marchid] = &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; 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; val = csr[addr] & ~2;
return iss::Ok; return iss::Ok;
} }
@ -928,9 +932,8 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ip(unsigned
return iss::Ok; 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) {
auto mask = get_pc_mask(); csr[addr] = val & get_pc_mask();
csr[addr] = val;//(csr[addr] & ~mask) | (val & mask);
return iss::Ok; return iss::Ok;
} }

View File

@ -297,7 +297,7 @@ private:
iss::status write_cycle(unsigned addr, reg_t val); iss::status write_cycle(unsigned addr, reg_t val);
iss::status read_instret(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 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_time(unsigned addr, reg_t &val);
iss::status read_status(unsigned addr, reg_t &val); iss::status read_status(unsigned addr, reg_t &val);
iss::status write_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 read_ip(unsigned addr, reg_t &val);
iss::status write_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 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}; reg_t mhartid_reg{0x0};
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb; std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
@ -315,6 +315,7 @@ private:
protected: protected:
void check_interrupt(); void check_interrupt();
bool pmp_check(const access_type type, const uint64_t addr, const unsigned len);
}; };
template <typename BASE, features_e FEAT> template <typename BASE, features_e FEAT>
@ -326,6 +327,7 @@ riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p()
csr[mvendorid] = 0x669; csr[mvendorid] = 0x669;
csr[marchid] = 0x80000003; csr[marchid] = 0x80000003;
csr[mimpid] = 1; csr[mimpid] = 1;
csr[mclicbase] = 0xc0000000; // TODO: should be taken from YAML file
uart_buf.str(""); uart_buf.str("");
for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){ 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; //csr_wr_cb[addr] = &this_class::write_reg;
} }
// common regs // 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) { for(auto addr: addrs) {
csr_rd_cb[addr] = &this_class::read_reg; csr_rd_cb[addr] = &this_class::read_reg;
csr_wr_cb[addr] = &this_class::write_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_rd_cb[ustatus] = &this_class::read_status;
csr_wr_cb[ustatus] = &this_class::write_status; csr_wr_cb[ustatus] = &this_class::write_status;
csr_wr_cb[ucause] = &this_class::write_cause; 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_rd_cb[mip] = &this_class::read_ip;
csr_wr_cb[mip] = &this_class::write_ip; csr_wr_cb[mip] = &this_class::write_ip;
csr_rd_cb[uip] = &this_class::read_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[mhartid] = &this_class::read_hartid;
csr_rd_cb[mcounteren] = &this_class::read_null; csr_rd_cb[mcounteren] = &this_class::read_null;
csr_wr_cb[mcounteren] = &this_class::write_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[misa] = &this_class::write_null;
csr_wr_cb[mvendorid] = &this_class::write_null; csr_wr_cb[mvendorid] = &this_class::write_null;
csr_wr_cb[marchid] = &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_rd_cb[mintthresh] = &this_class::read_reg;
csr_wr_cb[mintthresh] = &this_class::write_reg; csr_wr_cb[mintthresh] = &this_class::write_reg;
csr_rd_cb[mclicbase] = &this_class::read_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"); 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> 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, 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) { 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 { try {
switch (space) { switch (space) {
case traits<BASE>::MEM: { 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)) { if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) {
fault_data = addr; fault_data = addr;
if (access && iss::access_type::DEBUG) throw trap_access(0, 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 { try {
switch (space) { switch (space) {
case traits<BASE>::MEM: { 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)) { if (unlikely((access && iss::access_type::FETCH) && (addr & 0x1) == 1)) {
fault_data = addr; fault_data = addr;
if (access && iss::access_type::DEBUG) throw trap_access(0, 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; return iss::Ok;
} }
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_mtvec(unsigned addr, reg_t &val) { template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_tvec(unsigned addr, reg_t &val) {
val = csr[mtvec] & ~2; val = csr[addr] & ~2;
return iss::Ok; return iss::Ok;
} }
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_status(unsigned addr, reg_t &val) { 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; return iss::Ok;
} }
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_mepc(unsigned addr, reg_t val) { template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_epc(unsigned addr, reg_t val) {
auto mask = get_pc_mask(); csr[addr] = val & get_pc_mask();
csr[addr] = val;//(csr[addr] & ~mask) | (val & mask);
return iss::Ok; 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) { 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] // flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val // 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 trap_id = bit_sub<0, 16>(flags);
auto cause = bit_sub<16, 15>(flags); auto cause = bit_sub<16, 15>(flags);
if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause if (trap_id == 0 && cause == 11) cause = 0x8 + this->reg.PRIV; // adjust environment call cause