Strip down privileged modes. Only machine mode is supported

This commit is contained in:
Stanislaw Kaushanski 2020-09-07 11:54:45 +02:00
parent 969b408288
commit 6f3963a473

View File

@ -174,8 +174,6 @@ enum riscv_csr {
namespace {
std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
std::array<const char *, 16> trap_str = {{""
"Instruction address misaligned", // 0
"Instruction access fault", // 1
@ -326,8 +324,8 @@ public:
static const reg_t mstatus_reset_val = 0;
void write_mstatus(T val, unsigned priv_lvl) {
auto mask = get_mask(priv_lvl);
void write_mstatus(T val) {
auto mask = get_mask();
auto new_val = (mstatus.st.value & ~mask) | (val & mask);
mstatus = new_val;
}
@ -336,41 +334,19 @@ public:
static constexpr T get_misa() { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
static constexpr uint32_t get_mask(unsigned priv_lvl) {
#if __cplusplus < 201402L
return priv_lvl == PRIV_U ? 0x80000011UL : priv_lvl == PRIV_S ? 0x800de133UL : 0x807ff9ddUL;
#else
switch (priv_lvl) {
case PRIV_U: return 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
case PRIV_S: return 0x800de133UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011
default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
}
#endif
static constexpr uint32_t get_mask() {
return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011 // only machine mode is supported
}
static inline vm_info decode_vm_info(uint32_t state, T sptbr) {
if (state == PRIV_M) return {0, 0, 0, 0};
if (state <= PRIV_S)
switch (bit_sub<31, 1>(sptbr)) {
case 0: return {0, 0, 0, 0}; // off
case 1: return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT}; // SV32
default: abort();
}
abort();
return {0, 0, 0, 0}; // dummy
static inline vm_info decode_vm_info() {
return {0, 0, 0, 0};
}
};
const typename super::reg_t PGSIZE = 1 << PGSHIFT;
const typename super::reg_t PGMASK = PGSIZE - 1;
constexpr reg_t get_irq_mask(size_t mode) {
std::array<const reg_t, 4> m = {{
0b000100010001, // U mode
0b001100110011, // S mode
0,
0b101110111011 // M mode
}};
return m[mode];
constexpr reg_t get_irq_mask() {
return 0b101110111011; // only machine mode is supported
}
riscv_hart_m_p();
@ -396,8 +372,8 @@ public:
void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; };
void disass_output(uint64_t pc, const std::string instr) override {
CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [p:{};s:0x{:x};c:{}]",
pc, instr, lvl[this->reg.machine_state], (reg_t)state.mstatus, this->reg.icount);
CLOG(INFO, disass) << fmt::format("0x{:016x} {:40} [s:0x{:x};c:{}]",
pc, instr, (reg_t)state.mstatus, this->reg.icount);
};
iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; }
@ -497,22 +473,10 @@ riscv_hart_m_p<BASE>::riscv_hart_m_p()
csr_rd_cb[minstreth] = &riscv_hart_m_p<BASE>::read_cycle;
csr_rd_cb[mstatus] = &riscv_hart_m_p<BASE>::read_status;
csr_wr_cb[mstatus] = &riscv_hart_m_p<BASE>::write_status;
csr_rd_cb[sstatus] = &riscv_hart_m_p<BASE>::read_status;
csr_wr_cb[sstatus] = &riscv_hart_m_p<BASE>::write_status;
csr_rd_cb[ustatus] = &riscv_hart_m_p<BASE>::read_status;
csr_wr_cb[ustatus] = &riscv_hart_m_p<BASE>::write_status;
csr_rd_cb[mip] = &riscv_hart_m_p<BASE>::read_ip;
csr_wr_cb[mip] = &riscv_hart_m_p<BASE>::write_ip;
csr_rd_cb[sip] = &riscv_hart_m_p<BASE>::read_ip;
csr_wr_cb[sip] = &riscv_hart_m_p<BASE>::write_ip;
csr_rd_cb[uip] = &riscv_hart_m_p<BASE>::read_ip;
csr_wr_cb[uip] = &riscv_hart_m_p<BASE>::write_ip;
csr_rd_cb[mie] = &riscv_hart_m_p<BASE>::read_ie;
csr_wr_cb[mie] = &riscv_hart_m_p<BASE>::write_ie;
csr_rd_cb[sie] = &riscv_hart_m_p<BASE>::read_ie;
csr_wr_cb[sie] = &riscv_hart_m_p<BASE>::write_ie;
csr_rd_cb[uie] = &riscv_hart_m_p<BASE>::read_ie;
csr_wr_cb[uie] = &riscv_hart_m_p<BASE>::write_ie;
csr_rd_cb[mhartid] = &riscv_hart_m_p<BASE>::read_hartid;
}
@ -583,7 +547,7 @@ iss::status riscv_hart_m_p<BASE>::read(const address_type type, const access_typ
}
try {
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
vm_info vm = hart_state<reg_t>::decode_vm_info();
if (vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
@ -609,18 +573,7 @@ iss::status riscv_hart_m_p<BASE>::read(const address_type type, const access_typ
} break;
case traits<BASE>::FENCE: {
if ((addr + length) > mem.size()) return iss::Err;
switch (addr) {
case 2: // SFENCE:VMA lower
case 3: { // SFENCE:VMA upper
auto tvm = state.mstatus.TVM;
if (this->reg.machine_state == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
return iss::Ok;
}
}
return iss::Ok;
} break;
case traits<BASE>::RES: {
auto it = atomic_reservation.find(addr);
@ -677,7 +630,7 @@ iss::status riscv_hart_m_p<BASE>::write(const address_type type, const access_ty
}
try {
if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary
vm_info vm = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
vm_info vm = hart_state<reg_t>::decode_vm_info();
if (vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
@ -741,11 +694,6 @@ iss::status riscv_hart_m_p<BASE>::write(const address_type type, const access_ty
case 3: {
ptw.clear();
auto tvm = state.mstatus.TVM;
if (this->reg.machine_state == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
return iss::Ok;
}
}
@ -817,14 +765,12 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned addr, reg_t &val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
val = state.mstatus & hart_state<reg_t>::get_mask(req_priv_lvl);
val = state.mstatus & hart_state<reg_t>::get_mask();
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_status(unsigned addr, reg_t val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
state.write_mstatus(val, req_priv_lvl);
state.write_mstatus(val);
check_interrupt();
update_vm_info();
return iss::Ok;
@ -832,8 +778,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_status(unsigned
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_ie(unsigned addr, reg_t &val) {
val = csr[mie];
if (addr < mie) val &= csr[mideleg];
if (addr < sie) val &= csr[sideleg];
val &= csr[mideleg];
return iss::Ok;
}
@ -843,8 +788,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_hartid(unsigned
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ie(unsigned addr, reg_t val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
auto mask = get_irq_mask(req_priv_lvl);
auto mask = get_irq_mask();
csr[mie] = (csr[mie] & ~mask) | (val & mask);
check_interrupt();
return iss::Ok;
@ -852,76 +796,18 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ie(unsigned add
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_ip(unsigned addr, reg_t &val) {
val = csr[mip];
if (addr < mip) val &= csr[mideleg];
if (addr < sip) val &= csr[sideleg];
val &= csr[mideleg];
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_ip(unsigned addr, reg_t val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
auto mask = get_irq_mask(req_priv_lvl);
auto mask = get_irq_mask();
mask &= ~(1 << 7); // MTIP is read only
csr[mip] = (csr[mip] & ~mask) | (val & mask);
check_interrupt();
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_satp(unsigned addr, reg_t &val) {
reg_t tvm = state.mstatus.TVM;
if (this->reg.machine_state == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
val = state.satp;
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_satp(unsigned addr, reg_t val) {
reg_t tvm = state.mstatus.TVM;
if (this->reg.machine_state == PRIV_S & tvm != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return iss::Err;
}
state.satp = val;
update_vm_info();
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_fcsr(unsigned addr, reg_t &val) {
switch (addr) {
case 1: // fflags, 4:0
val = bit_sub<0, 5>(this->get_fcsr());
break;
case 2: // frm, 7:5
val = bit_sub<5, 3>(this->get_fcsr());
break;
case 3: // fcsr
val = this->get_fcsr();
break;
default:
return iss::Err;
}
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_fcsr(unsigned addr, reg_t val) {
switch (addr) {
case 1: // fflags, 4:0
this->set_fcsr((this->get_fcsr() & 0xffffffe0) | (val & 0x1f));
break;
case 2: // frm, 7:5
this->set_fcsr((this->get_fcsr() & 0xffffff1f) | ((val & 0x7) << 5));
break;
case 3: // fcsr
this->set_fcsr(val & 0xff);
break;
default:
return iss::Err;
}
return iss::Ok;
}
template <typename BASE>
iss::status riscv_hart_m_p<BASE>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
if ((paddr.val + length) > mem.size()) return iss::Err;
@ -1032,10 +918,10 @@ template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t addres
}
template <typename BASE> inline void riscv_hart_m_p<BASE>::update_vm_info() {
vm[1] = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
vm[1] = hart_state<reg_t>::decode_vm_info();
BASE::addr_mode[3]=BASE::addr_mode[2] = vm[1].is_active()? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
if (state.mstatus.MPRV)
vm[0] = hart_state<reg_t>::decode_vm_info(state.mstatus.MPP, state.satp);
vm[0] = hart_state<reg_t>::decode_vm_info();
else
vm[0] = vm[1];
BASE::addr_mode[1] = BASE::addr_mode[0]=vm[0].is_active() ? iss::address_type::VIRTUAL : iss::address_type::PHYSICAL;
@ -1057,11 +943,6 @@ template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
auto m_enabled = this->reg.machine_state < PRIV_M || (this->reg.machine_state == PRIV_M && mie);
auto enabled_interrupts = m_enabled ? ena_irq & ~ideleg : 0;
if (enabled_interrupts == 0) {
auto sie = state.mstatus.SIE;
auto s_enabled = this->reg.machine_state < PRIV_S || (this->reg.machine_state == PRIV_S && sie);
enabled_interrupts = s_enabled ? ena_irq & ideleg : 0;
}
if (enabled_interrupts != 0) {
int res = 0;
while ((enabled_interrupts & 1) == 0) enabled_interrupts >>= 1, res++;
@ -1094,7 +975,6 @@ typename riscv_hart_m_p<BASE>::phys_addr_t riscv_hart_m_p<BASE>::virt2phys(const
const vm_info &vm = this->vm[static_cast<uint16_t>(type) / 2];
const bool s_mode = mode == PRIV_S;
const bool sum = state.mstatus.SUM;
const bool mxr = state.mstatus.MXR;
@ -1118,7 +998,7 @@ typename riscv_hart_m_p<BASE>::phys_addr_t riscv_hart_m_p<BASE>::virt2phys(const
if (PTE_TABLE(pte)) { // next level of page table
base = ppn << PGSHIFT;
} else if ((pte & PTE_U) ? s_mode && (type == iss::access_type::FETCH || !sum) : !s_mode) {
} else if (!(pte & PTE_U)) {
break;
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
break;
@ -1163,36 +1043,22 @@ typename riscv_hart_m_p<BASE>::phys_addr_t riscv_hart_m_p<BASE>::virt2phys(const
}
template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
auto cur_priv = this->reg.machine_state;
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val
auto trap_id = bit_sub<0, 16>(flags);
auto cause = bit_sub<16, 15>(flags);
if (trap_id == 0 && cause == 11) cause = 0x8 + cur_priv; // adjust environment call cause
if (trap_id == 0 && cause == 11) cause = 0x8 + PRIV_M; // adjust environment call cause
// calculate effective privilege level
auto new_priv = PRIV_M;
if (trap_id == 0) { // exception
if (cur_priv != PRIV_M && ((csr[medeleg] >> cause) & 0x1) != 0)
new_priv = (csr[sedeleg] >> cause) & 0x1 ? PRIV_U : PRIV_S;
// store ret addr in xepc register
csr[uepc | (new_priv << 8)] = static_cast<reg_t>(addr); // store actual address instruction of exception
/*
* write mtval if new_priv=M_MODE, spec says:
* When a hardware breakpoint is triggered, or an instruction-fetch, load,
* or store address-misaligned,
* access, or page-fault exception occurs, mtval is written with the
* faulting effective address.
*/
csr[utval | (new_priv << 8)] = fault_data;
csr[mepc] = static_cast<reg_t>(addr); // store actual address instruction of exception
csr[mtval] = fault_data;
fault_data = 0;
} else {
if (cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
new_priv = (csr[sideleg] >> cause) & 0x1 ? PRIV_U : PRIV_S;
csr[uepc | (new_priv << 8)] = this->reg.NEXT_PC; // store next address if interrupt
csr[mepc] = this->reg.NEXT_PC; // store next address if interrupt
this->reg.pending_trap = 0;
}
size_t adr = ucause | (new_priv << 8);
csr[adr] = (trap_id << 31) + cause;
csr[mcause] = (trap_id << 31) + cause;
// update mstatus
// xPP field of mstatus is written with the active privilege mode at the time
// of the trap; the x PIE field of mstatus
@ -1200,41 +1066,25 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flag
// the trap; and the x IE field of mstatus
// is cleared
// store the actual privilege level in yPP and store interrupt enable flags
switch (new_priv) {
case PRIV_M:
state.mstatus.MPP = cur_priv;
state.mstatus.MPIE = state.mstatus.MIE;
state.mstatus.MIE = false;
break;
case PRIV_S:
state.mstatus.SPP = cur_priv;
state.mstatus.SPIE = state.mstatus.SIE;
state.mstatus.SIE = false;
break;
case PRIV_U:
state.mstatus.UPIE = state.mstatus.UIE;
state.mstatus.UIE = false;
break;
default:
break;
}
state.mstatus.MPP = PRIV_M;
state.mstatus.MPIE = state.mstatus.MIE;
state.mstatus.MIE = false;
// get trap vector
auto ivec = csr[utvec | (new_priv << 8)];
auto ivec = csr[mtvec];
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
// bits in mtvec
this->reg.NEXT_PC = ivec & ~0x1UL;
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
// reset trap state
this->reg.machine_state = new_priv;
this->reg.machine_state = PRIV_M;
this->reg.trap_state = 0;
std::array<char, 32> buffer;
sprintf(buffer.data(), "0x%016lx", addr);
if((flags&0xffffffff) != 0xffffffff)
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred, changing privilege level from "
<< lvl[cur_priv] << " to " << lvl[new_priv];
<< " at address " << buffer.data() << " occurred";
update_vm_info();
return this->reg.NEXT_PC;
}
@ -1244,47 +1094,24 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flag
auto inst_priv = flags & 0x3;
auto status = state.mstatus;
auto tsr = state.mstatus.TSR;
if (cur_priv == PRIV_S && inst_priv == PRIV_S && tsr != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
return this->reg.PC;
}
// pop the relevant lower-privilege interrupt enable and privilege mode stack
// clear respective yIE
switch (inst_priv) {
case PRIV_M:
if (inst_priv == PRIV_M) {
this->reg.machine_state = state.mstatus.MPP;
state.mstatus.MPP = 0; // clear mpp to U mode
state.mstatus.MIE = state.mstatus.MPIE;
break;
case PRIV_S:
this->reg.machine_state = state.mstatus.SPP;
state.mstatus.SPP = 0; // clear spp to U mode
state.mstatus.SIE = state.mstatus.SPIE;
break;
case PRIV_U:
this->reg.machine_state = 0;
state.mstatus.UIE = state.mstatus.UPIE;
break;
} else {
CLOG(ERROR, disass) << "Unsupported mode:" << inst_priv;
}
// 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.machine_state];
this->reg.NEXT_PC = csr[mepc];
CLOG(INFO, disass) << "Executing xRET";
update_vm_info();
return this->reg.NEXT_PC;
}
template <typename BASE> void riscv_hart_m_p<BASE>::wait_until(uint64_t flags) {
auto status = state.mstatus;
auto tw = status.TW;
if (this->reg.machine_state == PRIV_S && tw != 0) {
this->reg.trap_state = (1 << 31) | (2 << 16);
this->fault_data = this->reg.PC;
}
}
template <typename BASE> void riscv_hart_m_p<BASE>::wait_until(uint64_t flags) {}
} // namespace arch
} // namespace iss