fix MPP reset value, PMP inactive in U-mode handling and MRET in U-mode

This commit is contained in:
Eyck Jentzsch 2021-09-21 16:52:40 +02:00
parent 65b4db5eca
commit ba9339a50d
2 changed files with 41 additions and 35 deletions

View File

@ -145,7 +145,7 @@ public:
mstatus_t mstatus; mstatus_t mstatus;
static const reg_t mstatus_reset_val = 0; static const reg_t mstatus_reset_val = 0x1800;
void write_mstatus(T val, unsigned priv_lvl) { void write_mstatus(T val, unsigned priv_lvl) {
auto mask = get_mask(priv_lvl); auto mask = get_mask(priv_lvl);
@ -398,7 +398,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);
iss::status read_satp(unsigned addr, reg_t &val); iss::status read_satp(unsigned addr, reg_t &val);
iss::status write_satp(unsigned addr, reg_t val); iss::status write_satp(unsigned addr, reg_t val);
iss::status read_fcsr(unsigned addr, reg_t &val); iss::status read_fcsr(unsigned addr, reg_t &val);
@ -954,8 +954,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_epc(unsigned addr, reg_t val) { template <typename BASE> iss::status riscv_hart_msu_vp<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

@ -146,7 +146,7 @@ public:
mstatus_t mstatus; mstatus_t mstatus;
static const reg_t mstatus_reset_val = 0; static const reg_t mstatus_reset_val = 0x1800; // MPP set to 1
void write_mstatus(T val, unsigned priv_lvl) { void write_mstatus(T val, unsigned priv_lvl) {
auto mask = get_mask(priv_lvl); auto mask = get_mask(priv_lvl);
@ -543,25 +543,27 @@ template <typename BASE, features_e FEAT> bool riscv_hart_mu_p<BASE, FEAT>::pmp_
constexpr auto PMP_NA4 =0x2U; constexpr auto PMP_NA4 =0x2U;
constexpr auto PMP_NAPOT =0x3U; constexpr auto PMP_NAPOT =0x3U;
reg_t base = 0; reg_t base = 0;
auto any_active = false;
for (size_t i = 0; i < 16; i++) { for (size_t i = 0; i < 16; i++) {
reg_t tor = csr[pmpaddr0+i] << PMP_SHIFT; reg_t tor = csr[pmpaddr0+i] << PMP_SHIFT;
uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4); uint8_t cfg = csr[pmpcfg0+(i/4)]>>(i%4);
if (cfg & PMP_A) { if (cfg & PMP_A) {
any_active=true;
auto pmp_a = (cfg & PMP_A) >> 3; auto pmp_a = (cfg & PMP_A) >> 3;
bool is_tor = pmp_a == PMP_TOR; auto is_tor = pmp_a == PMP_TOR;
bool is_na4 = pmp_a == PMP_NA4; auto is_na4 = pmp_a == PMP_NA4;
reg_t mask = (csr[pmpaddr0+i] << 1) | (!is_na4); reg_t mask = (csr[pmpaddr0+i] << 1) | (!is_na4);
mask = ~(mask & ~(mask + 1)) << PMP_SHIFT; mask = ~(mask & ~(mask + 1)) << PMP_SHIFT;
// Check each 4-byte sector of the access // Check each 4-byte sector of the access
bool any_match = false; auto any_match = false;
bool all_match = true; auto all_match = true;
for (reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) { for (reg_t offset = 0; offset < len; offset += 1 << PMP_SHIFT) {
reg_t cur_addr = addr + offset; reg_t cur_addr = addr + offset;
bool napot_match = ((cur_addr ^ tor) & mask) == 0; auto napot_match = ((cur_addr ^ tor) & mask) == 0;
bool tor_match = base <= cur_addr && cur_addr < tor; auto tor_match = base <= cur_addr && cur_addr < tor;
bool match = is_tor ? tor_match : napot_match; auto match = is_tor ? tor_match : napot_match;
any_match |= match; any_match |= match;
all_match &= match; all_match &= match;
} }
@ -577,7 +579,7 @@ template <typename BASE, features_e FEAT> bool riscv_hart_mu_p<BASE, FEAT>::pmp_
} }
base = tor; base = tor;
} }
return this->reg.PRIV == PRIV_M; return !any_active || this->reg.PRIV == PRIV_M;
} }
@ -926,8 +928,6 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ie(unsigned addr, reg_t val) { template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_ie(unsigned addr, reg_t val) {
auto mask = get_irq_wrmask((addr >> 8) & 0x3); auto mask = get_irq_wrmask((addr >> 8) & 0x3);
if(this->reg.PRIV==0)
mask&= ~(0xff<<4); // STIE and UTIE are read only in user and supervisor mode
csr[mie] = (csr[mie] & ~mask) | (val & mask); csr[mie] = (csr[mie] & ~mask) | (val & mask);
check_interrupt(); check_interrupt();
return iss::Ok; return iss::Ok;
@ -1256,27 +1256,33 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::leave_trap(uint64_t flags) { template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::leave_trap(uint64_t flags) {
auto cur_priv = this->reg.PRIV; auto cur_priv = this->reg.PRIV;
auto inst_priv = (flags & 0x3)? 3:0; auto inst_priv = (flags & 0x3)? 3:0;
auto status = state.mstatus; if(inst_priv>cur_priv){
// pop the relevant lower-privilege interrupt enable and privilege mode stack auto trap_val = 0x80ULL << 24 | (2 << 16); // illegal instruction
// clear respective yIE this->reg.trap_state = trap_val;
switch (inst_priv) { this->reg.NEXT_PC = std::numeric_limits<uint32_t>::max();
case PRIV_M: } else {
this->reg.PRIV = state.mstatus.MPP; auto status = state.mstatus;
state.mstatus.MPP = 0; // clear mpp to U mode // pop the relevant lower-privilege interrupt enable and privilege mode stack
state.mstatus.MIE = state.mstatus.MPIE; // clear respective yIE
state.mstatus.MPIE = 1; switch (inst_priv) {
break; case PRIV_M:
case PRIV_U: this->reg.PRIV = state.mstatus.MPP;
this->reg.PRIV = 0; state.mstatus.MPP = 0; // clear mpp to U mode
state.mstatus.UIE = state.mstatus.UPIE; state.mstatus.MIE = state.mstatus.MPIE;
state.mstatus.UPIE = 1; state.mstatus.MPIE = 1;
break; break;
case PRIV_U:
this->reg.PRIV = 0;
state.mstatus.UIE = state.mstatus.UPIE;
state.mstatus.UPIE = 1;
break;
}
// sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to "
<< lvl[this->reg.PRIV];
check_interrupt();
} }
// sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to "
<< lvl[this->reg.PRIV];
check_interrupt();
return this->reg.NEXT_PC; return this->reg.NEXT_PC;
} }