diff options
author | Andrew Waterman <andrew@sifive.com> | 2020-07-09 13:48:17 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-09 13:48:17 -0700 |
commit | ecc039ef5726315b7e37e8a9c7b682bbe09c9cf5 (patch) | |
tree | b4b2c5a4f545251f8ffde3633a93eea67f16398a | |
parent | 26eb6a2cbc1dfa7edf43e04a858af0de7870e650 (diff) | |
parent | dff90a831ef3e393b2cea931515d25677fdced30 (diff) | |
download | spike-ecc039ef5726315b7e37e8a9c7b682bbe09c9cf5.zip spike-ecc039ef5726315b7e37e8a9c7b682bbe09c9cf5.tar.gz spike-ecc039ef5726315b7e37e8a9c7b682bbe09c9cf5.tar.bz2 |
Merge pull request #493 from avpatel/riscv-hyp-ext-v0.6.1
RISC-V H-Extension v0.6.1 Support
35 files changed, 946 insertions, 146 deletions
diff --git a/riscv/decode.h b/riscv/decode.h index 34fa1d2..76d7d90 100644 --- a/riscv/decode.h +++ b/riscv/decode.h @@ -232,6 +232,7 @@ private: #define require(x) if (unlikely(!(x))) throw trap_illegal_instruction(0) #define require_privilege(p) require(STATE.prv >= (p)) +#define require_novirt() if (unlikely(STATE.v == true)) throw trap_virtual_instruction(0) #define require_rv64 require(xlen == 64) #define require_rv32 require(xlen == 32) #define require_extension(s) require(p->supports_extension(s)) @@ -352,10 +353,18 @@ inline freg_t f128_negate(freg_t a) if (!STATE.serialized) return PC_SERIALIZE_BEFORE; \ STATE.serialized = false; \ unsigned csr_priv = get_field((which), 0x300); \ - bool mode_unsupported = csr_priv == PRV_S && !P.supports_extension('S'); \ - unsigned csr_read_only = get_field((which), 0xC00) == 3; \ - if (((write) && csr_read_only) || STATE.prv < csr_priv || mode_unsupported) \ + bool mode_unsupported = (csr_priv == PRV_S && !P.supports_extension('S')) || \ + (csr_priv == PRV_HS && !P.supports_extension('H')); \ + if (mode_unsupported) \ throw trap_illegal_instruction(0); \ + unsigned state_prv = (STATE.prv == PRV_S && !STATE.v) ? PRV_HS: STATE.prv; \ + unsigned csr_read_only = get_field((which), 0xC00) == 3; \ + if (((write) && csr_read_only) || state_prv < csr_priv) { \ + if (csr_priv == PRV_HS) \ + throw trap_virtual_instruction(0); \ + else \ + throw trap_illegal_instruction(0); \ + } \ (which); }) /* For debug only. This will fail if the native machine's float types are not IEEE */ diff --git a/riscv/dts.cc b/riscv/dts.cc index 6a38ba0..56b76e6 100644 --- a/riscv/dts.cc +++ b/riscv/dts.cc @@ -11,6 +11,7 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, reg_t initrd_start, reg_t initrd_end, + const char* bootargs, std::vector<processor_t*> procs, std::vector<std::pair<reg_t, mem_t*>> mems) { @@ -25,12 +26,22 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, " model = \"ucbbar,spike-bare\";\n" " chosen {\n"; if (initrd_start < initrd_end) { - s << " bootargs = \"root=/dev/ram console=hvc0 earlycon=sbi\";\n" - " linux,initrd-start = <" << (size_t)initrd_start << ">;\n" + s << " linux,initrd-start = <" << (size_t)initrd_start << ">;\n" " linux,initrd-end = <" << (size_t)initrd_end << ">;\n"; + if (!bootargs) + bootargs = "root=/dev/ram console=hvc0 earlycon=sbi"; } else { - s << " bootargs = \"console=hvc0 earlycon=sbi\";\n"; + if (!bootargs) + bootargs = "console=hvc0 earlycon=sbi"; } + s << " bootargs = \""; + for (size_t i = 0; i < strlen(bootargs); i++) { + if (bootargs[i] == '"') + s << '\\' << bootargs[i]; + else + s << bootargs[i]; + } + s << "\";\n"; s << " };\n" " cpus {\n" " #address-cells = <1>;\n" diff --git a/riscv/dts.h b/riscv/dts.h index 1c3f701..1f01e0f 100644 --- a/riscv/dts.h +++ b/riscv/dts.h @@ -8,6 +8,7 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, reg_t initrd_start, reg_t initrd_end, + const char* bootargs, std::vector<processor_t*> procs, std::vector<std::pair<reg_t, mem_t*>> mems); diff --git a/riscv/encoding.h b/riscv/encoding.h index 2471ec1..be66895 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -25,6 +25,8 @@ #define MSTATUS32_SD 0x80000000 #define MSTATUS_UXL 0x0000000300000000 #define MSTATUS_SXL 0x0000000C00000000 +#define MSTATUS_GVA 0x0000004000000000 +#define MSTATUS_MPV 0x0000008000000000 #define MSTATUS64_SD 0x8000000000000000 #define SSTATUS_UIE 0x00000001 @@ -41,6 +43,21 @@ #define SSTATUS_UXL 0x0000000300000000 #define SSTATUS64_SD 0x8000000000000000 +#define SSTATUS_VS_MASK (SSTATUS_SIE | SSTATUS_SPIE | \ + SSTATUS_SPP | SSTATUS_SUM | \ + SSTATUS_MXR | SSTATUS_UXL) + +#define HSTATUS_VSXL 0x300000000 +#define HSTATUS_VTSR 0x00400000 +#define HSTATUS_VTW 0x00200000 +#define HSTATUS_VTVM 0x00100000 +#define HSTATUS_VGEIN 0x0003f000 +#define HSTATUS_HU 0x00000200 +#define HSTATUS_SPVP 0x00000100 +#define HSTATUS_SPV 0x00000080 +#define HSTATUS_GVA 0x00000040 +#define HSTATUS_VSBE 0x00000020 + #define USTATUS_UIE 0x00000001 #define USTATUS_UPIE 0x00000010 @@ -102,25 +119,33 @@ #define MIP_USIP (1 << IRQ_U_SOFT) #define MIP_SSIP (1 << IRQ_S_SOFT) -#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_VSSIP (1 << IRQ_VS_SOFT) #define MIP_MSIP (1 << IRQ_M_SOFT) #define MIP_UTIP (1 << IRQ_U_TIMER) #define MIP_STIP (1 << IRQ_S_TIMER) -#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_VSTIP (1 << IRQ_VS_TIMER) #define MIP_MTIP (1 << IRQ_M_TIMER) #define MIP_UEIP (1 << IRQ_U_EXT) #define MIP_SEIP (1 << IRQ_S_EXT) -#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_VSEIP (1 << IRQ_VS_EXT) #define MIP_MEIP (1 << IRQ_M_EXT) +#define MIP_SGEIP (1 << IRQ_S_GEXT) + +#define MIP_S_MASK (MIP_SSIP | MIP_STIP | MIP_SEIP) +#define MIP_VS_MASK (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP) +#define MIP_HS_MASK (MIP_VS_MASK | MIP_SGEIP) + +#define MIDELEG_FORCED_MASK MIP_HS_MASK #define SIP_SSIP MIP_SSIP #define SIP_STIP MIP_STIP #define PRV_U 0 #define PRV_S 1 -#define PRV_H 2 #define PRV_M 3 +#define PRV_HS (PRV_S + 1) + #define SATP32_MODE 0x80000000 #define SATP32_ASID 0x7FC00000 #define SATP32_PPN 0x003FFFFF @@ -135,6 +160,19 @@ #define SATP_MODE_SV57 10 #define SATP_MODE_SV64 11 +#define HGATP32_MODE 0x80000000 +#define HGATP32_VMID 0x1FC00000 +#define HGATP32_PPN 0x003FFFFF + +#define HGATP64_MODE 0xF000000000000000 +#define HGATP64_VMID 0x03FFF00000000000 +#define HGATP64_PPN 0x00000FFFFFFFFFFF + +#define HGATP_MODE_OFF 0 +#define HGATP_MODE_SV32X4 1 +#define HGATP_MODE_SV39X4 8 +#define HGATP_MODE_SV48X4 9 + #define PMP_R 0x01 #define PMP_W 0x02 #define PMP_X 0x04 @@ -148,16 +186,17 @@ #define IRQ_U_SOFT 0 #define IRQ_S_SOFT 1 -#define IRQ_H_SOFT 2 +#define IRQ_VS_SOFT 2 #define IRQ_M_SOFT 3 #define IRQ_U_TIMER 4 #define IRQ_S_TIMER 5 -#define IRQ_H_TIMER 6 +#define IRQ_VS_TIMER 6 #define IRQ_M_TIMER 7 #define IRQ_U_EXT 8 #define IRQ_S_EXT 9 -#define IRQ_H_EXT 10 +#define IRQ_VS_EXT 10 #define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 #define IRQ_COP 12 #define IRQ_HOST 13 @@ -749,6 +788,32 @@ #define MASK_HFENCE_VVMA 0xfe007fff #define MATCH_HFENCE_GVMA 0x62000073 #define MASK_HFENCE_GVMA 0xfe007fff +#define MATCH_HLV_B 0x60004073 +#define MASK_HLV_B 0xfff0707f +#define MATCH_HLV_BU 0x60104073 +#define MASK_HLV_BU 0xfff0707f +#define MATCH_HLV_H 0x64004073 +#define MASK_HLV_H 0xfff0707f +#define MATCH_HLV_HU 0x64104073 +#define MASK_HLV_HU 0xfff0707f +#define MATCH_HLVX_HU 0x64304073 +#define MASK_HLVX_HU 0xfff0707f +#define MATCH_HLV_W 0x68004073 +#define MASK_HLV_W 0xfff0707f +#define MATCH_HLV_WU 0x68104073 +#define MASK_HLV_WU 0xfff0707f +#define MATCH_HLVX_WU 0x68304073 +#define MASK_HLVX_WU 0xfff0707f +#define MATCH_HLV_D 0x6c004073 +#define MASK_HLV_D 0xfff0707f +#define MATCH_HSV_B 0x62004073 +#define MASK_HSV_B 0xfe007fff +#define MATCH_HSV_H 0x66004073 +#define MASK_HSV_H 0xfe007fff +#define MATCH_HSV_W 0x6a004073 +#define MASK_HSV_W 0xfe007fff +#define MATCH_HSV_D 0x6e004073 +#define MASK_HSV_D 0xfe007fff #define MATCH_C_NOP 0x1 #define MASK_C_NOP 0xffff #define MATCH_C_ADDI16SP 0x6101 @@ -2017,12 +2082,16 @@ #define CAUSE_MISALIGNED_STORE 0x6 #define CAUSE_STORE_ACCESS 0x7 #define CAUSE_USER_ECALL 0x8 -#define CAUSE_SUPERVISOR_ECALL 0x9 -#define CAUSE_HYPERVISOR_ECALL 0xa +#define CAUSE_HYPERVISOR_ECALL 0x9 +#define CAUSE_SUPERVISOR_ECALL 0xa #define CAUSE_MACHINE_ECALL 0xb #define CAUSE_FETCH_PAGE_FAULT 0xc #define CAUSE_LOAD_PAGE_FAULT 0xd #define CAUSE_STORE_PAGE_FAULT 0xf +#define CAUSE_FETCH_GUEST_PAGE_FAULT 0x14 +#define CAUSE_LOAD_GUEST_PAGE_FAULT 0x15 +#define CAUSE_VIRTUAL_INSTRUCTION 0x16 +#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17 #endif #ifdef DECLARE_INSN DECLARE_INSN(slli_rv32, MATCH_SLLI_RV32, MASK_SLLI_RV32) @@ -2282,6 +2351,19 @@ DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI) DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI) DECLARE_INSN(hfence_vvma, MATCH_HFENCE_VVMA, MASK_HFENCE_VVMA) DECLARE_INSN(hfence_gvma, MATCH_HFENCE_GVMA, MASK_HFENCE_GVMA) +DECLARE_INSN(hlv_b, MATCH_HLV_B, MASK_HLV_B) +DECLARE_INSN(hlv_bu, MATCH_HLV_BU, MASK_HLV_BU) +DECLARE_INSN(hlv_h, MATCH_HLV_H, MASK_HLV_H) +DECLARE_INSN(hlv_hu, MATCH_HLV_HU, MASK_HLV_HU) +DECLARE_INSN(hlvx_hu, MATCH_HLVX_HU, MASK_HLVX_HU) +DECLARE_INSN(hlv_w, MATCH_HLV_W, MASK_HLV_W) +DECLARE_INSN(hlv_wu, MATCH_HLV_WU, MASK_HLV_WU) +DECLARE_INSN(hlvx_wu, MATCH_HLVX_WU, MASK_HLVX_WU) +DECLARE_INSN(hlv_d, MATCH_HLV_D, MASK_HLV_D) +DECLARE_INSN(hsv_b, MATCH_HSV_B, MASK_HSV_B) +DECLARE_INSN(hsv_h, MATCH_HSV_H, MASK_HSV_H) +DECLARE_INSN(hsv_w, MATCH_HSV_W, MASK_HSV_W) +DECLARE_INSN(hsv_d, MATCH_HSV_D, MASK_HSV_D) DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP) DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP) DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR) @@ -3066,4 +3148,8 @@ DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL) DECLARE_CAUSE("fetch page fault", CAUSE_FETCH_PAGE_FAULT) DECLARE_CAUSE("load page fault", CAUSE_LOAD_PAGE_FAULT) DECLARE_CAUSE("store page fault", CAUSE_STORE_PAGE_FAULT) +DECLARE_CAUSE("fetch guest page fault", CAUSE_FETCH_GUEST_PAGE_FAULT) +DECLARE_CAUSE("load guest page fault", CAUSE_LOAD_GUEST_PAGE_FAULT) +DECLARE_CAUSE("virtual instruction", CAUSE_VIRTUAL_INSTRUCTION) +DECLARE_CAUSE("store guest page fault", CAUSE_STORE_GUEST_PAGE_FAULT) #endif diff --git a/riscv/execute.cc b/riscv/execute.cc index 4ed2753..a6c7f10 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -347,7 +347,7 @@ void processor_t::step(size_t n) enter_debug_mode(DCSR_CAUSE_HWBP); break; case ACTION_DEBUG_EXCEPTION: { - mem_trap_t trap(CAUSE_BREAKPOINT, t.address); + insn_trap_t trap(CAUSE_BREAKPOINT, t.address); take_trap(trap, pc); break; } diff --git a/riscv/insns/c_ebreak.h b/riscv/insns/c_ebreak.h index a17200f..1c36b24 100644 --- a/riscv/insns/c_ebreak.h +++ b/riscv/insns/c_ebreak.h @@ -1,2 +1,2 @@ require_extension('C'); -throw trap_breakpoint(); +throw trap_breakpoint(0); diff --git a/riscv/insns/ebreak.h b/riscv/insns/ebreak.h index c22776c..f123f95 100644 --- a/riscv/insns/ebreak.h +++ b/riscv/insns/ebreak.h @@ -1 +1 @@ -throw trap_breakpoint(); +throw trap_breakpoint(0); diff --git a/riscv/insns/ecall.h b/riscv/insns/ecall.h index e298ac7..f5a064b 100644 --- a/riscv/insns/ecall.h +++ b/riscv/insns/ecall.h @@ -1,7 +1,11 @@ switch (STATE.prv) { case PRV_U: throw trap_user_ecall(); - case PRV_S: throw trap_supervisor_ecall(); + case PRV_S: + if (STATE.v) + throw trap_supervisor_ecall(); + else + throw trap_hypervisor_ecall(); case PRV_M: throw trap_machine_ecall(); default: abort(); } diff --git a/riscv/insns/hfence_gvma.h b/riscv/insns/hfence_gvma.h new file mode 100644 index 0000000..6ed039a --- /dev/null +++ b/riscv/insns/hfence_gvma.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(PRV_S); +require_novirt(); +MMU.flush_tlb(); diff --git a/riscv/insns/hfence_vvma.h b/riscv/insns/hfence_vvma.h new file mode 100644 index 0000000..6ed039a --- /dev/null +++ b/riscv/insns/hfence_vvma.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(PRV_S); +require_novirt(); +MMU.flush_tlb(); diff --git a/riscv/insns/hlv_b.h b/riscv/insns/hlv_b.h new file mode 100644 index 0000000..0fd63e4 --- /dev/null +++ b/riscv/insns/hlv_b.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_int8(RS1)); diff --git a/riscv/insns/hlv_bu.h b/riscv/insns/hlv_bu.h new file mode 100644 index 0000000..ae00b97 --- /dev/null +++ b/riscv/insns/hlv_bu.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_uint8(RS1)); diff --git a/riscv/insns/hlv_d.h b/riscv/insns/hlv_d.h new file mode 100644 index 0000000..cfad660 --- /dev/null +++ b/riscv/insns/hlv_d.h @@ -0,0 +1,5 @@ +require_extension('H'); +require_rv64; +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_int64(RS1)); diff --git a/riscv/insns/hlv_h.h b/riscv/insns/hlv_h.h new file mode 100644 index 0000000..bdfc6a7 --- /dev/null +++ b/riscv/insns/hlv_h.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_int16(RS1)); diff --git a/riscv/insns/hlv_hu.h b/riscv/insns/hlv_hu.h new file mode 100644 index 0000000..8f52eb3 --- /dev/null +++ b/riscv/insns/hlv_hu.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_uint16(RS1)); diff --git a/riscv/insns/hlv_w.h b/riscv/insns/hlv_w.h new file mode 100644 index 0000000..33bff44 --- /dev/null +++ b/riscv/insns/hlv_w.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_int32(RS1)); diff --git a/riscv/insns/hlv_wu.h b/riscv/insns/hlv_wu.h new file mode 100644 index 0000000..94a4553 --- /dev/null +++ b/riscv/insns/hlv_wu.h @@ -0,0 +1,5 @@ +require_extension('H'); +require_rv64; +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_uint32(RS1)); diff --git a/riscv/insns/hlvx_hu.h b/riscv/insns/hlvx_hu.h new file mode 100644 index 0000000..2050291 --- /dev/null +++ b/riscv/insns/hlvx_hu.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_x_uint16(RS1)); diff --git a/riscv/insns/hlvx_wu.h b/riscv/insns/hlvx_wu.h new file mode 100644 index 0000000..515b46e --- /dev/null +++ b/riscv/insns/hlvx_wu.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +WRITE_RD(MMU.guest_load_x_uint32(RS1)); diff --git a/riscv/insns/hsv_b.h b/riscv/insns/hsv_b.h new file mode 100644 index 0000000..e0f1162 --- /dev/null +++ b/riscv/insns/hsv_b.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +MMU.guest_store_uint8(RS1, RS2); diff --git a/riscv/insns/hsv_d.h b/riscv/insns/hsv_d.h new file mode 100644 index 0000000..6d41fb8 --- /dev/null +++ b/riscv/insns/hsv_d.h @@ -0,0 +1,5 @@ +require_extension('H'); +require_rv64; +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +MMU.guest_store_uint64(RS1, RS2); diff --git a/riscv/insns/hsv_h.h b/riscv/insns/hsv_h.h new file mode 100644 index 0000000..f4da613 --- /dev/null +++ b/riscv/insns/hsv_h.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +MMU.guest_store_uint16(RS1, RS2); diff --git a/riscv/insns/hsv_w.h b/riscv/insns/hsv_w.h new file mode 100644 index 0000000..efa7d97 --- /dev/null +++ b/riscv/insns/hsv_w.h @@ -0,0 +1,4 @@ +require_extension('H'); +require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_S : PRV_U); +require_novirt(); +MMU.guest_store_uint32(RS1, RS2); diff --git a/riscv/insns/mret.h b/riscv/insns/mret.h index 96933cf..cedfc72 100644 --- a/riscv/insns/mret.h +++ b/riscv/insns/mret.h @@ -2,8 +2,10 @@ require_privilege(PRV_M); set_pc_and_serialize(p->get_state()->mepc); reg_t s = STATE.mstatus; reg_t prev_prv = get_field(s, MSTATUS_MPP); +reg_t prev_virt = get_field(s, MSTATUS_MPV); s = set_field(s, MSTATUS_MIE, get_field(s, MSTATUS_MPIE)); s = set_field(s, MSTATUS_MPIE, 1); s = set_field(s, MSTATUS_MPP, PRV_U); -p->set_privilege(prev_prv); p->set_csr(CSR_MSTATUS, s); +p->set_privilege(prev_prv); +p->set_virt(prev_virt); diff --git a/riscv/insns/sret.h b/riscv/insns/sret.h index be837a3..bc063e6 100644 --- a/riscv/insns/sret.h +++ b/riscv/insns/sret.h @@ -1,10 +1,17 @@ require_extension('S'); require_privilege(get_field(STATE.mstatus, MSTATUS_TSR) ? PRV_M : PRV_S); -set_pc_and_serialize(p->get_state()->sepc); +if (STATE.v && get_field(STATE.hstatus, HSTATUS_VTSR)) + require_novirt(); +reg_t next_pc = (STATE.v) ? p->get_state()->vsepc : p->get_state()->sepc; +set_pc_and_serialize(next_pc); reg_t s = STATE.mstatus; reg_t prev_prv = get_field(s, MSTATUS_SPP); s = set_field(s, MSTATUS_SIE, get_field(s, MSTATUS_SPIE)); s = set_field(s, MSTATUS_SPIE, 1); s = set_field(s, MSTATUS_SPP, PRV_U); -p->set_privilege(prev_prv); p->set_csr(CSR_MSTATUS, s); +p->set_privilege(prev_prv); +if (!STATE.v) { + reg_t prev_virt = get_field(STATE.hstatus, HSTATUS_SPV); + p->set_virt(prev_virt); +} diff --git a/riscv/insns/wfi.h b/riscv/insns/wfi.h index 6504b78..5b1d1d6 100644 --- a/riscv/insns/wfi.h +++ b/riscv/insns/wfi.h @@ -1,2 +1,4 @@ require_privilege(get_field(STATE.mstatus, MSTATUS_TW) ? PRV_M : PRV_S); +if (STATE.v && get_field(STATE.hstatus, HSTATUS_VTW)) + require_novirt(); wfi(); diff --git a/riscv/mmu.cc b/riscv/mmu.cc index b038f23..e8dca6a 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -37,25 +37,37 @@ void mmu_t::flush_tlb() static void throw_access_exception(reg_t addr, access_type type) { switch (type) { - case FETCH: throw trap_instruction_access_fault(addr); - case LOAD: throw trap_load_access_fault(addr); - case STORE: throw trap_store_access_fault(addr); + case FETCH: throw trap_instruction_access_fault(addr, 0, 0); + case LOAD: throw trap_load_access_fault(addr, 0, 0); + case STORE: throw trap_store_access_fault(addr, 0, 0); default: abort(); } } -reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type) +reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type, uint32_t xlate_flags) { if (!proc) return addr; + bool mxr = get_field(proc->state.mstatus, MSTATUS_MXR); + bool virt = proc->state.v; reg_t mode = proc->state.prv; if (type != FETCH) { - if (!proc->state.debug_mode && get_field(proc->state.mstatus, MSTATUS_MPRV)) + if (!proc->state.debug_mode && get_field(proc->state.mstatus, MSTATUS_MPRV)) { mode = get_field(proc->state.mstatus, MSTATUS_MPP); + if (get_field(proc->state.mstatus, MSTATUS_MPV)) + virt = true; + } + if (!proc->state.debug_mode && (xlate_flags & RISCV_XLATE_VIRT)) { + virt = true; + mode = get_field(proc->state.hstatus, HSTATUS_SPVP); + if (type == LOAD && (xlate_flags & RISCV_XLATE_VIRT_MXR)) { + mxr = true; + } + } } - reg_t paddr = walk(addr, type, mode) | (addr & (PGSIZE-1)); + reg_t paddr = walk(addr, type, mode, virt, mxr) | (addr & (PGSIZE-1)); if (!pmp_ok(paddr, len, type, mode)) throw_access_exception(addr, type); return paddr; @@ -63,13 +75,13 @@ reg_t mmu_t::translate(reg_t addr, reg_t len, access_type type) tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr) { - reg_t paddr = translate(vaddr, sizeof(fetch_temp), FETCH); + reg_t paddr = translate(vaddr, sizeof(fetch_temp), FETCH, 0); if (auto host_addr = sim->addr_to_mem(paddr)) { return refill_tlb(vaddr, paddr, host_addr, FETCH); } else { if (!mmio_load(paddr, sizeof fetch_temp, (uint8_t*)&fetch_temp)) - throw trap_instruction_access_fault(vaddr); + throw trap_instruction_access_fault(vaddr, 0, 0); tlb_entry_t entry = {(char*)&fetch_temp - vaddr, paddr - vaddr}; return entry; } @@ -126,9 +138,9 @@ bool mmu_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes) return sim->mmio_store(addr, len, bytes); } -void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes) +void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes, uint32_t xlate_flags) { - reg_t paddr = translate(addr, len, LOAD); + reg_t paddr = translate(addr, len, LOAD, xlate_flags); if (auto host_addr = sim->addr_to_mem(paddr)) { memcpy(bytes, host_addr, len); @@ -137,7 +149,7 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes) else refill_tlb(addr, paddr, host_addr, LOAD); } else if (!mmio_load(paddr, len, bytes)) { - throw trap_load_access_fault(addr); + throw trap_load_access_fault(addr, 0, 0); } if (!matched_trigger) { @@ -148,9 +160,9 @@ void mmu_t::load_slow_path(reg_t addr, reg_t len, uint8_t* bytes) } } -void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes) +void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes, uint32_t xlate_flags) { - reg_t paddr = translate(addr, len, STORE); + reg_t paddr = translate(addr, len, STORE, xlate_flags); if (!matched_trigger) { reg_t data = reg_from_bytes(len, bytes); @@ -166,7 +178,7 @@ void mmu_t::store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes) else refill_tlb(addr, paddr, host_addr, STORE); } else if (!mmio_store(paddr, len, bytes)) { - throw trap_store_access_fault(addr); + throw trap_store_access_fault(addr, 0, 0); } } @@ -285,15 +297,82 @@ reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len) return true; } -reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode) +reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, bool virt, bool mxr) +{ + if (!virt) + return gpa; + + vm_info vm = decode_vm_info(proc->max_xlen, true, 0, proc->get_state()->hgatp); + if (vm.levels == 0) + return gpa; + + reg_t base = vm.ptbase; + for (int i = vm.levels - 1; i >= 0; i--) { + int ptshift = i * vm.idxbits; + int idxbits = (i == (vm.levels - 1)) ? vm.idxbits + vm.widenbits : vm.idxbits; + reg_t idx = (gpa >> (PGSHIFT + ptshift)) & ((reg_t(1) << idxbits) - 1); + + // check that physical address of PTE is legal + auto pte_paddr = base + idx * vm.ptesize; + auto ppte = sim->addr_to_mem(pte_paddr); + if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S)) { + throw_access_exception(gva, type); + } + + reg_t pte = vm.ptesize == 4 ? from_le(*(uint32_t*)ppte) : from_le(*(uint64_t*)ppte); + reg_t ppn = pte >> PTE_PPN_SHIFT; + + if (PTE_TABLE(pte)) { // next level of page table + base = ppn << PGSHIFT; + } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { + break; + } else if (!(pte & PTE_U)) { + break; + } else if (type == FETCH ? !(pte & PTE_X) : + type == LOAD ? !(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 { + reg_t ad = PTE_A | ((type == STORE) * PTE_D); +#ifdef RISCV_ENABLE_DIRTY + // set accessed and possibly dirty bits. + if ((pte & ad) != ad) { + if (!pmp_ok(pte_paddr, vm.ptesize, STORE, PRV_S)) + throw_access_exception(gva, type); + *(uint32_t*)ppte |= to_le((uint32_t)ad); + } +#else + // take exception if access or possibly dirty bit is not set. + if ((pte & ad) != ad) + break; +#endif + reg_t vpn = gpa >> PGSHIFT; + reg_t page_mask = (reg_t(1) << PGSHIFT) - 1; + reg_t page_base = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; + return page_base | (gpa & page_mask); + } + } + + switch (type) { + case FETCH: throw trap_instruction_guest_page_fault(gva, gpa >> 2, 0); + case LOAD: throw trap_load_guest_page_fault(gva, gpa >> 2, 0); + case STORE: throw trap_store_guest_page_fault(gva, gpa >> 2, 0); + default: abort(); + } +} + +reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool mxr) { - vm_info vm = decode_vm_info(proc->max_xlen, mode, proc->get_state()->satp); + reg_t page_mask = (reg_t(1) << PGSHIFT) - 1; + reg_t satp = (virt) ? proc->get_state()->vsatp : proc->get_state()->satp; + vm_info vm = decode_vm_info(proc->max_xlen, false, mode, satp); if (vm.levels == 0) - return addr & ((reg_t(2) << (proc->xlen-1))-1); // zero-extend from xlen + return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, virt, mxr) & ~page_mask; // zero-extend from xlen bool s_mode = mode == PRV_S; bool sum = get_field(proc->state.mstatus, MSTATUS_SUM); - bool mxr = get_field(proc->state.mstatus, MSTATUS_MXR); // verify bits xlen-1:va_bits-1 are all equal int va_bits = PGSHIFT + vm.levels * vm.idxbits; @@ -308,7 +387,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode) reg_t idx = (addr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1); // check that physical address of PTE is legal - auto pte_paddr = base + idx * vm.ptesize; + auto pte_paddr = s2xlate(addr, base + idx * vm.ptesize, LOAD, virt, false); auto ppte = sim->addr_to_mem(pte_paddr); if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S)) throw_access_exception(addr, type); @@ -344,15 +423,16 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode) #endif // for superpage mappings, make a fake leaf PTE for the TLB's benefit. reg_t vpn = addr >> PGSHIFT; - reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; - return value; + reg_t page_base = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; + reg_t phys = page_base | (addr & page_mask); + return s2xlate(addr, phys, type, virt, mxr) & ~page_mask; } } switch (type) { - case FETCH: throw trap_instruction_page_fault(addr); - case LOAD: throw trap_load_page_fault(addr); - case STORE: throw trap_store_page_fault(addr); + case FETCH: throw trap_instruction_page_fault(addr, 0, 0); + case LOAD: throw trap_load_page_fault(addr, 0, 0); + case STORE: throw trap_store_page_fault(addr, 0, 0); default: abort(); } } diff --git a/riscv/mmu.h b/riscv/mmu.h index f89d139..990f137 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -66,7 +66,7 @@ public: res += (reg_t)load_uint8(addr + i) << (i * 8); return res; #else - throw trap_load_address_misaligned(addr); + throw trap_load_address_misaligned(addr, 0, 0); #endif } @@ -76,7 +76,7 @@ public: for (size_t i = 0; i < size; i++) store_uint8(addr + i, data >> (i * 8)); #else - throw trap_store_address_misaligned(addr); + throw trap_store_address_misaligned(addr, 0, 0); #endif } @@ -87,9 +87,14 @@ public: proc->state.log_mem_read.push_back(std::make_tuple(addr, 0, size)); #endif +#define RISCV_XLATE_VIRT (1U << 0) +#define RISCV_XLATE_VIRT_MXR (1U << 1) + // template for functions that load an aligned value from memory - #define load_func(type) \ - inline type##_t load_##type(reg_t addr) { \ + #define load_func(type, prefix, xlate_flags) \ + inline type##_t prefix##_##type(reg_t addr) { \ + if (xlate_flags) \ + flush_tlb(); \ if (unlikely(addr & (sizeof(type##_t)-1))) \ return misaligned_load(addr, sizeof(type##_t)); \ reg_t vpn = addr >> PGSHIFT; \ @@ -109,22 +114,38 @@ public: return data; \ } \ type##_t res; \ - load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res); \ + load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res, (xlate_flags)); \ if (proc) READ_MEM(addr, size); \ + if (xlate_flags) \ + flush_tlb(); \ return from_le(res); \ } // load value from memory at aligned address; zero extend to register width - load_func(uint8) - load_func(uint16) - load_func(uint32) - load_func(uint64) + load_func(uint8, load, 0) + load_func(uint16, load, 0) + load_func(uint32, load, 0) + load_func(uint64, load, 0) + + // load value from guest memory at aligned address; zero extend to register width + load_func(uint8, guest_load, RISCV_XLATE_VIRT) + load_func(uint16, guest_load, RISCV_XLATE_VIRT) + load_func(uint32, guest_load, RISCV_XLATE_VIRT) + load_func(uint64, guest_load, RISCV_XLATE_VIRT) + load_func(uint16, guest_load_x, RISCV_XLATE_VIRT|RISCV_XLATE_VIRT_MXR) + load_func(uint32, guest_load_x, RISCV_XLATE_VIRT|RISCV_XLATE_VIRT_MXR) // load value from memory at aligned address; sign extend to register width - load_func(int8) - load_func(int16) - load_func(int32) - load_func(int64) + load_func(int8, load, 0) + load_func(int16, load, 0) + load_func(int32, load, 0) + load_func(int64, load, 0) + + // load value from guest memory at aligned address; sign extend to register width + load_func(int8, guest_load, RISCV_XLATE_VIRT) + load_func(int16, guest_load, RISCV_XLATE_VIRT) + load_func(int32, guest_load, RISCV_XLATE_VIRT) + load_func(int64, guest_load, RISCV_XLATE_VIRT) #ifndef RISCV_ENABLE_COMMITLOG # define WRITE_MEM(addr, value, size) ({}) @@ -134,8 +155,10 @@ public: #endif // template for functions that store an aligned value to memory - #define store_func(type) \ - void store_##type(reg_t addr, type##_t val) { \ + #define store_func(type, prefix, xlate_flags) \ + void prefix##_##type(reg_t addr, type##_t val) { \ + if (xlate_flags) \ + flush_tlb(); \ if (unlikely(addr & (sizeof(type##_t)-1))) \ return misaligned_store(addr, val, sizeof(type##_t)); \ reg_t vpn = addr >> PGSHIFT; \ @@ -154,10 +177,12 @@ public: *(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_le(val); \ } \ else { \ - type##_t le_val = to_le(val); \ - store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&le_val); \ + type##_t le_val = to_le(val); \ + store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&le_val, (xlate_flags)); \ if (proc) WRITE_MEM(addr, val, size); \ } \ + if (xlate_flags) \ + flush_tlb(); \ } // template for functions that perform an atomic memory operation @@ -165,17 +190,17 @@ public: template<typename op> \ type##_t amo_##type(reg_t addr, op f) { \ if (addr & (sizeof(type##_t)-1)) \ - throw trap_store_address_misaligned(addr); \ + throw trap_store_address_misaligned(addr, 0, 0); \ try { \ auto lhs = load_##type(addr); \ store_##type(addr, f(lhs)); \ return lhs; \ } catch (trap_load_page_fault& t) { \ /* AMO faults should be reported as store faults */ \ - throw trap_store_page_fault(t.get_tval()); \ + throw trap_store_page_fault(t.get_tval(), t.get_tval2(), t.get_tinst()); \ } catch (trap_load_access_fault& t) { \ /* AMO faults should be reported as store faults */ \ - throw trap_store_access_fault(t.get_tval()); \ + throw trap_store_access_fault(t.get_tval(), t.get_tval2(), t.get_tinst()); \ } \ } @@ -183,7 +208,7 @@ public: { #ifndef RISCV_ENABLE_MISALIGNED if (unlikely(addr & (sizeof(float128_t)-1))) - throw trap_store_address_misaligned(addr); + throw trap_store_address_misaligned(addr, 0, 0); #endif store_uint64(addr, val.v[0]); store_uint64(addr + 8, val.v[1]); @@ -193,16 +218,22 @@ public: { #ifndef RISCV_ENABLE_MISALIGNED if (unlikely(addr & (sizeof(float128_t)-1))) - throw trap_load_address_misaligned(addr); + throw trap_load_address_misaligned(addr, 0, 0); #endif return (float128_t){load_uint64(addr), load_uint64(addr + 8)}; } // store value to memory at aligned address - store_func(uint8) - store_func(uint16) - store_func(uint32) - store_func(uint64) + store_func(uint8, store, 0) + store_func(uint16, store, 0) + store_func(uint32, store, 0) + store_func(uint64, store, 0) + + // store value to guest memory at aligned address + store_func(uint8, guest_store, RISCV_XLATE_VIRT) + store_func(uint16, guest_store, RISCV_XLATE_VIRT) + store_func(uint32, guest_store, RISCV_XLATE_VIRT) + store_func(uint64, guest_store, RISCV_XLATE_VIRT) // perform an atomic memory operation at an aligned address amo_func(uint32) @@ -215,23 +246,23 @@ public: inline void acquire_load_reservation(reg_t vaddr) { - reg_t paddr = translate(vaddr, 1, LOAD); + reg_t paddr = translate(vaddr, 1, LOAD, 0); if (auto host_addr = sim->addr_to_mem(paddr)) load_reservation_address = refill_tlb(vaddr, paddr, host_addr, LOAD).target_offset + vaddr; else - throw trap_load_access_fault(vaddr); // disallow LR to I/O space + throw trap_load_access_fault(vaddr, 0, 0); // disallow LR to I/O space } inline bool check_load_reservation(reg_t vaddr, size_t size) { if (vaddr & (size-1)) - throw trap_store_address_misaligned(vaddr); + throw trap_store_address_misaligned(vaddr, 0, 0); - reg_t paddr = translate(vaddr, 1, STORE); + reg_t paddr = translate(vaddr, 1, STORE, 0); if (auto host_addr = sim->addr_to_mem(paddr)) return load_reservation_address == refill_tlb(vaddr, paddr, host_addr, STORE).target_offset + vaddr; else - throw trap_store_access_fault(vaddr); // disallow SC to I/O space + throw trap_store_access_fault(vaddr, 0, 0); // disallow SC to I/O space } static const reg_t ICACHE_ENTRIES = 1024; @@ -335,17 +366,20 @@ private: tlb_entry_t refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_type type); const char* fill_from_mmio(reg_t vaddr, reg_t paddr); + // perform a stage2 translation for a given guest address + reg_t s2xlate(reg_t gva, reg_t gpa, access_type type, bool virt, bool mxr); + // perform a page table walk for a given VA; set referenced/dirty bits - reg_t walk(reg_t addr, access_type type, reg_t prv); + reg_t walk(reg_t addr, access_type type, reg_t prv, bool virt, bool mxr); // handle uncommon cases: TLB misses, page faults, MMIO tlb_entry_t fetch_slow_path(reg_t addr); - void load_slow_path(reg_t addr, reg_t len, uint8_t* bytes); - void store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes); + void load_slow_path(reg_t addr, reg_t len, uint8_t* bytes, uint32_t xlate_flags); + void store_slow_path(reg_t addr, reg_t len, const uint8_t* bytes, uint32_t xlate_flags); bool mmio_load(reg_t addr, size_t len, uint8_t* bytes); bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes); bool mmio_ok(reg_t addr, access_type type); - reg_t translate(reg_t addr, reg_t len, access_type type); + reg_t translate(reg_t addr, reg_t len, access_type type, uint32_t xlate_flags); // ITLB lookup inline tlb_entry_t translate_insn_addr(reg_t addr) { @@ -402,27 +436,41 @@ private: struct vm_info { int levels; int idxbits; + int widenbits; int ptesize; reg_t ptbase; }; -inline vm_info decode_vm_info(int xlen, reg_t prv, reg_t satp) +inline vm_info decode_vm_info(int xlen, bool stage2, reg_t prv, reg_t satp) { if (prv == PRV_M) { - return {0, 0, 0, 0}; - } else if (prv <= PRV_S && xlen == 32) { + return {0, 0, 0, 0, 0}; + } else if (!stage2 && prv <= PRV_S && xlen == 32) { switch (get_field(satp, SATP32_MODE)) { - case SATP_MODE_OFF: return {0, 0, 0, 0}; - case SATP_MODE_SV32: return {2, 10, 4, (satp & SATP32_PPN) << PGSHIFT}; + case SATP_MODE_OFF: return {0, 0, 0, 0, 0}; + case SATP_MODE_SV32: return {2, 10, 0, 4, (satp & SATP32_PPN) << PGSHIFT}; default: abort(); } - } else if (prv <= PRV_S && xlen == 64) { + } else if (!stage2 && prv <= PRV_S && xlen == 64) { switch (get_field(satp, SATP64_MODE)) { - case SATP_MODE_OFF: return {0, 0, 0, 0}; - case SATP_MODE_SV39: return {3, 9, 8, (satp & SATP64_PPN) << PGSHIFT}; - case SATP_MODE_SV48: return {4, 9, 8, (satp & SATP64_PPN) << PGSHIFT}; - case SATP_MODE_SV57: return {5, 9, 8, (satp & SATP64_PPN) << PGSHIFT}; - case SATP_MODE_SV64: return {6, 9, 8, (satp & SATP64_PPN) << PGSHIFT}; + case SATP_MODE_OFF: return {0, 0, 0, 0, 0}; + case SATP_MODE_SV39: return {3, 9, 0, 8, (satp & SATP64_PPN) << PGSHIFT}; + case SATP_MODE_SV48: return {4, 9, 0, 8, (satp & SATP64_PPN) << PGSHIFT}; + case SATP_MODE_SV57: return {5, 9, 0, 8, (satp & SATP64_PPN) << PGSHIFT}; + case SATP_MODE_SV64: return {6, 9, 0, 8, (satp & SATP64_PPN) << PGSHIFT}; + default: abort(); + } + } else if (stage2 && xlen == 32) { + switch (get_field(satp, HGATP32_MODE)) { + case HGATP_MODE_OFF: return {0, 0, 0, 0, 0}; + case HGATP_MODE_SV32X4: return {2, 10, 2, 4, (satp & HGATP32_PPN) << PGSHIFT}; + default: abort(); + } + } else if (stage2 && xlen == 64) { + switch (get_field(satp, HGATP64_MODE)) { + case HGATP_MODE_OFF: return {0, 0, 0, 0, 0}; + case HGATP_MODE_SV39X4: return {3, 9, 2, 8, (satp & HGATP64_PPN) << PGSHIFT}; + case HGATP_MODE_SV48X4: return {4, 9, 2, 8, (satp & HGATP64_PPN) << PGSHIFT}; default: abort(); } } else { diff --git a/riscv/processor.cc b/riscv/processor.cc index b2b38d1..315a035 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -203,7 +203,7 @@ void processor_t::parse_isa_string(const char* str) char error_msg[256]; const char* p = lowercase.c_str(); - const char* all_subsets = "imafdqc" + const char* all_subsets = "imafdqch" #ifdef __SIZEOF_INT128__ "v" #endif @@ -312,6 +312,7 @@ void state_t::reset(reg_t max_isa) FPR.reset(); prv = PRV_M; + v = false; misa = max_isa; mstatus = 0; mepc = 0; @@ -332,6 +333,22 @@ void state_t::reset(reg_t max_isa) stvec = 0; satp = 0; scause = 0; + mtval2 = 0; + mtinst = 0; + hstatus = 0; + hideleg = 0; + hedeleg = 0; + hcounteren = 0; + htval = 0; + htinst = 0; + hgatp = 0; + vsstatus = 0; + vstvec = 0; + vsscratch = 0; + vsepc = 0; + vscause = 0; + vstval = 0; + vsatp = 0; dpc = 0; dscratch0 = 0; @@ -448,6 +465,8 @@ void processor_t::reset() { state.reset(max_isa); + state.mideleg = supports_extension('H') ? MIDELEG_FORCED_MASK : 0; + state.dcsr.halt = halt_on_reset; halt_on_reset = false; set_csr(CSR_MSTATUS, state.mstatus); @@ -499,15 +518,33 @@ void processor_t::set_pmp_granularity(reg_t gran) { void processor_t::take_interrupt(reg_t pending_interrupts) { - reg_t mie = get_field(state.mstatus, MSTATUS_MIE); - reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie); - reg_t enabled_interrupts = pending_interrupts & ~state.mideleg & -m_enabled; + reg_t enabled_interrupts, deleg, status, mie, m_enabled; + reg_t hsie, hs_enabled, vsie, vs_enabled; - reg_t sie = get_field(state.mstatus, MSTATUS_SIE); - reg_t s_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie); - // M-ints have highest priority; consider S-ints only if no M-ints pending - if (enabled_interrupts == 0) - enabled_interrupts = pending_interrupts & state.mideleg & -s_enabled; + // Do nothing if no pending interrupts + if (!pending_interrupts) { + return; + } + + // M-ints have higher priority over HS-ints and VS-ints + mie = get_field(state.mstatus, MSTATUS_MIE); + m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie); + enabled_interrupts = pending_interrupts & ~state.mideleg & -m_enabled; + if (enabled_interrupts == 0) { + // HS-ints have higher priority over VS-ints + deleg = state.mideleg & ~MIP_VS_MASK; + status = (state.v) ? state.vsstatus : state.mstatus; + hsie = get_field(status, MSTATUS_SIE); + hs_enabled = state.prv < PRV_S || (state.prv == PRV_S && hsie); + enabled_interrupts = pending_interrupts & deleg & -hs_enabled; + if (state.v && enabled_interrupts == 0) { + // VS-ints have least priority and can only be taken with virt enabled + deleg = state.mideleg & state.hideleg; + vsie = get_field(state.mstatus, MSTATUS_SIE); + vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && vsie); + enabled_interrupts = pending_interrupts & deleg & -vs_enabled; + } + } if (!state.debug_mode && enabled_interrupts) { // nonstandard interrupts have highest priority @@ -526,6 +563,12 @@ void processor_t::take_interrupt(reg_t pending_interrupts) enabled_interrupts = MIP_SSIP; else if (enabled_interrupts & MIP_STIP) enabled_interrupts = MIP_STIP; + else if (enabled_interrupts & MIP_VSEIP) + enabled_interrupts = MIP_VSEIP; + else if (enabled_interrupts & MIP_VSSIP) + enabled_interrupts = MIP_VSSIP; + else if (enabled_interrupts & MIP_VSTIP) + enabled_interrupts = MIP_VSTIP; else abort(); @@ -549,7 +592,7 @@ reg_t processor_t::legalize_privilege(reg_t prv) if (!supports_extension('U')) return PRV_M; - if (prv == PRV_H || (prv == PRV_S && !supports_extension('S'))) + if (prv == PRV_S && !supports_extension('S')) return PRV_U; return prv; @@ -561,6 +604,49 @@ void processor_t::set_privilege(reg_t prv) state.prv = legalize_privilege(prv); } +void processor_t::set_virt(bool virt) +{ + reg_t tmp, mask; + + if (state.prv == PRV_M) + return; + + if (state.v != virt) { + /* + * Ideally, we should flush TLB here but we don't need it because + * set_virt() is always used in conjucter with set_privilege() and + * set_privilege() will flush TLB unconditionally. + */ + if (state.v and !virt) { + /* + * When transitioning from virt-on (VS/VU) to virt-off (HS/M) + * we should sync Guest/VM FS, VS, and XS state with Host FS, + * VS, and XS state. + */ + if ((state.mstatus & SSTATUS_FS) == SSTATUS_FS) { + state.vsstatus |= SSTATUS_FS; + state.vsstatus |= (xlen == 64 ? SSTATUS64_SD : SSTATUS32_SD); + } + if ((state.mstatus & SSTATUS_VS) == SSTATUS_VS) { + state.vsstatus |= SSTATUS_VS; + state.vsstatus |= (xlen == 64 ? SSTATUS64_SD : SSTATUS32_SD); + } + if ((state.mstatus & SSTATUS_XS) == SSTATUS_XS) { + state.vsstatus |= SSTATUS_XS; + state.vsstatus |= (xlen == 64 ? SSTATUS64_SD : SSTATUS32_SD); + } + } + mask = SSTATUS_VS_MASK; + mask |= (supports_extension('F') ? SSTATUS_FS : 0); + mask |= (supports_extension('V') ? SSTATUS_VS : 0); + mask |= (xlen == 64 ? SSTATUS64_SD : SSTATUS32_SD); + tmp = state.mstatus & mask; + state.mstatus = (state.mstatus & ~mask) | (state.vsstatus & mask); + state.vsstatus = tmp; + state.v = virt; + } +} + void processor_t::enter_debug_mode(uint8_t cause) { state.debug_mode = true; @@ -598,37 +684,72 @@ void processor_t::take_trap(trap_t& t, reg_t epc) return; } - // by default, trap to M-mode, unless delegated to S-mode + // By default, trap to M-mode, unless delegated to HS-mode or VS-mode + reg_t vsdeleg, hsdeleg; reg_t bit = t.cause(); - reg_t deleg = state.medeleg; + bool curr_virt = state.v; bool interrupt = (bit & ((reg_t)1 << (max_xlen-1))) != 0; - if (interrupt) - deleg = state.mideleg, bit &= ~((reg_t)1 << (max_xlen-1)); - if (state.prv <= PRV_S && bit < max_xlen && ((deleg >> bit) & 1)) { - // handle the trap in S-mode + if (interrupt) { + vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.mideleg & state.hideleg) : 0; + hsdeleg = (state.prv <= PRV_S) ? state.mideleg : 0; + bit &= ~((reg_t)1 << (max_xlen-1)); + } else { + vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.medeleg & state.hedeleg) : 0; + hsdeleg = (state.prv <= PRV_S) ? state.medeleg : 0; + } + if (state.prv <= PRV_S && bit < max_xlen && ((vsdeleg >> bit) & 1)) { + // Handle the trap in VS-mode + reg_t vector = (state.vstvec & 1) && interrupt ? 4*bit : 0; + state.pc = (state.vstvec & ~(reg_t)1) + vector; + state.vscause = (interrupt) ? (t.cause() - 1) : t.cause(); + state.vsepc = epc; + state.vstval = t.get_tval(); + + reg_t s = state.mstatus; + s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); + s = set_field(s, MSTATUS_SPP, state.prv); + s = set_field(s, MSTATUS_SIE, 0); + set_csr(CSR_MSTATUS, s); + set_privilege(PRV_S); + } else if (state.prv <= PRV_S && bit < max_xlen && ((hsdeleg >> bit) & 1)) { + // Handle the trap in HS-mode + set_virt(false); reg_t vector = (state.stvec & 1) && interrupt ? 4*bit : 0; state.pc = (state.stvec & ~(reg_t)1) + vector; state.scause = t.cause(); state.sepc = epc; state.stval = t.get_tval(); + state.htval = t.get_tval2(); + state.htinst = t.get_tinst(); reg_t s = state.mstatus; s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); s = set_field(s, MSTATUS_SPP, state.prv); s = set_field(s, MSTATUS_SIE, 0); set_csr(CSR_MSTATUS, s); + s = state.hstatus; + s = set_field(s, HSTATUS_SPVP, state.prv); + s = set_field(s, HSTATUS_SPV, curr_virt); + s = set_field(s, HSTATUS_GVA, t.has_gva()); + set_csr(CSR_HSTATUS, s); set_privilege(PRV_S); } else { + // Handle the trap in M-mode + set_virt(false); reg_t vector = (state.mtvec & 1) && interrupt ? 4*bit : 0; state.pc = (state.mtvec & ~(reg_t)1) + vector; state.mepc = epc; state.mcause = t.cause(); state.mtval = t.get_tval(); + state.mtval2 = t.get_tval2(); + state.mtinst = t.get_tinst(); reg_t s = state.mstatus; s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE)); s = set_field(s, MSTATUS_MPP, state.prv); s = set_field(s, MSTATUS_MIE, 0); + s = set_field(s, MSTATUS_MPV, curr_virt); + s = set_field(s, MSTATUS_GVA, t.has_gva()); set_csr(CSR_MSTATUS, s); set_privilege(PRV_M); } @@ -669,9 +790,11 @@ void processor_t::set_csr(int which, reg_t val) val = zext_xlen(val); reg_t supervisor_ints = supports_extension('S') ? MIP_SSIP | MIP_STIP | MIP_SEIP : 0; + reg_t vssip_int = supports_extension('H') ? MIP_VSSIP : 0; + reg_t hypervisor_ints = supports_extension('H') ? MIP_HS_MASK : 0; reg_t coprocessor_ints = (ext != NULL) << IRQ_COP; reg_t delegable_ints = supervisor_ints | coprocessor_ints; - reg_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; + reg_t all_ints = delegable_ints | hypervisor_ints | MIP_MSIP | MIP_MTIP; if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.max_pmp) { // If no PMPs are configured, disallow access to all. Otherwise, allow @@ -736,13 +859,17 @@ void processor_t::set_csr(int which, reg_t val) bool has_fs = supports_extension('S') || supports_extension('F') || supports_extension('V'); bool has_vs = supports_extension('V'); + bool has_mpv = supports_extension('S') && supports_extension('H'); + bool has_gva = has_mpv; reg_t mask = MSTATUS_MIE | MSTATUS_MPIE | MSTATUS_MPRV | (supports_extension('S') ? (MSTATUS_SUM | MSTATUS_SIE | MSTATUS_SPIE) : 0) | MSTATUS_MXR | MSTATUS_TW | MSTATUS_TVM | MSTATUS_TSR | (has_fs ? MSTATUS_FS : 0) | (has_vs ? MSTATUS_VS : 0) - | (ext ? MSTATUS_XS : 0); + | (ext ? MSTATUS_XS : 0) + | (has_gva ? MSTATUS_GVA : 0) + | (has_mpv ? MSTATUS_MPV : 0); reg_t requested_mpp = legalize_privilege(get_field(val, MSTATUS_MPP)); state.mstatus = set_field(state.mstatus, MSTATUS_MPP, requested_mpp); @@ -768,7 +895,7 @@ void processor_t::set_csr(int which, reg_t val) break; } case CSR_MIP: { - reg_t mask = supervisor_ints & (MIP_SSIP | MIP_STIP); + reg_t mask = (supervisor_ints | hypervisor_ints) & (MIP_SSIP | MIP_STIP | vssip_int); state.mip = (state.mip & ~mask) | (val & mask); break; } @@ -783,9 +910,16 @@ void processor_t::set_csr(int which, reg_t val) (1 << CAUSE_MISALIGNED_FETCH) | (1 << CAUSE_BREAKPOINT) | (1 << CAUSE_USER_ECALL) | + (1 << CAUSE_SUPERVISOR_ECALL) | (1 << CAUSE_FETCH_PAGE_FAULT) | (1 << CAUSE_LOAD_PAGE_FAULT) | (1 << CAUSE_STORE_PAGE_FAULT); + mask |= supports_extension('H') ? + (1 << CAUSE_FETCH_GUEST_PAGE_FAULT) | + (1 << CAUSE_LOAD_GUEST_PAGE_FAULT) | + (1 << CAUSE_VIRTUAL_INSTRUCTION) | + (1 << CAUSE_STORE_GUEST_PAGE_FAULT) + : 0; state.medeleg = (state.medeleg & ~mask) | (val & mask); break; } @@ -819,33 +953,90 @@ void processor_t::set_csr(int which, reg_t val) return set_csr(CSR_MSTATUS, (state.mstatus & ~mask) | (val & mask)); } case CSR_SIP: { - reg_t mask = MIP_SSIP & state.mideleg; - return set_csr(CSR_MIP, (state.mip & ~mask) | (val & mask)); + reg_t mask; + if (state.v) { + mask = state.hideleg & MIP_VSSIP; + val = val << 1; + } else { + mask = state.mideleg & MIP_SSIP; + } + state.mip = (state.mip & ~mask) | (val & mask); + break; + } + case CSR_SIE: { + reg_t mask; + if (state.v) { + mask = state.hideleg & MIP_VS_MASK; + val = val << 1; + } else { + mask = state.mideleg & ~MIP_HS_MASK; + } + state.mie = (state.mie & ~mask) | (val & mask); + break; } - case CSR_SIE: - return set_csr(CSR_MIE, - (state.mie & ~state.mideleg) | (val & state.mideleg)); case CSR_SATP: { + reg_t reg_val = 0; reg_t rv64_ppn_mask = (reg_t(1) << (MAX_PADDR_BITS - PGSHIFT)) - 1; mmu->flush_tlb(); if (max_xlen == 32) - state.satp = val & (SATP32_PPN | SATP32_MODE); + reg_val = val & (SATP32_PPN | SATP32_MODE); if (max_xlen == 64 && (get_field(val, SATP64_MODE) == SATP_MODE_OFF || get_field(val, SATP64_MODE) == SATP_MODE_SV39 || get_field(val, SATP64_MODE) == SATP_MODE_SV48)) - state.satp = val & (SATP64_PPN | SATP64_MODE | rv64_ppn_mask); + reg_val = val & (SATP64_PPN | SATP64_MODE | rv64_ppn_mask); + if (state.v) + state.vsatp = reg_val; + else + state.satp = reg_val; break; } - case CSR_SEPC: state.sepc = val & ~(reg_t)1; break; - case CSR_STVEC: state.stvec = val & ~(reg_t)2; break; - case CSR_SSCRATCH: state.sscratch = val; break; - case CSR_SCAUSE: state.scause = val; break; - case CSR_STVAL: state.stval = val; break; + case CSR_SEPC: + if (state.v) + state.vsepc = val & ~(reg_t)1; + else + state.sepc = val & ~(reg_t)1; + break; + case CSR_STVEC: + if (state.v) + state.vstvec = val & ~(reg_t)2; + else + state.stvec = val & ~(reg_t)2; + break; + case CSR_SSCRATCH: + if (state.v) + state.vsscratch = val; + else + state.sscratch = val; + break; + case CSR_SCAUSE: + if (state.v) + state.vscause = val; + else + state.scause = val; + break; + case CSR_STVAL: + if (state.v) + state.vstval = val; + else + state.stval = val; + break; case CSR_MEPC: state.mepc = val & ~(reg_t)1; break; case CSR_MTVEC: state.mtvec = val & ~(reg_t)2; break; case CSR_MSCRATCH: state.mscratch = val; break; case CSR_MCAUSE: state.mcause = val; break; case CSR_MTVAL: state.mtval = val; break; + case CSR_MTVAL2: + if (supports_extension('H')) + state.mtval2 = val; + else + throw trap_illegal_instruction(0); + break; + case CSR_MTINST: + if (supports_extension('H')) + state.mtinst = val; + else + throw trap_illegal_instruction(0); + break; case CSR_MISA: { // the write is ignored if increasing IALIGN would misalign the PC if (!(val & (1L << ('C' - 'A'))) && (state.pc & 2)) @@ -861,11 +1052,118 @@ void processor_t::set_csr(int which, reg_t val) mask |= 1L << ('F' - 'A'); mask |= 1L << ('D' - 'A'); mask |= 1L << ('C' - 'A'); + mask |= 1L << ('H' - 'A'); mask &= max_isa; state.misa = (val & mask) | (state.misa & ~mask); break; } + case CSR_HSTATUS: { + reg_t mask = HSTATUS_VTSR | HSTATUS_VTW | HSTATUS_VTVM | + HSTATUS_HU | HSTATUS_SPVP | HSTATUS_SPV | HSTATUS_GVA; + state.hstatus = (state.hstatus & ~mask) | (val & mask); + break; + } + case CSR_HEDELEG: { + reg_t mask = + (1 << CAUSE_MISALIGNED_FETCH) | + (1 << CAUSE_BREAKPOINT) | + (1 << CAUSE_MISALIGNED_LOAD) | + (1 << CAUSE_LOAD_ACCESS) | + (1 << CAUSE_MISALIGNED_STORE) | + (1 << CAUSE_STORE_ACCESS) | + (1 << CAUSE_USER_ECALL) | + (1 << CAUSE_FETCH_PAGE_FAULT) | + (1 << CAUSE_LOAD_PAGE_FAULT) | + (1 << CAUSE_STORE_PAGE_FAULT); + state.hedeleg = (state.hedeleg & ~mask) | (val & mask); + break; + } + case CSR_HIDELEG: { + reg_t mask = MIP_VS_MASK; + state.hideleg = (state.hideleg & ~mask) | (val & mask); + break; + } + case CSR_HIE: { + reg_t mask = MIP_HS_MASK; + state.mie = (state.mie & ~mask) | (val & mask); + break; + } + case CSR_HTIMEDELTA: + case CSR_HTIMEDELTAH: + throw trap_illegal_instruction(0); + break; + case CSR_HCOUNTEREN: + state.hcounteren = val; + break; + case CSR_HGEIE: + /* Ignore */ + break; + case CSR_HTVAL: + state.htinst = val; + break; + case CSR_HIP: { + reg_t mask = MIP_VSSIP; + state.mip = (state.mip & ~mask) | (val & mask); + break; + } + case CSR_HVIP: { + reg_t mask = MIP_VS_MASK; + state.mip = (state.mip & ~mask) | (val & mask); + break; + } + case CSR_HTINST: + state.htinst = val; + break; + case CSR_HGATP: { + reg_t reg_val = 0; + reg_t rv64_ppn_mask = (reg_t(1) << (MAX_PADDR_BITS - PGSHIFT)) - 1; + mmu->flush_tlb(); + if (max_xlen == 32) + reg_val = val & (HGATP32_PPN | HGATP32_MODE); + if (max_xlen == 64 && (get_field(val, HGATP64_MODE) == HGATP_MODE_OFF || + get_field(val, HGATP64_MODE) == HGATP_MODE_SV39X4 || + get_field(val, HGATP64_MODE) == HGATP_MODE_SV48X4)) + reg_val = val & (HGATP64_PPN | HGATP64_MODE | rv64_ppn_mask); + state.hgatp = reg_val; + break; + } + case CSR_VSSTATUS: { + reg_t mask = SSTATUS_VS_MASK; + mask |= (supports_extension('F') ? SSTATUS_FS : 0); + mask |= (supports_extension('V') ? SSTATUS_VS : 0); + mask |= (xlen == 64 ? SSTATUS64_SD : SSTATUS32_SD); + state.vsstatus = (state.vsstatus & ~mask) | (val & mask); + break; + } + case CSR_VSIE: { + reg_t mask = state.hideleg & MIP_VS_MASK; + state.mie = (state.mie & ~mask) | ((val << 1) & mask); + break; + } + case CSR_VSTVEC: state.vstvec = val & ~(reg_t)2; break; + case CSR_VSSCRATCH: state.vsscratch = val; break; + case CSR_VSEPC: state.vsepc = val & ~(reg_t)1; break; + case CSR_VSCAUSE: state.vscause = val; break; + case CSR_VSTVAL: state.vstval = val; break; + case CSR_VSIP: { + reg_t mask = state.hideleg & MIP_VSSIP; + state.mip = (state.mip & ~mask) | ((val << 1) & mask); + break; + } + case CSR_VSATP: { + reg_t reg_val = 0; + reg_t rv64_ppn_mask = (reg_t(1) << (MAX_PADDR_BITS - PGSHIFT)) - 1; + mmu->flush_tlb(); + if (max_xlen == 32) + reg_val = val & (SATP32_PPN | SATP32_MODE); + if (max_xlen == 64 && (get_field(val, SATP64_MODE) == SATP_MODE_OFF || + get_field(val, SATP64_MODE) == SATP_MODE_SV39 || + get_field(val, SATP64_MODE) == SATP_MODE_SV48)) + reg_val = val & (SATP64_PPN | SATP64_MODE | rv64_ppn_mask); + state.vsatp = reg_val; + break; + } case CSR_TSELECT: if (val < state.num_triggers) { state.tselect = val; @@ -1029,6 +1327,8 @@ reg_t processor_t::get_csr(int which) uint32_t ctr_en = -1; if (state.prv < PRV_M) ctr_en &= state.mcounteren; + if (state.v) + ctr_en &= state.hcounteren; if (state.prv < PRV_S) ctr_en &= state.scounteren; bool ctr_ok = (ctr_en >> (which & 31)) & 1; @@ -1091,6 +1391,11 @@ reg_t processor_t::get_csr(int which) case CSR_CYCLE: if (ctr_ok) return state.minstret; + if (state.v && + ((state.mcounteren >> (which & 31)) & 1) && + !((state.hcounteren >> (which & 31)) & 1)) { + throw trap_virtual_instruction(0); + } break; case CSR_MINSTRET: case CSR_MCYCLE: @@ -1099,6 +1404,11 @@ reg_t processor_t::get_csr(int which) case CSR_CYCLEH: if (ctr_ok && xlen == 32) return state.minstret >> 32; + if (state.v && + ((state.mcounteren >> (which & 31)) & 1) && + !((state.hcounteren >> (which & 31)) & 1)) { + throw trap_virtual_instruction(0); + } break; case CSR_MINSTRETH: case CSR_MCYCLEH: @@ -1118,20 +1428,70 @@ reg_t processor_t::get_csr(int which) sstatus |= (xlen == 32 ? SSTATUS32_SD : SSTATUS64_SD); return sstatus; } - case CSR_SIP: return state.mip & state.mideleg; - case CSR_SIE: return state.mie & state.mideleg; - case CSR_SEPC: return state.sepc & pc_alignment_mask(); - case CSR_STVAL: return state.stval; - case CSR_STVEC: return state.stvec; - case CSR_SCAUSE: - if (max_xlen > xlen) - return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1)); - return state.scause; - case CSR_SATP: + case CSR_SIP: { + if (state.v) { + return (state.mip & state.hideleg & MIP_VS_MASK) >> 1; + } else { + return state.mip & state.mideleg & ~MIP_HS_MASK; + } + } + case CSR_SIE: { + if (state.v) { + return (state.mie & state.hideleg & MIP_VS_MASK) >> 1; + } else { + return state.mie & state.mideleg & ~MIP_HS_MASK; + } + } + case CSR_SEPC: { + if (state.v) { + return state.vsepc & pc_alignment_mask(); + } else { + return state.sepc & pc_alignment_mask(); + } + } + case CSR_STVAL: { + if (state.v) { + return state.vstval; + } else { + return state.stval; + } + } + case CSR_STVEC: { + if (state.v) { + return state.vstvec; + } else { + return state.stvec; + } + } + case CSR_SCAUSE: { + if (state.v) { + if (max_xlen > xlen) + return state.vscause | ((state.vscause >> (max_xlen-1)) << (xlen-1)); + return state.vscause; + } else { + if (max_xlen > xlen) + return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1)); + return state.scause; + } + } + case CSR_SATP: { if (get_field(state.mstatus, MSTATUS_TVM)) require_privilege(PRV_M); - return state.satp; - case CSR_SSCRATCH: return state.sscratch; + if (state.v) { + if (get_field(state.hstatus, HSTATUS_VTVM)) + throw trap_virtual_instruction(0); + return state.vsatp; + } else { + return state.satp; + } + } + case CSR_SSCRATCH: { + if (state.v) { + return state.vsscratch; + } else { + return state.sscratch; + } + } case CSR_MSTATUS: return state.mstatus; case CSR_MIP: return state.mip; case CSR_MIE: return state.mie; @@ -1139,6 +1499,14 @@ reg_t processor_t::get_csr(int which) case CSR_MSCRATCH: return state.mscratch; case CSR_MCAUSE: return state.mcause; case CSR_MTVAL: return state.mtval; + case CSR_MTVAL2: + if (supports_extension('H')) + return state.mtval2; + break; + case CSR_MTINST: + if (supports_extension('H')) + return state.mtinst; + break; case CSR_MISA: return state.misa; case CSR_MARCHID: return 5; case CSR_MIMPID: return 0; @@ -1153,6 +1521,33 @@ reg_t processor_t::get_csr(int which) if (!supports_extension('S')) break; return state.mideleg; + case CSR_HSTATUS: return state.hstatus; + case CSR_HEDELEG: return state.hedeleg; + case CSR_HIDELEG: return state.hideleg; + case CSR_HIE: return state.mie & MIP_HS_MASK; + case CSR_HCOUNTEREN: return state.hcounteren; + case CSR_HGEIE: return 0; + case CSR_HTVAL: return state.htval; + case CSR_HIP: return state.mip & MIP_HS_MASK; + case CSR_HVIP: return state.mip & MIP_VS_MASK; + case CSR_HTINST: return state.htinst; + case CSR_HGATP: return state.hgatp; + case CSR_HGEIP: return 0; + case CSR_VSSTATUS: { + reg_t mask = SSTATUS_VS_MASK; + mask |= (supports_extension('F') ? SSTATUS_FS : 0); + mask |= (supports_extension('V') ? SSTATUS_VS : 0); + mask |= (xlen == 64 ? SSTATUS64_SD : SSTATUS32_SD); + return state.vsstatus & mask; + } + case CSR_VSIE: return (state.mie & state.hideleg & MIP_VS_MASK) >> 1; + case CSR_VSTVEC: return state.vstvec; + case CSR_VSSCRATCH: return state.vsscratch; + case CSR_VSEPC: return state.vsepc & pc_alignment_mask(); + case CSR_VSCAUSE: return state.vscause; + case CSR_VSTVAL: return state.vstval; + case CSR_VSIP: return (state.mip & state.hideleg & MIP_VS_MASK) >> 1; + case CSR_VSATP: return state.vsatp; case CSR_TSELECT: return state.tselect; case CSR_TDATA1: if (state.tselect < state.num_triggers) { diff --git a/riscv/processor.h b/riscv/processor.h index 68e13d8..d8f3c79 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -164,6 +164,7 @@ struct state_t // control and status registers reg_t prv; // TODO: Can this be an enum instead? + bool v; reg_t misa; reg_t mstatus; reg_t mepc; @@ -185,6 +186,23 @@ struct state_t reg_t satp; reg_t scause; + reg_t mtval2; + reg_t mtinst; + reg_t hstatus; + reg_t hideleg; + reg_t hedeleg; + uint32_t hcounteren; + reg_t htval; + reg_t htinst; + reg_t hgatp; + reg_t vsstatus; + reg_t vstvec; + reg_t vsscratch; + reg_t vsepc; + reg_t vscause; + reg_t vstval; + reg_t vsatp; + reg_t dpc; reg_t dscratch0, dscratch1; dcsr_t dcsr; @@ -284,10 +302,11 @@ public: } void check_pc_alignment(reg_t pc) { if (unlikely(pc & ~pc_alignment_mask())) - throw trap_instruction_address_misaligned(pc); + throw trap_instruction_address_misaligned(pc, 0, 0); } reg_t legalize_privilege(reg_t); void set_privilege(reg_t); + void set_virt(bool); void update_histogram(reg_t pc); const disassembler_t* get_disassembler() { return disassembler; } diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 1aa3352..73c4cef 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -739,6 +739,23 @@ riscv_insn_ext_v = \ $(riscv_insn_ext_v_ctrl) \ $(riscv_insn_ext_v_ldst) \ +riscv_insn_ext_h = \ + hfence_gvma \ + hfence_vvma \ + hlv_b \ + hlv_bu \ + hlv_h \ + hlv_hu \ + hlvx_hu \ + hlv_w \ + hlv_wu \ + hlvx_wu \ + hlv_d \ + hsv_b \ + hsv_h \ + hsv_w \ + hsv_d \ + riscv_insn_priv = \ csrrc \ csrrci \ @@ -765,6 +782,7 @@ riscv_insn_list = \ $(riscv_insn_ext_zfh) \ $(riscv_insn_ext_q) \ $(if $(HAVE_INT128),$(riscv_insn_ext_v),) \ + $(riscv_insn_ext_h) \ $(riscv_insn_priv) \ riscv_gen_srcs = \ diff --git a/riscv/sim.cc b/riscv/sim.cc index b25d79e..2aced1b 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -28,7 +28,7 @@ static void handle_signal(int sig) sim_t::sim_t(const char* isa, const char* priv, const char* varch, size_t nprocs, bool halted, bool real_time_clint, - reg_t initrd_start, reg_t initrd_end, + reg_t initrd_start, reg_t initrd_end, const char* bootargs, reg_t start_pc, std::vector<std::pair<reg_t, mem_t*>> mems, std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices, const std::vector<std::string>& args, @@ -42,6 +42,7 @@ sim_t::sim_t(const char* isa, const char* priv, const char* varch, procs(std::max(nprocs, size_t(1))), initrd_start(initrd_start), initrd_end(initrd_end), + bootargs(bootargs), start_pc(start_pc), dtb_file(dtb_file ? dtb_file : ""), dtb_enabled(dtb_enabled), @@ -230,7 +231,7 @@ void sim_t::make_dtb() dtb = strstream.str(); } else { - dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems); + dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, bootargs, procs, mems); dtb = dts_compile(dts); } } @@ -271,7 +272,7 @@ void sim_t::set_rom() dtb = strstream.str(); } else { - dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems); + dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, bootargs, procs, mems); dtb = dts_compile(dts); } diff --git a/riscv/sim.h b/riscv/sim.h index 7cf83d6..c6e5582 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -25,7 +25,7 @@ class sim_t : public htif_t, public simif_t public: sim_t(const char* isa, const char* priv, const char* varch, size_t _nprocs, bool halted, bool real_time_clint, - reg_t initrd_start, reg_t initrd_end, + reg_t initrd_start, reg_t initrd_end, const char* bootargs, reg_t start_pc, std::vector<std::pair<reg_t, mem_t*>> mems, std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices, const std::vector<std::string>& args, const std::vector<int> hartids, @@ -64,6 +64,7 @@ private: std::vector<processor_t*> procs; reg_t initrd_start; reg_t initrd_end; + const char* bootargs; reg_t start_pc; std::string dts; std::string dtb; diff --git a/riscv/trap.h b/riscv/trap.h index ac048eb..824af6f 100644 --- a/riscv/trap.h +++ b/riscv/trap.h @@ -13,18 +13,23 @@ class trap_t public: trap_t(reg_t which) : which(which) {} virtual const char* name(); + virtual bool has_gva() { return false; } virtual bool has_tval() { return false; } virtual reg_t get_tval() { return 0; } + virtual bool has_tval2() { return false; } + virtual reg_t get_tval2() { return 0; } + virtual bool has_tinst() { return false; } + virtual reg_t get_tinst() { return 0; } reg_t cause() { return which; } private: char _name[16]; reg_t which; }; -class mem_trap_t : public trap_t +class insn_trap_t : public trap_t { public: - mem_trap_t(reg_t which, reg_t tval) + insn_trap_t(reg_t which, reg_t tval) : trap_t(which), tval(tval) {} bool has_tval() override { return true; } reg_t get_tval() override { return tval; } @@ -32,22 +37,45 @@ class mem_trap_t : public trap_t reg_t tval; }; +class mem_trap_t : public trap_t +{ + public: + mem_trap_t(reg_t which, bool gva, reg_t tval, reg_t tval2, reg_t tinst) + : trap_t(which), gva(gva), tval(tval), tval2(tval2), tinst(tinst) {} + bool has_gva() override { return gva; } + bool has_tval() override { return true; } + reg_t get_tval() override { return tval; } + bool has_tval2() override { return true; } + reg_t get_tval2() override { return tval2; } + bool has_tinst() override { return true; } + reg_t get_tinst() override { return tinst; } + private: + bool gva; + reg_t tval, tval2, tinst; +}; + #define DECLARE_TRAP(n, x) class trap_##x : public trap_t { \ public: \ trap_##x() : trap_t(n) {} \ const char* name() { return "trap_"#x; } \ }; +#define DECLARE_INST_TRAP(n, x) class trap_##x : public insn_trap_t { \ + public: \ + trap_##x(reg_t tval) : insn_trap_t(n, tval) {} \ + const char* name() { return "trap_"#x; } \ +}; + #define DECLARE_MEM_TRAP(n, x) class trap_##x : public mem_trap_t { \ public: \ - trap_##x(reg_t tval) : mem_trap_t(n, tval) {} \ + trap_##x(reg_t tval, reg_t tval2, reg_t tinst) : mem_trap_t(n, true, tval, tval2, tinst) {} \ const char* name() { return "trap_"#x; } \ }; DECLARE_MEM_TRAP(CAUSE_MISALIGNED_FETCH, instruction_address_misaligned) DECLARE_MEM_TRAP(CAUSE_FETCH_ACCESS, instruction_access_fault) -DECLARE_MEM_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) -DECLARE_TRAP(CAUSE_BREAKPOINT, breakpoint) +DECLARE_INST_TRAP(CAUSE_ILLEGAL_INSTRUCTION, illegal_instruction) +DECLARE_INST_TRAP(CAUSE_BREAKPOINT, breakpoint) DECLARE_MEM_TRAP(CAUSE_MISALIGNED_LOAD, load_address_misaligned) DECLARE_MEM_TRAP(CAUSE_MISALIGNED_STORE, store_address_misaligned) DECLARE_MEM_TRAP(CAUSE_LOAD_ACCESS, load_access_fault) @@ -59,5 +87,9 @@ DECLARE_TRAP(CAUSE_MACHINE_ECALL, machine_ecall) DECLARE_MEM_TRAP(CAUSE_FETCH_PAGE_FAULT, instruction_page_fault) DECLARE_MEM_TRAP(CAUSE_LOAD_PAGE_FAULT, load_page_fault) DECLARE_MEM_TRAP(CAUSE_STORE_PAGE_FAULT, store_page_fault) +DECLARE_MEM_TRAP(CAUSE_FETCH_GUEST_PAGE_FAULT, instruction_guest_page_fault) +DECLARE_MEM_TRAP(CAUSE_LOAD_GUEST_PAGE_FAULT, load_guest_page_fault) +DECLARE_INST_TRAP(CAUSE_VIRTUAL_INSTRUCTION, virtual_instruction) +DECLARE_MEM_TRAP(CAUSE_STORE_GUEST_PAGE_FAULT, store_guest_page_fault) #endif diff --git a/spike_main/spike.cc b/spike_main/spike.cc index d4e572c..78cd890 100644 --- a/spike_main/spike.cc +++ b/spike_main/spike.cc @@ -50,7 +50,9 @@ static void help(int exit_code = 1) fprintf(stderr, " --rbb-port=<port> Listen on <port> for remote bitbang connection\n"); fprintf(stderr, " --dump-dts Print device tree string and exit\n"); fprintf(stderr, " --disable-dtb Don't write the device tree blob into memory\n"); + fprintf(stderr, " --kernel=<path> Load kernel flat image into memory\n"); fprintf(stderr, " --initrd=<path> Load kernel initrd into memory\n"); + fprintf(stderr, " --bootargs=<args> Provide custom bootargs for kernel [default: console=hvc0 earlycon=sbi]\n"); fprintf(stderr, " --real-time-clint Increment clint time at real-time rate\n"); fprintf(stderr, " --dm-progsize=<words> Progsize for the debug module [default 2]\n"); fprintf(stderr, " --dm-sba=<bits> Debug bus master supports up to " @@ -189,8 +191,11 @@ int main(int argc, char** argv) bool dtb_enabled = true; bool real_time_clint = false; size_t nprocs = 1; + const char* kernel = NULL; + reg_t kernel_offset, kernel_size; size_t initrd_size; reg_t initrd_start = 0, initrd_end = 0; + const char* bootargs = NULL; reg_t start_pc = reg_t(-1); std::vector<std::pair<reg_t, mem_t*>> mems; std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices; @@ -300,7 +305,9 @@ int main(int argc, char** argv) parser.option(0, "dump-dts", 0, [&](const char *s){dump_dts = true;}); parser.option(0, "disable-dtb", 0, [&](const char *s){dtb_enabled = false;}); parser.option(0, "dtb", 1, [&](const char *s){dtb_file = s;}); + parser.option(0, "kernel", 1, [&](const char* s){kernel = s;}); parser.option(0, "initrd", 1, [&](const char* s){initrd = s;}); + parser.option(0, "bootargs", 1, [&](const char* s){bootargs = s;}); parser.option(0, "real-time-clint", 0, [&](const char *s){real_time_clint = true;}); parser.option(0, "extlib", 1, [&](const char *s){ void *lib = dlopen(s, RTLD_NOW | RTLD_GLOBAL); @@ -338,6 +345,20 @@ int main(int argc, char** argv) if (!*argv1) help(); + if (kernel && check_file_exists(kernel)) { + kernel_size = get_file_size(kernel); + if (isa[2] == '6' && isa[3] == '4') + kernel_offset = 0x200000; + else + kernel_offset = 0x400000; + for (auto& m : mems) { + if (kernel_size && (kernel_offset + kernel_size) < m.second->size()) { + read_file_bytes(kernel, 0, m.second->contents() + kernel_offset, kernel_size); + break; + } + } + } + if (initrd && check_file_exists(initrd)) { initrd_size = get_file_size(initrd); for (auto& m : mems) { @@ -351,7 +372,7 @@ int main(int argc, char** argv) } sim_t s(isa, priv, varch, nprocs, halted, real_time_clint, - initrd_start, initrd_end, start_pc, mems, plugin_devices, htif_args, + initrd_start, initrd_end, bootargs, start_pc, mems, plugin_devices, htif_args, std::move(hartids), dm_config, log_path, dtb_enabled, dtb_file); std::unique_ptr<remote_bitbang_t> remote_bitbang((remote_bitbang_t *) NULL); std::unique_ptr<jtag_dtm_t> jtag_dtm( |