update core wrapper: remove virtual memory support

This commit is contained in:
Stanislaw Kaushanski 2020-09-07 13:29:45 +02:00
parent 6f3963a473
commit 293c396a0d
2 changed files with 2 additions and 174 deletions

View File

@ -8,7 +8,7 @@ This library provide the infrastructure to build RISC-V ISS. Currently part of t
* RV32I (TGF01)
* RV32MIC (TGF02)
All pass the respective compliance tests. Along with those ISA implementations there is a wrapper implementing the M/S/U modes including virtual memory management and CSRs as of privileged spec 1.10. The main.cpp in src allows to build a stand-alone ISS when integrated into a top-level project. For further information please have a look at [https://git.minres.com/VP/RISCV-VP](https://git.minres.com/VP/RISCV-VP).
All pass the respective compliance tests. Along with those ISA implementations there is a wrapper (riscv_hart_m_p.h) implementing the Machine privileged mode as of privileged spec 1.10. The main.cpp in src allows to build a stand-alone ISS when integrated into a top-level project. For further information please have a look at [https://git.minres.com/VP/RISCV-VP](https://git.minres.com/VP/RISCV-VP).
Last but not least an SystemC wrapper is provided which allows easy integration into SystemC based virtual platforms.

View File

@ -231,14 +231,6 @@ enum {
ISA_U = 1 << 20
};
struct vm_info {
int levels;
int idxbits;
int ptesize;
uint64_t ptbase;
bool is_active() { return levels; }
};
class trap_load_access_fault : public trap_access {
public:
trap_load_access_fault(uint64_t badaddr)
@ -249,28 +241,12 @@ public:
illegal_instruction_fault(uint64_t badaddr)
: trap_access(2 << 16, badaddr) {}
};
class trap_instruction_page_fault : public trap_access {
public:
trap_instruction_page_fault(uint64_t badaddr)
: trap_access(12 << 16, badaddr) {}
};
class trap_load_page_fault : public trap_access {
public:
trap_load_page_fault(uint64_t badaddr)
: trap_access(13 << 16, badaddr) {}
};
class trap_store_page_fault : public trap_access {
public:
trap_store_page_fault(uint64_t badaddr)
: trap_access(15 << 16, badaddr) {}
};
} // namespace
template <typename BASE> class riscv_hart_m_p : public BASE {
public:
using super = BASE;
using this_class = riscv_hart_m_p<BASE>;
using virt_addr_t = typename super::virt_addr_t;
using phys_addr_t = typename super::phys_addr_t;
using reg_t = typename super::reg_t;
using addr_t = typename super::addr_t;
@ -337,13 +313,7 @@ public:
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() {
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() {
return 0b101110111011; // only machine mode is supported
@ -356,8 +326,6 @@ public:
std::pair<uint64_t, bool> load_file(std::string name, int type = -1) override;
virtual phys_addr_t virt2phys(const iss::addr_t &addr) override;
iss::status read(const address_type type, const access_type access, const uint32_t space,
const uint64_t addr, const unsigned length, uint8_t *const data) override;
iss::status write(const address_type type, const access_type access, const uint32_t space,
@ -366,7 +334,6 @@ public:
virtual uint64_t enter_trap(uint64_t flags) override { return riscv_hart_m_p::enter_trap(flags, fault_data); }
virtual uint64_t enter_trap(uint64_t flags, uint64_t addr) override;
virtual uint64_t leave_trap(uint64_t flags) override;
void wait_until(uint64_t flags) override;
const reg_t& get_mhartid() const { return mhartid_reg; }
void set_mhartid(reg_t mhartid) { mhartid_reg = mhartid; };
@ -412,7 +379,6 @@ protected:
hart_state<reg_t> state;
uint64_t cycle_offset;
reg_t fault_data;
std::array<vm_info, 2> vm;
uint64_t tohost = tohost_dflt;
uint64_t fromhost = fromhost_dflt;
unsigned to_host_wr_cnt = 0;
@ -423,7 +389,6 @@ protected:
using csr_page_type = typename csr_type::page_type;
mem_type mem;
csr_type csr;
void update_vm_info();
std::stringstream uart_buf;
std::unordered_map<reg_t, uint64_t> ptw;
std::unordered_map<uint64_t, uint8_t> atomic_reservation;
@ -439,10 +404,6 @@ private:
iss::status write_ie(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 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);
iss::status write_fcsr(unsigned addr, reg_t val);
iss::status read_hartid(unsigned addr, reg_t &val);
reg_t mhartid_reg{0xF};
@ -546,17 +507,6 @@ iss::status riscv_hart_m_p<BASE>::read(const address_type type, const access_typ
return iss::Err;
}
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();
if (vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
auto res = read(type, access, space, addr, len1, data);
if (res == iss::Ok)
res = read(type, access, space, split_addr, length - len1, data + len1);
return res;
}
}
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);
@ -629,17 +579,6 @@ iss::status riscv_hart_m_p<BASE>::write(const address_type type, const access_ty
return iss::Err;
}
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();
if (vm.levels != 0) { // VM is active
auto split_addr = (addr + length) & ~PGMASK;
auto len1 = split_addr - addr;
auto res = write(type, access, space, addr, len1, data);
if (res == iss::Ok)
res = write(type, access, space, split_addr, length - len1, data + len1);
return res;
}
}
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);
@ -772,7 +711,6 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_status(unsigned
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_status(unsigned addr, reg_t val) {
state.write_mstatus(val);
check_interrupt();
update_vm_info();
return iss::Ok;
}
@ -914,30 +852,15 @@ iss::status riscv_hart_m_p<BASE>::write_mem(phys_addr_t paddr, unsigned length,
template <typename BASE> inline void riscv_hart_m_p<BASE>::reset(uint64_t address) {
BASE::reset(address);
state.mstatus = hart_state<reg_t>::mstatus_reset_val;
update_vm_info();
}
template <typename BASE> inline void riscv_hart_m_p<BASE>::update_vm_info() {
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();
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;
ptw.clear();
}
template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
auto status = state.mstatus;
auto ip = csr[mip];
auto ie = csr[mie];
auto ideleg = csr[mideleg];
// Multiple simultaneous interrupts and traps at the same privilege level are
// handled in the following decreasing priority order:
// external interrupts, software interrupts, timer interrupts, then finally
// any synchronous traps.
auto ena_irq = ip & ie;
auto ena_irq = csr[mip] & csr[mie];
bool mie = state.mstatus.MIE;
auto m_enabled = this->reg.machine_state < PRIV_M || (this->reg.machine_state == PRIV_M && mie);
@ -950,98 +873,6 @@ template <typename BASE> void riscv_hart_m_p<BASE>::check_interrupt() {
}
}
template <typename BASE>
typename riscv_hart_m_p<BASE>::phys_addr_t riscv_hart_m_p<BASE>::virt2phys(const iss::addr_t &addr) {
const auto type = addr.access & iss::access_type::FUNC;
auto it = ptw.find(addr.val >> PGSHIFT);
if (it != ptw.end()) {
const reg_t pte = it->second;
const reg_t ad = PTE_A | (type == iss::access_type::WRITE) * PTE_D;
#ifdef RISCV_ENABLE_DIRTY
// set accessed and possibly dirty bits.
*(uint32_t *)ppte |= ad;
return {addr.getAccessType(), addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)};
#else
// take exception if access or possibly dirty bit is not set.
if ((pte & ad) == ad)
return {addr.access, addr.space, (pte & (~PGMASK)) | (addr.val & PGMASK)};
else
ptw.erase(it); // throw an exception
#endif
} else {
uint32_t mode = type != iss::access_type::FETCH && state.mstatus.MPRV ? // MPRV
state.mstatus.MPP :
this->reg.machine_state;
const vm_info &vm = this->vm[static_cast<uint16_t>(type) / 2];
const bool sum = state.mstatus.SUM;
const bool mxr = state.mstatus.MXR;
// verify bits xlen-1:va_bits-1 are all equal
const int va_bits = PGSHIFT + vm.levels * vm.idxbits;
const reg_t mask = (reg_t(1) << (traits<BASE>::XLEN > -(va_bits - 1))) - 1;
const reg_t masked_msbs = (addr.val >> (va_bits - 1)) & mask;
const int levels = (masked_msbs != 0 && masked_msbs != mask) ? 0 : vm.levels;
reg_t base = vm.ptbase;
for (int i = levels - 1; i >= 0; i--) {
const int ptshift = i * vm.idxbits;
const reg_t idx = (addr.val >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
// check that physical address of PTE is legal
reg_t pte = 0;
const uint8_t res = this->read(iss::address_type::PHYSICAL, addr.access,
traits<BASE>::MEM, base + idx * vm.ptesize, vm.ptesize, (uint8_t *)&pte);
if (res != 0) throw trap_load_access_fault(addr.val);
const reg_t ppn = pte >> PTE_PPN_SHIFT;
if (PTE_TABLE(pte)) { // next level of page table
base = ppn << PGSHIFT;
} else if (!(pte & PTE_U)) {
break;
} else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
break;
} else if (type == iss::access_type::FETCH
? !(pte & PTE_X)
: type == iss::access_type::READ ? !(pte & PTE_R) && !(mxr && (pte & PTE_X))
: !((pte & PTE_R) && (pte & PTE_W))) {
break;
} else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) {
break;
} else {
const reg_t ad = PTE_A | ((type == iss::access_type::WRITE) * PTE_D);
#ifdef RISCV_ENABLE_DIRTY
// set accessed and possibly dirty bits.
*(uint32_t *)ppte |= ad;
#else
// take exception if access or possibly dirty bit is not set.
if ((pte & ad) != ad) break;
#endif
// for superpage mappings, make a fake leaf PTE for the TLB's benefit.
const reg_t vpn = addr.val >> PGSHIFT;
const reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
const reg_t offset = addr.val & PGMASK;
ptw[vpn] = value | (pte & 0xff);
return {addr.access, addr.space, value | offset};
}
}
}
switch (type) {
case access_type::FETCH:
this->fault_data = addr.val;
throw trap_instruction_page_fault(addr.val);
case access_type::READ:
this->fault_data = addr.val;
throw trap_load_page_fault(addr.val);
case access_type::WRITE:
this->fault_data = addr.val;
throw trap_store_page_fault(addr.val);
default:
abort();
}
}
template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flags, uint64_t addr) {
// flags are ACTIVE[31:31], CAUSE[30:16], TRAPID[15:0]
// calculate and write mcause val
@ -1085,7 +916,6 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::enter_trap(uint64_t flag
CLOG(INFO, disass) << (trap_id ? "Interrupt" : "Trap") << " with cause '"
<< (trap_id ? irq_str[cause] : trap_str[cause]) << "' (" << cause << ")"
<< " at address " << buffer.data() << " occurred";
update_vm_info();
return this->reg.NEXT_PC;
}
@ -1107,11 +937,9 @@ template <typename BASE> uint64_t riscv_hart_m_p<BASE>::leave_trap(uint64_t flag
// sets the pc to the value stored in the x epc register.
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) {}
} // namespace arch
} // namespace iss