Refactored hart vm implementation to use more structured description
This commit is contained in:
parent
d59dfa93f6
commit
768df67646
@ -45,6 +45,7 @@
|
||||
#include <unordered_map>
|
||||
#include <util/ities.h>
|
||||
#include <util/sparse_array.h>
|
||||
#include <util/bit_field.h>
|
||||
|
||||
namespace iss {
|
||||
namespace arch {
|
||||
@ -245,166 +246,8 @@ struct trap_store_page_fault : public trap_access {
|
||||
};
|
||||
}
|
||||
|
||||
using mstatus32_t = union {
|
||||
uint32_t val;
|
||||
struct /*mstatus*/ {
|
||||
uint32_t SD : 1, // SD bit is read-only and is set when either the FS or XS
|
||||
// bits encode a Dirty state (i.e., SD=((FS==11) OR
|
||||
// (XS==11)))
|
||||
_WPRI3 : 8, // unused
|
||||
TSR : 1, // Trap SRET
|
||||
TW : 1, // Timeout Wait
|
||||
TVM : 1, // Trap Virtual Memory
|
||||
MXR : 1, // Make eXecutable Readable
|
||||
SUM : 1, // permit Supervisor User Memory access
|
||||
MPRV : 1, // Modify PRiVilege
|
||||
XS : 2, // status of additional user-mode extensions and associated
|
||||
// state, All off/None dirty or clean, some on/None dirty, some
|
||||
// clean/Some dirty
|
||||
FS : 2, // floating-point unit status Off/Initial/Clean/Dirty
|
||||
MPP : 2, // machine previous privilege
|
||||
_WPRI2 : 2, // unused
|
||||
SPP : 1, // supervisor previous privilege
|
||||
MPIE : 1, // previous machine interrupt-enable
|
||||
_WPRI1 : 1, // unused
|
||||
SPIE : 1, // previous supervisor interrupt-enable
|
||||
UPIE : 1, // previous user interrupt-enable
|
||||
MIE : 1, // machine interrupt-enable
|
||||
_WPRI0 : 1, // unused
|
||||
SIE : 1, // supervisor interrupt-enable
|
||||
UIE : 1; // user interrupt-enable
|
||||
} m;
|
||||
struct /*sstatus*/ {
|
||||
uint32_t SD : 1, _WPRI4 : 11, MXR : 1, SUM : 1, _WPRI3 : 1, XS : 2, FS : 2, _WPRI2 : 4, SPP : 1, _WPRI1 : 2,
|
||||
SPIE : 1, UPIE : 1, _WPRI0 : 2, SIE : 1, UIE : 1;
|
||||
} s;
|
||||
struct /*ustatus*/ {
|
||||
uint32_t SD : 1, _WPRI4 : 11, MXR : 1, SUM : 1, _WPRI3 : 1, XS : 2, FS : 2, _WPRI2 : 8, UPIE : 1, _WPRI0 : 3,
|
||||
UIE : 1;
|
||||
} u;
|
||||
};
|
||||
|
||||
using mstatus64_t = union {
|
||||
uint64_t val;
|
||||
struct /*mstatus*/ {
|
||||
uint64_t SD : 1, // SD bit is read-only and is set when either the FS or XS
|
||||
// bits encode a Dirty state (i.e., SD=((FS==11) OR
|
||||
// (XS==11)))
|
||||
_WPRI4 : 27, // unused
|
||||
SXL : 2, // value of XLEN for S-mode
|
||||
UXL : 2, // value of XLEN for U-mode
|
||||
_WPRI3 : 9, // unused
|
||||
TSR : 1, // Trap SRET
|
||||
TW : 1, // Timeout Wait
|
||||
TVM : 1, // Trap Virtual Memory
|
||||
MXR : 1, // Make eXecutable Readable
|
||||
SUM : 1, // permit Supervisor User Memory access
|
||||
MPRV : 1, // Modify PRiVilege
|
||||
XS : 2, // status of additional user-mode extensions and associated
|
||||
// state, All off/None dirty or clean, some on/None dirty, some
|
||||
// clean/Some dirty
|
||||
FS : 2, // floating-point unit status Off/Initial/Clean/Dirty
|
||||
MPP : 2, // machine previous privilege
|
||||
_WPRI2 : 2, // unused
|
||||
SPP : 1, // supervisor previous privilege
|
||||
MPIE : 1, // previous machine interrupt-enable
|
||||
_WPRI1 : 1, // unused
|
||||
SPIE : 1, // previous supervisor interrupt-enable
|
||||
UPIE : 1, // previous user interrupt-enable
|
||||
MIE : 1, // machine interrupt-enable
|
||||
_WPRI0 : 1, // unused
|
||||
SIE : 1, // supervisor interrupt-enable
|
||||
UIE : 1; // ‚user interrupt-enable
|
||||
} m;
|
||||
struct /*sstatus*/ {
|
||||
uint64_t SD : 1,
|
||||
_WPRI5 : 29, // unused
|
||||
UXL : 2, // value of XLEN for U-mode
|
||||
_WPRI4 : 12, MXR : 1, SUM : 1, _WPRI3 : 1, XS : 2, FS : 2, _WPRI2 : 4, SPP : 1, _WPRI1 : 2, SPIE : 1,
|
||||
UPIE : 1, _WPRI0 : 2, SIE : 1, UIE : 1;
|
||||
} s;
|
||||
struct /*ustatus*/ {
|
||||
uint32_t SD : 1,
|
||||
_WPRI4 : 29, // unused
|
||||
UXL : 2, // value of XLEN for U-mode
|
||||
_WPRI3 : 12, MXR : 1, SUM : 1, _WPRI2 : 1, XS : 2, FS : 2, _WPRI1 : 8, UPIE : 1, _WPRI0 : 3, UIE : 1;
|
||||
} u;
|
||||
};
|
||||
|
||||
template <unsigned L> inline vm_info decode_vm_info(uint32_t state, uint64_t sptbr);
|
||||
|
||||
template <> inline vm_info decode_vm_info<32u>(uint32_t state, uint64_t sptbr) {
|
||||
if (state == PRIV_M) {
|
||||
return {0, 0, 0, 0};
|
||||
} else if (state <= PRIV_S) {
|
||||
switch (bit_sub<31, 1>(sptbr)) {
|
||||
case 0: // off
|
||||
return {0, 0, 0, 0};
|
||||
case 1: // SV32
|
||||
return {2, 10, 4, bit_sub<0, 22>(sptbr) << PGSHIFT};
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
}
|
||||
|
||||
template <> inline vm_info decode_vm_info<64u>(uint32_t state, uint64_t sptbr) {
|
||||
if (state == PRIV_M) {
|
||||
return {0, 0, 0, 0};
|
||||
} else if (state <= PRIV_S) {
|
||||
switch (bit_sub<60, 4>(sptbr)) {
|
||||
case 0: // off
|
||||
return {0, 0, 0, 0};
|
||||
case 8: // SV39
|
||||
return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
||||
case 9: // SV48
|
||||
return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
||||
case 10: // SV57
|
||||
return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
||||
case 11: // SV64
|
||||
return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
}
|
||||
|
||||
constexpr uint32_t get_mask(unsigned priv_lvl, uint32_t mask) {
|
||||
switch (priv_lvl) {
|
||||
case PRIV_U:
|
||||
return mask & 0x80000011UL; // 0b1000 0000 0000 0000 0000 0000 0001 0001
|
||||
case PRIV_S:
|
||||
return mask & 0x800de133UL; // 0b1000 0000 0000 1101 1110 0001 0011 0011
|
||||
default:
|
||||
return mask & 0x807ff9ddUL; // 0b1000 0000 0111 1111 1111 1001 1011 1011
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint64_t get_mask(unsigned priv_lvl, uint64_t mask) {
|
||||
switch (priv_lvl) {
|
||||
case PRIV_U:
|
||||
return mask & 0x8000000000000011ULL; // 0b1...0 1111 0000 0000 0111 1111
|
||||
// 1111 1001 1011 1011
|
||||
case PRIV_S:
|
||||
return mask & 0x80000003000de133ULL; // 0b1...0 0011 0000 0000 0000 1101
|
||||
// 1110 0001 0011 0011
|
||||
default:
|
||||
return mask & 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111
|
||||
// 1111 1001 1011 1011
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint32_t get_misa(uint32_t mask) { return (1UL << 30) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||
|
||||
constexpr uint64_t get_misa(uint64_t mask) { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||
|
||||
template <typename BASE> struct riscv_hart_msu_vp : public BASE {
|
||||
template <typename BASE> class riscv_hart_msu_vp : public BASE {
|
||||
public:
|
||||
using super = BASE;
|
||||
using this_class = riscv_hart_msu_vp<BASE>;
|
||||
using virt_addr_t = typename super::virt_addr_t;
|
||||
@ -415,6 +258,148 @@ template <typename BASE> struct riscv_hart_msu_vp : public BASE {
|
||||
using rd_csr_f = iss::status (this_class::*)(unsigned addr, reg_t &);
|
||||
using wr_csr_f = iss::status (this_class::*)(unsigned addr, reg_t);
|
||||
|
||||
// primary template
|
||||
template<class T, class Enable = void> struct hart_state { };
|
||||
// specialization 32bit
|
||||
template <typename T>
|
||||
struct hart_state<T, typename std::enable_if<std::is_same<T, uint32_t>::value>::type> {
|
||||
BEGIN_BF_DECL(mstatus_t, T);
|
||||
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11)))
|
||||
BF_FIELD(SD, 31, 1);
|
||||
// Trap SRET
|
||||
BF_FIELD(TSR, 22, 1);
|
||||
// Timeout Wait
|
||||
BF_FIELD(TW, 21, 1);
|
||||
// Trap Virtual Memory
|
||||
BF_FIELD(TVM, 20, 1);
|
||||
// Make eXecutable Readable
|
||||
BF_FIELD(MXR, 19, 1);
|
||||
// permit Supervisor User Memory access
|
||||
BF_FIELD(SUM, 18, 1);
|
||||
// Modify PRiVilege
|
||||
BF_FIELD(MPRV, 17, 1);
|
||||
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty
|
||||
BF_FIELD(XS, 15, 2);
|
||||
// floating-point unit status Off/Initial/Clean/Dirty
|
||||
BF_FIELD(FS, 13, 2);
|
||||
// machine previous privilege
|
||||
BF_FIELD(MPP, 11, 2);
|
||||
// supervisor previous privilege
|
||||
BF_FIELD(SPP, 8, 1);
|
||||
// previous machine interrupt-enable
|
||||
BF_FIELD(MPIE, 7, 1);
|
||||
// previous supervisor interrupt-enable
|
||||
BF_FIELD(SPIE, 5, 1);
|
||||
// previous user interrupt-enable
|
||||
BF_FIELD(UPIE, 4, 1);
|
||||
// machine interrupt-enable
|
||||
BF_FIELD(MIE, 3, 1);
|
||||
// supervisor interrupt-enable
|
||||
BF_FIELD(SIE, 1, 1);
|
||||
// user interrupt-enable
|
||||
BF_FIELD(UIE, 0, 1);
|
||||
END_BF_DECL();
|
||||
|
||||
mstatus_t mstatus;
|
||||
|
||||
T satp;
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
// specialization 64bit
|
||||
template <typename T>
|
||||
struct hart_state<T, typename std::enable_if<std::is_same<T, uint64_t>::value>::type> {
|
||||
BEGIN_BF_DECL(mstatus_t, T);
|
||||
// SD bit is read-only and is set when either the FS or XS bits encode a Dirty state (i.e., SD=((FS==11) OR XS==11)))
|
||||
BF_FIELD(SD, 63, 1);
|
||||
// value of XLEN for S-mode
|
||||
BF_FIELD(SXL, 34, 2);
|
||||
// value of XLEN for U-mode
|
||||
BF_FIELD(UXL, 32, 2);
|
||||
// Trap SRET
|
||||
BF_FIELD(TSR, 22, 1);
|
||||
// Timeout Wait
|
||||
BF_FIELD(TW, 21, 1);
|
||||
// Trap Virtual Memory
|
||||
BF_FIELD(TVM, 20, 1);
|
||||
// Make eXecutable Readable
|
||||
BF_FIELD(MXR, 19, 1);
|
||||
// permit Supervisor User Memory access
|
||||
BF_FIELD(SUM, 18, 1);
|
||||
// Modify PRiVilege
|
||||
BF_FIELD(MPRV, 17, 1);
|
||||
// status of additional user-mode extensions and associated state, All off/None dirty or clean, some on/None dirty, some clean/Some dirty
|
||||
BF_FIELD(XS, 15, 2);
|
||||
// floating-point unit status Off/Initial/Clean/Dirty
|
||||
BF_FIELD(FS, 13, 2);
|
||||
// machine previous privilege
|
||||
BF_FIELD(MPP, 11, 2);
|
||||
// supervisor previous privilege
|
||||
BF_FIELD(SPP, 8, 1);
|
||||
// previous machine interrupt-enable
|
||||
BF_FIELD(MPIE, 7, 1);
|
||||
// previous supervisor interrupt-enable
|
||||
BF_FIELD(SPIE, 5, 1);
|
||||
// previous user interrupt-enable
|
||||
BF_FIELD(UPIE, 4, 1);
|
||||
// machine interrupt-enable
|
||||
BF_FIELD(MIE, 3, 1);
|
||||
// supervisor interrupt-enable
|
||||
BF_FIELD(SIE, 1, 1);
|
||||
// user interrupt-enable
|
||||
BF_FIELD(UIE, 0, 1);
|
||||
END_BF_DECL();
|
||||
|
||||
mstatus_t mstatus;
|
||||
|
||||
T satp;
|
||||
|
||||
static constexpr T get_misa() { return (2ULL << 62) | ISA_I | ISA_M | ISA_A | ISA_U | ISA_S | ISA_M; }
|
||||
|
||||
static constexpr T get_mask(unsigned priv_lvl) {
|
||||
switch (priv_lvl) {
|
||||
case PRIV_U: return 0x8000000000000011ULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
case PRIV_S: return 0x80000003000de133ULL; // 0b1...0 0011 0000 0000 0000 1101 1110 0001 0011 0011
|
||||
default: return 0x8000000f007ff9ddULL; // 0b1...0 1111 0000 0000 0111 1111 1111 1001 1011 1011
|
||||
}
|
||||
}
|
||||
|
||||
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<60, 4>(sptbr)) {
|
||||
case 0: return {0, 0, 0, 0}; // off
|
||||
case 8: return {3, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV39
|
||||
case 9: return {4, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV48
|
||||
case 10: return {5, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV57
|
||||
case 11: return {6, 9, 8, bit_sub<0, 44>(sptbr) << PGSHIFT};// SV64
|
||||
default: abort();
|
||||
}
|
||||
abort();
|
||||
return {0, 0, 0, 0}; // dummy
|
||||
}
|
||||
};
|
||||
|
||||
const typename super::reg_t PGSIZE = 1 << PGSHIFT;
|
||||
const typename super::reg_t PGMASK = PGSIZE - 1;
|
||||
|
||||
@ -446,7 +431,7 @@ template <typename BASE> struct riscv_hart_msu_vp : public BASE {
|
||||
virtual std::string get_additional_disass_info() {
|
||||
std::stringstream s;
|
||||
s << "[p:" << lvl[this->reg.machine_state] << ";s:0x" << std::hex << std::setfill('0')
|
||||
<< std::setw(sizeof(reg_t) * 2) << mstatus_r << std::dec << ";c:" << this->reg.icount << "]";
|
||||
<< std::setw(sizeof(reg_t) * 2) << (reg_t)state.mstatus << std::dec << ";c:" << this->reg.icount << "]";
|
||||
return s.str();
|
||||
};
|
||||
|
||||
@ -466,8 +451,7 @@ protected:
|
||||
using csr_page_type = typename csr_type::page_type;
|
||||
mem_type mem;
|
||||
csr_type csr;
|
||||
reg_t &mstatus_r;
|
||||
reg_t &satp_r;
|
||||
hart_state<reg_t> state;
|
||||
unsigned to_host_wr_cnt = 0;
|
||||
std::stringstream uart_buf;
|
||||
std::unordered_map<reg_t, uint64_t> ptw;
|
||||
@ -490,9 +474,8 @@ private:
|
||||
|
||||
template <typename BASE>
|
||||
riscv_hart_msu_vp<BASE>::riscv_hart_msu_vp()
|
||||
: mstatus_r(csr[mstatus])
|
||||
, satp_r(csr[satp]) {
|
||||
csr[misa] = traits<BASE>::XLEN == 32 ? 1ULL << (traits<BASE>::XLEN - 2) : 2ULL << (traits<BASE>::XLEN - 2);
|
||||
: state() {
|
||||
csr[misa] = hart_state<reg_t>::get_misa();
|
||||
uart_buf.str("");
|
||||
// read-only registers
|
||||
csr_wr_cb[misa] = nullptr;
|
||||
@ -539,13 +522,10 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::load_file(std::string nam
|
||||
// Load ELF data
|
||||
if (!reader.load(name)) throw std::runtime_error("could not process elf file");
|
||||
// check elf properties
|
||||
// TODO: fix ELFCLASS like:
|
||||
// if ( reader.get_class() != ELFCLASS32 ) throw std::runtime_error("wrong
|
||||
// elf class in file");
|
||||
if ( reader.get_class() != ELFCLASS32 )
|
||||
if(sizeof(reg_t) == 4) throw std::runtime_error("wrong elf class in file");
|
||||
if (reader.get_type() != ET_EXEC) throw std::runtime_error("wrong elf type in file");
|
||||
// TODO: fix machine type like:
|
||||
// if ( reader.get_machine() != EM_RISCV ) throw std::runtime_error("wrong
|
||||
// elf machine in file");
|
||||
if ( reader.get_machine() != EM_RISCV ) throw std::runtime_error("wrong elf machine in file");
|
||||
for (const auto pseg : reader.segments) {
|
||||
const auto fsize = pseg->get_file_size(); // 0x42c/0x0
|
||||
const auto seg_data = pseg->get_data();
|
||||
@ -588,7 +568,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const iss::addr_t &addr, unsigned leng
|
||||
}
|
||||
try {
|
||||
if ((addr.val & ~PGMASK) != ((addr.val + length - 1) & ~PGMASK)) { // we may cross a page boundary
|
||||
vm_info vm = decode_vm_info<traits<BASE>::XLEN>(this->reg.machine_state, csr[satp]);
|
||||
vm_info vm = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
|
||||
if (vm.levels != 0) { // VM is active
|
||||
auto split_addr = (addr.val + length) & ~PGMASK;
|
||||
auto len1 = split_addr - addr.val;
|
||||
@ -616,8 +596,7 @@ iss::status riscv_hart_msu_vp<BASE>::read(const iss::addr_t &addr, unsigned leng
|
||||
switch (addr.val) {
|
||||
case 2: // SFENCE:VMA lower
|
||||
case 3: { // SFENCE:VMA upper
|
||||
auto status = csr[mstatus];
|
||||
auto tvm = status & (1 << 20);
|
||||
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;
|
||||
@ -677,7 +656,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const iss::addr_t &addr, unsigned len
|
||||
}
|
||||
try {
|
||||
if ((addr.val & ~PGMASK) != ((addr.val + length - 1) & ~PGMASK)) { // we may cross a page boundary
|
||||
vm_info vm = decode_vm_info<traits<BASE>::XLEN>(this->reg.machine_state, csr[satp]);
|
||||
vm_info vm = hart_state<reg_t>::decode_vm_info(this->reg.machine_state, state.satp);
|
||||
if (vm.levels != 0) { // VM is active
|
||||
auto split_addr = (addr.val + length) & ~PGMASK;
|
||||
auto len1 = split_addr - addr.val;
|
||||
@ -738,8 +717,7 @@ iss::status riscv_hart_msu_vp<BASE>::write(const iss::addr_t &addr, unsigned len
|
||||
case 2:
|
||||
case 3: {
|
||||
ptw.clear();
|
||||
auto status = csr[mstatus];
|
||||
auto tvm = status & (1 << 20);
|
||||
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;
|
||||
@ -799,18 +777,17 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_cycle(unsigne
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_status(unsigned addr, reg_t &val) {
|
||||
auto req_priv_lvl = addr >> 8;
|
||||
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
||||
auto mask = get_mask(req_priv_lvl, (reg_t)(std::numeric_limits<reg_t>::max()));
|
||||
val = csr[mstatus] & mask;
|
||||
val = state.mstatus & hart_state<reg_t>::get_mask(req_priv_lvl);
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_status(unsigned addr, reg_t val) {
|
||||
auto req_priv_lvl = addr >> 8;
|
||||
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
||||
auto mask = get_mask(req_priv_lvl, (reg_t)std::numeric_limits<reg_t>::max());
|
||||
auto old_val = csr[mstatus];
|
||||
auto mask = hart_state<reg_t>::get_mask(req_priv_lvl);
|
||||
auto old_val = state.mstatus;
|
||||
auto new_val = (old_val & ~mask) | (val & mask);
|
||||
csr[mstatus] = new_val;
|
||||
state.mstatus = new_val;
|
||||
check_interrupt();
|
||||
return iss::Ok;
|
||||
}
|
||||
@ -836,9 +813,9 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ie(unsigned
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_ip(unsigned addr, reg_t &val) {
|
||||
auto req_priv_lvl = addr >> 8;
|
||||
if (this->reg.machine_state < req_priv_lvl) throw illegal_instruction_fault(this->fault_data);
|
||||
val = csr[mie];
|
||||
if (addr < mie) val &= csr[mideleg];
|
||||
if (addr < sie) val &= csr[sideleg];
|
||||
val = csr[mip];
|
||||
if (addr < mip) val &= csr[mideleg];
|
||||
if (addr < sip) val &= csr[sideleg];
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
@ -852,26 +829,24 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_ip(unsigned
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_satp(unsigned addr, reg_t &val) {
|
||||
auto status = csr[mstatus];
|
||||
auto tvm = status & (1 << 20);
|
||||
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 = csr[satp];
|
||||
val = state.satp;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_satp(unsigned addr, reg_t val) {
|
||||
auto status = csr[mstatus];
|
||||
auto tvm = status & (1 << 20);
|
||||
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;
|
||||
}
|
||||
csr[satp] = val;
|
||||
state.satp = val;
|
||||
return iss::Ok;
|
||||
}
|
||||
|
||||
@ -973,7 +948,7 @@ iss::status riscv_hart_msu_vp<BASE>::write_mem(phys_addr_t paddr, unsigned lengt
|
||||
}
|
||||
|
||||
template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
|
||||
auto status = csr[mstatus];
|
||||
auto status = state.mstatus;
|
||||
auto ip = csr[mip];
|
||||
auto ie = csr[mie];
|
||||
auto ideleg = csr[mideleg];
|
||||
@ -983,19 +958,19 @@ template <typename BASE> void riscv_hart_msu_vp<BASE>::check_interrupt() {
|
||||
// any synchronous traps.
|
||||
auto ena_irq = ip & ie;
|
||||
|
||||
auto mie = (csr[mstatus] >> 3) & 1;
|
||||
auto mie = state.mstatus.MIE;
|
||||
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 = (csr[mstatus] >> 1) & 1;
|
||||
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++;
|
||||
this->reg.pending_trap = res << 16 | 1;
|
||||
this->reg.pending_trap = res << 16 | 1; // 0x80 << 24 | (cause << 16) | trap_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -1011,12 +986,11 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::v2p(const
|
||||
}
|
||||
|
||||
const auto type = (access_type)(addr.getAccessType() & ~iss::DEBUG);
|
||||
uint32_t mode = type != iss::FETCH && bit_sub<17, 1>(mstatus_r) ? // MPRV
|
||||
mode = bit_sub<11, 2>(mstatus_r)
|
||||
: // MPV
|
||||
uint32_t mode = type != iss::FETCH && state.mstatus.MPRV ? // MPRV
|
||||
mode = state.mstatus.MPP:
|
||||
this->reg.machine_state;
|
||||
|
||||
const vm_info vm = decode_vm_info<traits<BASE>::XLEN>(mode, satp_r);
|
||||
const vm_info vm = hart_state<reg_t>::decode_vm_info(mode, state.satp);
|
||||
|
||||
if (vm.levels == 0) {
|
||||
phys_addr_t ret(addr);
|
||||
@ -1025,8 +999,8 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::v2p(const
|
||||
}
|
||||
|
||||
const bool s_mode = mode == PRIV_S;
|
||||
const bool sum = bit_sub<18, 1>(mstatus_r); // MSTATUS_SUM);
|
||||
const bool mxr = bit_sub<19, 1>(mstatus_r); // MSTATUS_MXR);
|
||||
const bool sum = state.mstatus.SUM;
|
||||
const bool mxr = state.mstatus.MXR;
|
||||
|
||||
auto it = ptw.find(addr.val >> PGSHIFT);
|
||||
if (it != ptw.end()) {
|
||||
@ -1110,9 +1084,10 @@ typename riscv_hart_msu_vp<BASE>::phys_addr_t riscv_hart_msu_vp<BASE>::v2p(const
|
||||
|
||||
template <typename BASE> uint64_t riscv_hart_msu_vp<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 = flags & 0xffff;
|
||||
auto cause = (flags >> 16) & 0x7fff;
|
||||
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
|
||||
// calculate effective privilege level
|
||||
auto new_priv = PRIV_M;
|
||||
@ -1143,26 +1118,23 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
||||
// is written with the value of the active interrupt-enable bit at the time of
|
||||
// the trap; and the x IE field of mstatus
|
||||
// is cleared
|
||||
auto status = csr[mstatus];
|
||||
auto xie = (status >> cur_priv) & 1;
|
||||
// store the actual privilege level in yPP
|
||||
// store the actual privilege level in yPP and store interrupt enable flags
|
||||
switch (new_priv) {
|
||||
case PRIV_M:
|
||||
status &= ~(3 << 11);
|
||||
status |= (cur_priv & 0x3) << 11;
|
||||
state.mstatus.MPP=cur_priv;
|
||||
state.mstatus.MPIE=state.mstatus.MIE;
|
||||
break;
|
||||
case PRIV_S:
|
||||
status &= ~(1 << 8);
|
||||
status |= (cur_priv & 0x1) << 8;
|
||||
state.mstatus.SPP = cur_priv;
|
||||
state.mstatus.SPIE=state.mstatus.SIE;
|
||||
break;
|
||||
case PRIV_U:
|
||||
state.mstatus.UPIE=state.mstatus.UIE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// store interrupt enable flags
|
||||
status &= ~(1 << (new_priv + 4) | 1 << cur_priv); // clear respective xPIE and yIE
|
||||
status |= (xie << (new_priv + 4)); // store yIE
|
||||
|
||||
csr[mstatus] = status;
|
||||
// get trap vector
|
||||
auto ivec = csr[utvec | (new_priv << 8)];
|
||||
// calculate addr// set NEXT_PC to trap addressess to jump to based on MODE
|
||||
@ -1174,7 +1146,7 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
||||
this->reg.trap_state = 0;
|
||||
char buffer[32];
|
||||
sprintf(buffer, "0x%016lx", addr);
|
||||
CLOG(INFO, disass) << (trap_id ? "Interrupt " : "Trap ") << trap_id << " with cause '" << irq_str[cause]
|
||||
CLOG(INFO, disass) << (trap_id ? "Interrupt " : "Trap ") << " with cause '" << (trap_id ? irq_str[cause] : trap_str[cause])
|
||||
<< "' at address " << buffer << " occurred, changing privilege level from " << lvl[cur_priv]
|
||||
<< " to " << lvl[new_priv];
|
||||
return this->reg.NEXT_PC;
|
||||
@ -1183,10 +1155,9 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::enter_trap(uint64_t f
|
||||
template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t flags) {
|
||||
auto cur_priv = this->reg.machine_state;
|
||||
auto inst_priv = flags & 0x3;
|
||||
auto status = csr[mstatus];
|
||||
auto ppl = inst_priv; // previous privilege level
|
||||
auto status = state.mstatus;
|
||||
|
||||
auto tsr = status & (1 << 22);
|
||||
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;
|
||||
@ -1194,33 +1165,32 @@ template <typename BASE> uint64_t riscv_hart_msu_vp<BASE>::leave_trap(uint64_t f
|
||||
}
|
||||
|
||||
// pop the relevant lower-privilege interrupt enable and privilege mode stack
|
||||
// clear respective yIE
|
||||
switch (inst_priv) {
|
||||
case PRIV_M:
|
||||
ppl = (status >> 11) & 0x3;
|
||||
status &= ~(0x3 << 11); // clear mpp to U mode
|
||||
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:
|
||||
ppl = (status >> 8) & 1;
|
||||
status &= ~(1 << 8); // clear spp to U mode
|
||||
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:
|
||||
ppl = 0;
|
||||
this->reg.machine_state = 0;
|
||||
state.mstatus.UIE=state.mstatus.UPIE;
|
||||
break;
|
||||
}
|
||||
// sets the pc to the value stored in the x epc register.
|
||||
this->reg.NEXT_PC = csr[uepc | inst_priv << 8];
|
||||
status &= ~(1 << ppl); // clear respective yIE
|
||||
auto pie = (status >> (inst_priv + 4)) & 0x1; // previous interrupt enable
|
||||
status |= pie << inst_priv; // and set the pie
|
||||
csr[mstatus] = status;
|
||||
this->reg.machine_state = ppl;
|
||||
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " << lvl[ppl];
|
||||
CLOG(INFO, disass) << "Executing xRET , changing privilege level from " << lvl[cur_priv] << " to " << lvl[this->reg.machine_state];
|
||||
return this->reg.NEXT_PC;
|
||||
}
|
||||
|
||||
template <typename BASE> void riscv_hart_msu_vp<BASE>::wait_until(uint64_t flags) {
|
||||
auto status = csr[mstatus];
|
||||
auto tw = status & (1 << 21);
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user