fix privilege behavior

This commit is contained in:
Eyck Jentzsch 2021-07-07 11:30:00 +02:00
parent 2f4b5bd9b2
commit 473f8a5a17
3 changed files with 322 additions and 86 deletions

View File

@ -176,6 +176,10 @@ public:
return 0b100010001000; // only machine mode is supported
}
constexpr reg_t get_pc_mask() {
return traits<BASE>::MISA_VAL&0b0100?~1:~3;
}
riscv_hart_m_p();
virtual ~riscv_hart_m_p() = default;
@ -285,6 +289,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);
reg_t mhartid_reg{0x0};
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
@ -317,13 +322,12 @@ riscv_hart_m_p<BASE>::riscv_hart_m_p()
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = cycle; addr <= hpmcounter31; ++addr){
for (unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = cycleh; addr <= hpmcounter31h; ++addr){
for (unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
//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}};
@ -333,9 +337,12 @@ riscv_hart_m_p<BASE>::riscv_hart_m_p()
}
// special handling & overrides
csr_rd_cb[time] = &this_class::read_time;
csr_wr_cb[time] = nullptr;
csr_rd_cb[timeh] = &this_class::read_time;
csr_wr_cb[timeh] = nullptr;
csr_rd_cb[cycle] = &this_class::read_cycle;
csr_rd_cb[cycleh] = &this_class::read_cycle;
csr_rd_cb[instret] = &this_class::read_instret;
csr_rd_cb[instreth] = &this_class::read_instret;
csr_rd_cb[mcycle] = &this_class::read_cycle;
csr_wr_cb[mcycle] = &this_class::write_cycle;
csr_rd_cb[mcycleh] = &this_class::read_cycle;
@ -354,6 +361,7 @@ riscv_hart_m_p<BASE>::riscv_hart_m_p()
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;
@ -656,9 +664,9 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned addr, reg_t &val) {
if (addr == minstret) {
if ((addr&0xff) == (minstret&0xff)) {
val = static_cast<reg_t>(this->reg.instret);
} else if (addr == minstreth) {
} else if ((addr&0xff) == (minstreth&0xff)) {
if (sizeof(typename traits<BASE>::reg_t) != 4) return iss::Err;
val = static_cast<reg_t>(this->reg.instret >> 32);
}
@ -667,11 +675,11 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if (addr == minstreth)
if ((addr&0xff) == (minstreth&0xff))
return iss::Err;
this->reg.instret = static_cast<uint64_t>(val);
} else {
if (addr == minstret) {
if ((addr&0xff) == (minstret&0xff)) {
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
} else {
this->reg.instret = (static_cast<uint64_t>(val)<<32) + (this->reg.instret & 0xffffffff);
@ -710,7 +718,6 @@ 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];
//val &= csr[mideleg];
return iss::Ok;
}
@ -728,7 +735,6 @@ 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];
//val &= csr[mideleg];
return iss::Ok;
}
@ -740,6 +746,12 @@ 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) {
auto mask = get_pc_mask();
csr[addr] = val;//(csr[addr] & ~mask) | (val & mask);
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(mem_read_cb) return mem_read_cb(paddr, length, data);
@ -798,9 +810,10 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
// tohost handling in case of riscv-test
if (paddr.access && iss::access_type::FUNC) {
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4));
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4)) ||
(traits<BASE>::XLEN == 64 && paddr.val == tohost);
auto tohost_lower =
(traits<BASE>::XLEN == 32 && paddr.val == tohost);
(traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
if (tohost_lower || tohost_upper) {
uint64_t hostvar = *reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask));
if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
@ -831,7 +844,8 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
}
} else if (tohost_lower)
to_host_wr_cnt++;
} else if (traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) {
} else if ((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) ||
(traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t *>(p.data() + (fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
}
@ -856,7 +870,7 @@ template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
bool mie = state.mstatus.MIE;
auto m_enabled = this->reg.PRIV < PRIV_M || (this->reg.PRIV == PRIV_M && mie);
auto enabled_interrupts = m_enabled ? ena_irq /*& ~ideleg*/ : 0;
auto enabled_interrupts = m_enabled ? ena_irq : 0;
if (enabled_interrupts != 0) {
int res = 0;
@ -918,7 +932,7 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flag
state.mstatus.MIE = state.mstatus.MPIE;
state.mstatus.MPIE = 1;
// sets the pc to the value stored in the x epc register.
this->reg.NEXT_PC = csr[mepc];
this->reg.NEXT_PC = csr[mepc] & get_pc_mask();
CLOG(INFO, disass) << "Executing xRET";
check_interrupt();
return this->reg.NEXT_PC;

View File

@ -356,7 +356,10 @@ protected:
virtual iss::status write_csr(unsigned addr, reg_t val);
hart_state_type state;
uint64_t cycle_offset;
int64_t cycle_offset{0};
uint64_t mcycle_csr{0};
int64_t instret_offset{0};
uint64_t minstret_csr{0};
reg_t fault_data;
std::array<vm_info, 2> vm;
uint64_t tohost = tohost_dflt;
@ -377,10 +380,15 @@ protected:
std::unordered_map<unsigned, wr_csr_f> csr_wr_cb;
private:
iss::status read_null(unsigned addr, reg_t &val);
iss::status read_reg(unsigned addr, reg_t &val);
iss::status write_reg(unsigned addr, reg_t val);
iss::status read_null(unsigned addr, reg_t &val);
iss::status write_null(unsigned addr, reg_t val){return iss::status::Ok;}
iss::status read_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 write_instret(unsigned addr, reg_t val);
iss::status read_mtvec(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);
@ -389,6 +397,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 read_satp(unsigned addr, reg_t &val);
iss::status write_satp(unsigned addr, reg_t val);
iss::status read_fcsr(unsigned addr, reg_t &val);
@ -405,15 +414,19 @@ protected:
template <typename BASE>
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
: state()
, cycle_offset(0)
, instr_if(*this) {
csr[misa] = hart_state_type::get_misa();
// reset values
csr[misa] = traits<BASE>::MISA_VAL;
csr[mvendorid] = 0x669;
csr[marchid] = 0x80000003;
csr[mimpid] = 1;
uart_buf.str("");
for (unsigned addr = mcycle; addr <= mhpmcounter31; ++addr){
for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = mcycleh; addr <= mhpmcounter31h; ++addr){
for (unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
@ -421,23 +434,35 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = cycle; addr <= hpmcounter31; ++addr){
for (unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = cycleh; addr <= hpmcounter31h; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
//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}};
for(auto addr: addrs) {
csr_rd_cb[addr] = &this_class::read_reg;
csr_wr_cb[addr] = &this_class::write_reg;
}
// special handling
// special handling & overrides
csr_rd_cb[time] = &this_class::read_time;
csr_wr_cb[time] = nullptr;
csr_rd_cb[timeh] = &this_class::read_time;
csr_wr_cb[timeh] = nullptr;
csr_rd_cb[cycle] = &this_class::read_cycle;
csr_rd_cb[cycleh] = &this_class::read_cycle;
csr_rd_cb[instret] = &this_class::read_instret;
csr_rd_cb[instreth] = &this_class::read_instret;
csr_rd_cb[mcycle] = &this_class::read_cycle;
csr_wr_cb[mcycle] = &this_class::write_cycle;
csr_rd_cb[mcycleh] = &this_class::read_cycle;
csr_rd_cb[minstret] = &this_class::read_cycle;
csr_rd_cb[minstreth] = &this_class::read_cycle;
csr_wr_cb[mcycleh] = &this_class::write_cycle;
csr_rd_cb[minstret] = &this_class::read_instret;
csr_wr_cb[minstret] = &this_class::write_instret;
csr_rd_cb[minstreth] = &this_class::read_instret;
csr_wr_cb[minstreth] = &this_class::write_instret;
csr_rd_cb[mstatus] = &this_class::read_status;
csr_wr_cb[mstatus] = &this_class::write_status;
csr_rd_cb[sstatus] = &this_class::read_status;
@ -457,16 +482,14 @@ riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
csr_rd_cb[uie] = &this_class::read_ie;
csr_wr_cb[uie] = &this_class::write_ie;
csr_rd_cb[mhartid] = &this_class::read_hartid;
// common regs
const std::array<unsigned, 6> addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}};
for(auto addr: addrs) {
csr_rd_cb[addr] = &this_class::read_reg;
csr_wr_cb[addr] = &this_class::write_reg;
}
// read-only registers
csr_rd_cb[misa] = &this_class::read_reg;
csr_wr_cb[misa] = nullptr;
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;
csr_wr_cb[mimpid] = &this_class::write_null;
csr_rd_cb[satp] = &this_class::read_satp;
csr_wr_cb[satp] = &this_class::write_satp;
csr_rd_cb[fcsr] = &this_class::read_fcsr;
@ -557,10 +580,14 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
auto res = type==iss::address_type::PHYSICAL?
read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data):
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
if (unlikely(res != iss::Ok)){
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
fault_data=addr;
}
return res;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
} break;
@ -597,6 +624,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_
return iss::Ok;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
}
@ -651,11 +679,14 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
auto res = type==iss::address_type::PHYSICAL?
write_mem(phys_addr_t{access, space, addr}, length, data):
write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
if (unlikely(res != iss::Ok))
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault)
if (unlikely(res != iss::Ok)) {
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
fault_data=addr;
}
return res;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
@ -720,6 +751,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const address_type type, const access
return iss::Ok;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
}
@ -774,8 +806,50 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_cycle(unsigned a
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_time(unsigned addr, reg_t &val) {
uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052;
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if (addr == mcycleh)
return iss::Err;
mcycle_csr = static_cast<uint64_t>(val);
} else {
if (addr == mcycle) {
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
} else {
mcycle_csr = (static_cast<uint64_t>(val)<<32) + (mcycle_csr & 0xffffffff);
}
}
cycle_offset = mcycle_csr-this->reg.icount; // TODO: relying on wrap-around
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned addr, reg_t &val) {
if ((addr&0xff) == (minstret&0xff)) {
val = static_cast<reg_t>(this->reg.instret);
} else if ((addr&0xff) == (minstreth&0xff)) {
if (sizeof(typename traits<BASE>::reg_t) != 4) return iss::Err;
val = static_cast<reg_t>(this->reg.instret >> 32);
}
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if ((addr&0xff) == (minstreth&0xff))
return iss::Err;
this->reg.instret = static_cast<uint64_t>(val);
} else {
if ((addr&0xff) == (minstret&0xff)) {
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
} else {
this->reg.instret = (static_cast<uint64_t>(val)<<32) + (this->reg.instret & 0xffffffff);
}
}
this->reg.instret--;
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned addr, reg_t &val) {
uint64_t time_val = this->reg.icount / (100000000 / 32768 - 1); //-> ~3052;
if (addr == time) {
val = static_cast<reg_t>(time_val);
} else if (addr == timeh) {
@ -785,6 +859,11 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_time(unsigned
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_mtvec(unsigned addr, reg_t &val) {
val = csr[mtvec] & ~2;
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t &val) {
auto req_priv_lvl = (addr >> 8) & 0x3;
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
@ -835,6 +914,12 @@ 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);
return iss::Ok;
}
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
reg_t tvm = state.mstatus.TVM;
if (this->reg.PRIV == PRIV_S & tvm != 0) {
@ -918,7 +1003,6 @@ iss::status riscv_hart_msu_vp<BASE>::read_mem(phys_addr_t paddr, unsigned length
template <typename BASE>
iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
if ((paddr.val + length) > mem.size()) return iss::Err;
if(mem_write_cb) return mem_write_cb(paddr, length, data);
switch (paddr.val) {
case 0x10013000: // UART0 base, TXFIFO reg
@ -1153,7 +1237,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
* access, or page-fault exception occurs, mtval is written with the
* faulting effective address.
*/
csr[utval | (new_priv << 8)] = cause==2?instr : fault_data;
csr[utval | (new_priv << 8)] = cause==2?((instr & 0x3)==3?instr:instr&0xffff):fault_data;
fault_data = 0;
} else {
if (cur_priv != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)
@ -1193,7 +1277,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
auto ivec = csr[utvec | (new_priv << 8)];
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
// bits in mtvec
this->reg.NEXT_PC = ivec & ~0x1UL;
this->reg.NEXT_PC = ivec & ~0x3UL;
if ((ivec & 0x1) == 1 && trap_id != 0) this->reg.NEXT_PC += 4 * cause;
// reset trap state
this->reg.PRIV = new_priv;

View File

@ -154,15 +154,29 @@ public:
mstatus = new_val;
}
T satp;
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
default: return 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
case PRIV_U: return 0x00000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
default:
// +-SD
// | +-TSR
// | |+-TW
// | ||+-TVM
// | |||+-MXR
// | ||||+-SUM
// | |||||+-MPRV
// | |||||| +-XS
// | |||||| | +-FS
// | |||||| | | +-MPP
// | |||||| | | | +-SPP
// | |||||| | | | |+-MPIE
// | |||||| | | | || +-UPIE
// | ||||||/|/|/| || |+-MIE
// | ||||||/|/|/| || || +-UIE
return 0b00000000000000000001100010011001;
}
#endif
}
@ -172,13 +186,17 @@ public:
constexpr reg_t get_irq_mask(size_t mode) {
std::array<const reg_t, 4> m = {{
0b000100010001, // U mode
0b001100110011, // S mode
0, // S mode
0,
0b101110111011 // M mode
0b100110011001 // M mode
}};
return m[mode];
}
constexpr reg_t get_pc_mask() {
return traits<BASE>::MISA_VAL&0b0100?~1:~3;
}
riscv_hart_mu_p();
virtual ~riscv_hart_mu_p() = default;
@ -200,7 +218,7 @@ public:
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.PRIV], (reg_t)state.mstatus, this->reg.ccount);
pc, instr, lvl[this->reg.PRIV], (reg_t)state.mstatus, this->reg.icount);
};
iss::instrumentation_if *get_instrumentation_if() override { return &instr_if; }
@ -249,7 +267,10 @@ protected:
virtual iss::status write_csr(unsigned addr, reg_t val);
hart_state_type state;
uint64_t cycle_offset;
int64_t cycle_offset{0};
uint64_t mcycle_csr{0};
int64_t instret_offset{0};
uint64_t minstret_csr{0};
reg_t fault_data;
uint64_t tohost = tohost_dflt;
uint64_t fromhost = fromhost_dflt;
@ -270,7 +291,13 @@ protected:
private:
iss::status read_reg(unsigned addr, reg_t &val);
iss::status write_reg(unsigned addr, reg_t val);
iss::status read_null(unsigned addr, reg_t &val);
iss::status write_null(unsigned addr, reg_t val){return iss::status::Ok;}
iss::status read_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 write_instret(unsigned addr, reg_t val);
iss::status read_mtvec(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);
@ -279,6 +306,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);
reg_t mhartid_reg{0x0};
std::function<iss::status(phys_addr_t, unsigned, uint8_t *const)>mem_read_cb;
@ -291,21 +319,55 @@ protected:
template <typename BASE, features_e FEAT>
riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p()
: state()
, cycle_offset(0)
, instr_if(*this) {
// reset values
csr[misa] = traits<BASE>::MISA_VAL;
csr[mvendorid] = 0x669;
csr[marchid] = 0x80000003;
csr[mimpid] = 1;
uart_buf.str("");
for (unsigned addr = mcycle; addr <= hpmcounter31; ++addr) csr_wr_cb[addr] = nullptr;
for (unsigned addr = mcycleh; addr <= hpmcounter31h; ++addr) csr_wr_cb[addr] = nullptr;
// special handling
for (unsigned addr = mhpmcounter3; addr <= mhpmcounter31; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = mhpmcounter3h; addr <= mhpmcounter31h; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = mhpmevent3; addr <= mhpmevent31; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
csr_wr_cb[addr] = &this_class::write_reg;
}
for (unsigned addr = hpmcounter3; addr <= hpmcounter31; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
}
for (unsigned addr = hpmcounter3h; addr <= hpmcounter31h; ++addr){
csr_rd_cb[addr] = &this_class::read_null;
//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}};
for(auto addr: addrs) {
csr_rd_cb[addr] = &this_class::read_reg;
csr_wr_cb[addr] = &this_class::write_reg;
}
// special handling & overrides
csr_rd_cb[time] = &this_class::read_time;
csr_wr_cb[time] = nullptr;
csr_rd_cb[timeh] = &this_class::read_time;
csr_wr_cb[timeh] = nullptr;
csr_rd_cb[cycle] = &this_class::read_cycle;
csr_rd_cb[cycleh] = &this_class::read_cycle;
csr_rd_cb[instret] = &this_class::read_instret;
csr_rd_cb[instreth] = &this_class::read_instret;
csr_rd_cb[mcycle] = &this_class::read_cycle;
csr_wr_cb[mcycle] = &this_class::write_cycle;
csr_rd_cb[mcycleh] = &this_class::read_cycle;
csr_rd_cb[minstret] = &this_class::read_cycle;
csr_rd_cb[minstreth] = &this_class::read_cycle;
csr_wr_cb[mcycleh] = &this_class::write_cycle;
csr_rd_cb[minstret] = &this_class::read_instret;
csr_wr_cb[minstret] = &this_class::write_instret;
csr_rd_cb[minstreth] = &this_class::read_instret;
csr_wr_cb[minstreth] = &this_class::write_instret;
csr_rd_cb[mstatus] = &this_class::read_status;
csr_wr_cb[mstatus] = &this_class::write_status;
csr_rd_cb[ustatus] = &this_class::read_status;
@ -319,15 +381,14 @@ riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p()
csr_rd_cb[uie] = &this_class::read_ie;
csr_wr_cb[uie] = &this_class::write_ie;
csr_rd_cb[mhartid] = &this_class::read_hartid;
// common regs
const std::array<unsigned, 6> addrs{{mepc, mtvec, mscratch, mcause, mtval, mscratch}};
for(auto addr: addrs) {
csr_rd_cb[addr] = &this_class::read_reg;
csr_wr_cb[addr] = &this_class::write_reg;
}
// read-only registers
csr_rd_cb[misa] = &this_class::read_reg;
csr_wr_cb[misa] = nullptr;
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;
csr_wr_cb[mimpid] = &this_class::write_null;
if(FEAT & FEAT_PMP){
for(size_t i=pmpaddr0; i<=pmpaddr15; ++i){
@ -413,13 +474,23 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
return iss::Err;
}
try {
auto alignment = access == iss::access_type::FETCH? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length;
if(alignment>1 && (addr&(alignment-1))){
this->reg.trap_state = 1<<31 | 4<<16;
fault_data=addr;
return iss::Err;
}
auto res = type==iss::address_type::PHYSICAL?
read_mem( BASE::v2p(phys_addr_t{access, space, addr}), length, data):
read_mem( BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
if (unlikely(res != iss::Ok)) this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
if (unlikely(res != iss::Ok)){
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 5 (load access fault
fault_data=addr;
}
return res;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
} break;
@ -445,6 +516,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
return iss::Ok;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
}
@ -485,14 +557,22 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
return iss::Err;
}
try {
if(length>1 && (addr&(length-1))){
this->reg.trap_state = 1<<31 | 6<<16;
fault_data=addr;
return iss::Err;
}
auto res = type==iss::address_type::PHYSICAL?
write_mem(phys_addr_t{access, space, addr}, length, data):
write_mem(BASE::v2p(iss::addr_t{access, type, space, addr}), length, data);
if (unlikely(res != iss::Ok))
this->reg.trap_state = (1 << 31) | (5 << 16); // issue trap 7 (Store/AMO access fault)
if (unlikely(res != iss::Ok)) {
this->reg.trap_state = (1 << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)
fault_data=addr;
}
return res;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
@ -552,6 +632,7 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
return iss::Ok;
} catch (trap_access &ta) {
this->reg.trap_state = (1 << 31) | ta.id;
fault_data=ta.addr;
return iss::Err;
}
}
@ -585,6 +666,11 @@ 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_m_p<BASE, FEAT>::read_null(unsigned addr, reg_t &val) {
val = 0;
return iss::Ok;
}
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::write_reg(unsigned addr, reg_t val) {
csr[addr] = val;
return iss::Ok;
@ -601,8 +687,50 @@ 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_m_p<BASE, FEAT>::write_cycle(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if (addr == mcycleh)
return iss::Err;
mcycle_csr = static_cast<uint64_t>(val);
} else {
if (addr == mcycle) {
mcycle_csr = (mcycle_csr & 0xffffffff00000000) + val;
} else {
mcycle_csr = (static_cast<uint64_t>(val)<<32) + (mcycle_csr & 0xffffffff);
}
}
cycle_offset = mcycle_csr-this->reg.icount; // TODO: relying on wrap-around
return iss::Ok;
}
template <typename BASE, features_e FEAT> iss::status riscv_hart_m_p<BASE, FEAT>::read_instret(unsigned addr, reg_t &val) {
if ((addr&0xff) == (minstret&0xff)) {
val = static_cast<reg_t>(this->reg.instret);
} else if ((addr&0xff) == (minstreth&0xff)) {
if (sizeof(typename traits<BASE>::reg_t) != 4) return iss::Err;
val = static_cast<reg_t>(this->reg.instret >> 32);
}
return iss::Ok;
}
template <typename BASE, features_e FEAT> iss::status riscv_hart_m_p<BASE, FEAT>::write_instret(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) {
if ((addr&0xff) == (minstreth&0xff))
return iss::Err;
this->reg.instret = static_cast<uint64_t>(val);
} else {
if ((addr&0xff) == (minstret&0xff)) {
this->reg.instret = (this->reg.instret & 0xffffffff00000000) + val;
} else {
this->reg.instret = (static_cast<uint64_t>(val)<<32) + (this->reg.instret & 0xffffffff);
}
}
this->reg.instret--;
return iss::Ok;
}
template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT>::read_time(unsigned addr, reg_t &val) {
uint64_t time_val = (this->reg.icount + cycle_offset) / (100000000 / 32768 - 1); //-> ~3052;
uint64_t time_val = this->reg.icount / (100000000 / 32768 - 1); //-> ~3052;
if (addr == time) {
val = static_cast<reg_t>(time_val);
} else if (addr == timeh) {
@ -612,6 +740,10 @@ template <typename BASE, features_e FEAT> iss::status riscv_hart_mu_p<BASE, FEAT
return iss::Ok;
}
template <typename BASEE, features_e FEAT> iss::status riscv_hart_m_p<BASE, FEAT>::read_mtvec(unsigned addr, reg_t &val) {
val = csr[mtvec] & ~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) {
auto req_priv_lvl = (addr >> 8) & 0x3;
val = state.mstatus & hart_state_type::get_mask(req_priv_lvl);
@ -659,9 +791,14 @@ 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_m_p<BASE, FEAT>::write_mepc(unsigned addr, reg_t val) {
auto mask = get_pc_mask();
csr[addr] = val;//(csr[addr] & ~mask) | (val & mask);
return iss::Ok;
}
template <typename BASE, features_e FEAT>
iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned length, uint8_t *const data) {
if ((paddr.val + length) > mem.size()) return iss::Err;
if(mem_read_cb) return mem_read_cb(paddr, length, data);
switch (paddr.val) {
case 0x0200BFF8: { // CLINT base, mtime reg
@ -677,9 +814,9 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned le
if (this->reg.icount > 30000) data[3] |= 0x80;
} break;
default: {
const auto &p = mem(paddr.val / mem.page_size);
auto offs = paddr.val & mem.page_addr_mask;
std::copy(p.data() + offs, p.data() + offs + length, data);
for(auto offs=0U; offs<length; ++offs) {
*(data + offs)=mem[(paddr.val+offs)%mem.size()];
}
}
}
return iss::Ok;
@ -687,7 +824,6 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read_mem(phys_addr_t paddr, unsigned le
template <typename BASE, features_e FEAT>
iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned length, const uint8_t *const data) {
if ((paddr.val + length) > mem.size()) return iss::Err;
if(mem_write_cb) return mem_write_cb(paddr, length, data);
switch (paddr.val) {
case 0x10013000: // UART0 base, TXFIFO reg
@ -719,9 +855,10 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
std::copy(data, data + length, p.data() + (paddr.val & mem.page_addr_mask));
// tohost handling in case of riscv-test
if (paddr.access && iss::access_type::FUNC) {
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4));
auto tohost_upper = (traits<BASE>::XLEN == 32 && paddr.val == (tohost + 4)) ||
(traits<BASE>::XLEN == 64 && paddr.val == tohost);
auto tohost_lower =
(traits<BASE>::XLEN == 32 && paddr.val == tohost);
(traits<BASE>::XLEN == 32 && paddr.val == tohost) || (traits<BASE>::XLEN == 64 && paddr.val == tohost);
if (tohost_lower || tohost_upper) {
uint64_t hostvar = *reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask));
if (tohost_upper || (tohost_lower && to_host_wr_cnt > 0)) {
@ -752,7 +889,8 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write_mem(phys_addr_t paddr, unsigned l
}
} else if (tohost_lower)
to_host_wr_cnt++;
} else if (traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) {
} else if ((traits<BASE>::XLEN == 32 && paddr.val == fromhost + 4) ||
(traits<BASE>::XLEN == 64 && paddr.val == fromhost)) {
uint64_t fhostvar = *reinterpret_cast<uint64_t *>(p.data() + (fromhost & mem.page_addr_mask));
*reinterpret_cast<uint64_t *>(p.data() + (tohost & mem.page_addr_mask)) = fhostvar;
}
@ -809,7 +947,7 @@ template <typename BASE, features_e FEAT> uint64_t riscv_hart_mu_p<BASE, FEAT>::
* access, or page-fault exception occurs, mtval is written with the
* faulting effective address.
*/
csr[utval | (new_priv << 8)] = cause==2?instr:fault_data;
csr[utval | (new_priv << 8)] = cause==2?((instr & 0x3)==3?instr:instr&0xffff):fault_data;
fault_data = 0;
} else {
if (this->reg.PRIV != PRIV_M && ((csr[mideleg] >> cause) & 0x1) != 0)