aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2020-07-09 13:48:17 -0700
committerGitHub <noreply@github.com>2020-07-09 13:48:17 -0700
commitecc039ef5726315b7e37e8a9c7b682bbe09c9cf5 (patch)
treeb4b2c5a4f545251f8ffde3633a93eea67f16398a
parent26eb6a2cbc1dfa7edf43e04a858af0de7870e650 (diff)
parentdff90a831ef3e393b2cea931515d25677fdced30 (diff)
downloadspike-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
-rw-r--r--riscv/decode.h15
-rw-r--r--riscv/dts.cc17
-rw-r--r--riscv/dts.h1
-rw-r--r--riscv/encoding.h104
-rw-r--r--riscv/execute.cc2
-rw-r--r--riscv/insns/c_ebreak.h2
-rw-r--r--riscv/insns/ebreak.h2
-rw-r--r--riscv/insns/ecall.h6
-rw-r--r--riscv/insns/hfence_gvma.h4
-rw-r--r--riscv/insns/hfence_vvma.h4
-rw-r--r--riscv/insns/hlv_b.h4
-rw-r--r--riscv/insns/hlv_bu.h4
-rw-r--r--riscv/insns/hlv_d.h5
-rw-r--r--riscv/insns/hlv_h.h4
-rw-r--r--riscv/insns/hlv_hu.h4
-rw-r--r--riscv/insns/hlv_w.h4
-rw-r--r--riscv/insns/hlv_wu.h5
-rw-r--r--riscv/insns/hlvx_hu.h4
-rw-r--r--riscv/insns/hlvx_wu.h4
-rw-r--r--riscv/insns/hsv_b.h4
-rw-r--r--riscv/insns/hsv_d.h5
-rw-r--r--riscv/insns/hsv_h.h4
-rw-r--r--riscv/insns/hsv_w.h4
-rw-r--r--riscv/insns/mret.h4
-rw-r--r--riscv/insns/sret.h11
-rw-r--r--riscv/insns/wfi.h2
-rw-r--r--riscv/mmu.cc128
-rw-r--r--riscv/mmu.h140
-rw-r--r--riscv/processor.cc481
-rw-r--r--riscv/processor.h21
-rw-r--r--riscv/riscv.mk.in18
-rw-r--r--riscv/sim.cc7
-rw-r--r--riscv/sim.h3
-rw-r--r--riscv/trap.h42
-rw-r--r--spike_main/spike.cc23
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(