diff options
| author | Nadime Barhoumi <nadime@riscv.org> | 2026-04-24 16:46:28 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-24 13:46:28 -0700 |
| commit | 0bbecd1a01c61a16ad45fdfd89f29ebfdb493d1d (patch) | |
| tree | 44c99b561b63858df13f11463a140352d22d695a | |
| parent | 933a897d8631773f385d45938facc466dddc7514 (diff) | |
| download | riscv-tests-0bbecd1a01c61a16ad45fdfd89f29ebfdb493d1d.tar.gz riscv-tests-0bbecd1a01c61a16ad45fdfd89f29ebfdb493d1d.tar.bz2 riscv-tests-0bbecd1a01c61a16ad45fdfd89f29ebfdb493d1d.zip | |
Adds four RV64 hypervisor tests exercising implicit G-stage faults raised
during VS-stage page table walks:
- 2-stage_translation_implicit_load_error
- 2-stage_translation_implicit_load_error_hs
- 2-stage_translation_implicit_store_error
- 2-stage_translation_implicit_store_error_hs
The load variants trigger the fault by leaving the G-stage PTE invalid
(no PTE_V), causing the implicit PTE read during the VS-stage walk to
fault. The store variants omit PTE_W on the G-stage PTE, so G-stage
grants read but denies write, causing the implicit PTE store for the
A/D writeback to fail. These require hardware A/D updates, which occur
when Svadu is enabled via ADUE.
Each test checks mcause/scause, mtval/stval, and mtval2/htval and
verifies that mtinst/htinst contains the expected pseudoinstruction:
- 0x3000 — 64-bit implicit VS-stage PTE load
- 0x3020 — 64-bit implicit VS-stage PTE store
| -rw-r--r-- | isa/Makefile | 2 | ||||
| -rw-r--r-- | isa/hypervisor-svadu/2-stage_translation_implicit_store_error.S | 131 | ||||
| -rw-r--r-- | isa/hypervisor-svadu/2-stage_translation_implicit_store_error_hs.S | 138 | ||||
| -rw-r--r-- | isa/hypervisor-svadu/Makefrag | 9 | ||||
| -rw-r--r-- | isa/hypervisor/2-stage_translation_implicit_load_error.S | 121 | ||||
| -rw-r--r-- | isa/hypervisor/2-stage_translation_implicit_load_error_hs.S | 129 | ||||
| -rw-r--r-- | isa/hypervisor/Makefrag | 2 |
7 files changed, 532 insertions, 0 deletions
diff --git a/isa/Makefile b/isa/Makefile index 0f0106f..d5e67cd 100644 --- a/isa/Makefile +++ b/isa/Makefile @@ -26,6 +26,7 @@ include $(src_dir)/rv64ssvnapot/Makefrag include $(src_dir)/rv64mi/Makefrag include $(src_dir)/rv64mzicbo/Makefrag include $(src_dir)/hypervisor/Makefrag +include $(src_dir)/hypervisor-svadu/Makefrag else include $(src_dir)/rv32ui/Makefrag include $(src_dir)/rv32uc/Makefrag @@ -123,6 +124,7 @@ $(eval $(call compile_template,rv64si,-march=rv64g -mabi=lp64d)) $(eval $(call compile_template,rv64ssvnapot,-march=rv64g -mabi=lp64d)) $(eval $(call compile_template,rv64mi,-march=rv64g -mabi=lp64d)) $(eval $(call compile_template,hypervisor,-march=rv64gh -mabi=lp64d)) +$(eval $(call compile_template,hypervisor-svadu,-march=rv64gh -mabi=lp64d)) else $(eval $(call compile_template,rv32ui,-march=rv32g -mabi=ilp32)) $(eval $(call compile_template,rv32uc,-march=rv32g -mabi=ilp32)) diff --git a/isa/hypervisor-svadu/2-stage_translation_implicit_store_error.S b/isa/hypervisor-svadu/2-stage_translation_implicit_store_error.S new file mode 100644 index 0000000..460c8aa --- /dev/null +++ b/isa/hypervisor-svadu/2-stage_translation_implicit_store_error.S @@ -0,0 +1,131 @@ +# See LICENSE for license details. + +#***************************************************************************** +# 2-stage_translation_implicit_store_error.S +#----------------------------------------------------------------------------- +# +# Tests that an implicit G-stage store fault on PTE A/D writeback during +# a VS-stage page table walk is correctly reported to M-mode. +# + +#include "riscv_test.h" +#include "test_macros.h" + +#define STORE_PSEUDOINSTRUCTION_RV64 0x3020 + +# Address used as GVA, GPA, and SPA (identity-mapped gigapage) +#define IDENTITY_ADDR 0x80000000 + +RVTEST_RV64M +RVTEST_CODE_BEGIN + +li TESTNUM, 2 + +# Enable hardware A/D update (Svadu) in both M and HS environment +# configuration registers. Required for the A/D-bit writeback that +# triggers the implicit G-stage store fault under test. +machine_setup: + li t0, MENVCFG_HADE + csrs menvcfg, t0 + + li t0, HENVCFG_HADE + csrs henvcfg, t0 + +# Map GVA 0x80000000 -> GPA 0x80000000 +vs_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_V | PTE_R | PTE_W | PTE_X + la t1, vspt_0 + li t2, 2 # VPN[2] = 2 for GVA 0x80000000 under Sv39 + sll t2, t2, 3 # byte offset = 2 * 8 (PTE size) + add t1, t1, t2 # vspt_0 + byte offset + sd t0, 0(t1) # vspt_0[2] = pte + +init_vsatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, vspt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw vsatp, t1 + hfence.vvma + +# Map GPA 0x80000000 -> SPA 0x80000000 +# +# PTE_W is omitted so that G-stage grants read (the VS-stage walk can reach +# the leaf PTE) but denies write, causing the implicit PTE store for the +# A/D writeback to fail. +guest_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_V | PTE_R | PTE_X | PTE_U # No PTE_W + la t1, gpt_0 + li t2, 2 + sll t2, t2, 3 + add t1, t1, t2 + sd t0, 0(t1) + +init_hgatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, gpt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw hgatp, t1 + hfence.gvma + +hstatus_init: + li t0, HSTATUS_SPVP + csrs hstatus, t0 + + la t0, check_fault + csrw mtvec, t0 + + li t0, IDENTITY_ADDR + hsv.w t2, 0(t0) + + j fail # Should not reach here + +check_fault: + la t0, trap_vector + csrw mtvec, t0 + + # Check mcause + csrr t0, mcause + li t1, CAUSE_STORE_GUEST_PAGE_FAULT + bne t0, t1, fail + + # Check mtval + csrr t0, mtval + li t1, IDENTITY_ADDR + bne t0, t1, fail + + # Check mtval2 == faulting PTE address (&vspt_0[2]) >> 2 + la t0, vspt_0 + li t1, 2 + sll t1, t1, 3 + add t0, t0, t1 + srli t0, t0, 2 + csrr t2, mtval2 + bne t2, t0, fail + + # Check mtinst + csrr t0, mtinst + li t1, STORE_PSEUDOINSTRUCTION_RV64 + bne t0, t1, fail + + RVTEST_PASS + TEST_PASSFAIL + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA + +.align 12 +vspt_0: .dword 0 # 4 KiB, Sv39 root page table + +.align 14 +gpt_0: .fill 512, 8, 0 # 16 KiB, Sv39x4 G-stage root page table + +RVTEST_DATA_END diff --git a/isa/hypervisor-svadu/2-stage_translation_implicit_store_error_hs.S b/isa/hypervisor-svadu/2-stage_translation_implicit_store_error_hs.S new file mode 100644 index 0000000..b07febd --- /dev/null +++ b/isa/hypervisor-svadu/2-stage_translation_implicit_store_error_hs.S @@ -0,0 +1,138 @@ +# See LICENSE for license details. + +#***************************************************************************** +# 2-stage_translation_implicit_store_error_hs.S +#----------------------------------------------------------------------------- +# +# Tests that an implicit G-stage store fault on PTE A/D writeback during +# a VS-stage page table walk is correctly delegated to HS-mode. +# + +#include "riscv_test.h" +#include "test_macros.h" + +#define STORE_PSEUDOINSTRUCTION_RV64 0x3020 + +# Address used as GVA, GPA, and SPA (identity-mapped gigapage) +#define IDENTITY_ADDR 0x80000000 + +RVTEST_RV64M +RVTEST_CODE_BEGIN + +li TESTNUM, 2 + +# Enable hardware A/D update (Svadu) in both M and HS environment +# configuration registers. Required for the A/D-bit writeback that +# triggers the implicit G-stage store fault under test. +machine_setup: + li t0, MENVCFG_HADE + csrs menvcfg, t0 + + li t0, HENVCFG_HADE + csrs henvcfg, t0 + + RVTEST_ENABLE_SUPERVISOR + la a0, vs_pt_init + csrw mepc, a0 + + # Delegate to HS-Mode + li t0, (1 << CAUSE_STORE_GUEST_PAGE_FAULT) + csrw medeleg, t0 + + mret + +# Map GVA 0x80000000 -> GPA 0x80000000 +vs_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_V | PTE_R | PTE_W | PTE_X + la t1, vspt_0 + li t2, 2 # VPN[2] = 2 for GVA 0x80000000 under Sv39 + sll t2, t2, 3 # byte offset = 2 * 8 (PTE size) + add t1, t1, t2 # vspt_0 + byte offset + sd t0, 0(t1) # vspt_0[2] = pte + +init_vsatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, vspt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw vsatp, t1 + hfence.vvma + +# Map GPA 0x80000000 -> SPA 0x80000000 +# +# PTE_W is omitted so that G-stage grants read (the VS-stage walk can reach +# the leaf PTE) but denies write, causing the implicit PTE store for the +# A/D writeback to fail. +guest_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_V | PTE_R | PTE_X | PTE_U # No PTE_W + la t1, gpt_0 + li t2, 2 + sll t2, t2, 3 + add t1, t1, t2 + sd t0, 0(t1) + +init_hgatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, gpt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw hgatp, t1 + hfence.gvma + +hstatus_init: + li t0, HSTATUS_SPVP + csrs hstatus, t0 + + la t0, check_fault + csrw stvec, t0 + + li t0, IDENTITY_ADDR + hsv.w t2, 0(t0) + + j fail # Should not reach here + +check_fault: + # Check scause + csrr t0, scause + li t1, CAUSE_STORE_GUEST_PAGE_FAULT + bne t0, t1, fail + + # Check stval + csrr t0, stval + li t1, IDENTITY_ADDR + bne t0, t1, fail + + # Check htval == faulting PTE address (&vspt_0[2]) >> 2 + la t0, vspt_0 + li t1, 2 + sll t1, t1, 3 + add t0, t0, t1 + srli t0, t0, 2 + csrr t2, htval + bne t2, t0, fail + + # Check htinst + csrr t0, htinst + li t1, STORE_PSEUDOINSTRUCTION_RV64 + bne t0, t1, fail + + RVTEST_PASS + TEST_PASSFAIL + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA + +.align 12 +vspt_0: .dword 0 # 4 KiB, Sv39 root page table + +.align 14 +gpt_0: .fill 512, 8, 0 # 16 KiB, Sv39x4 G-stage root page table + +RVTEST_DATA_END diff --git a/isa/hypervisor-svadu/Makefrag b/isa/hypervisor-svadu/Makefrag new file mode 100644 index 0000000..4fc1704 --- /dev/null +++ b/isa/hypervisor-svadu/Makefrag @@ -0,0 +1,9 @@ +#======================================================================= +# Makefrag for hypervisor-svadu tests +#----------------------------------------------------------------------- + +hypervisor-svadu_sc_tests = \ + 2-stage_translation_implicit_store_error \ + 2-stage_translation_implicit_store_error_hs \ + +hypervisor-svadu_p_tests = $(addprefix hypervisor-svadu-p-, $(hypervisor-svadu_sc_tests)) diff --git a/isa/hypervisor/2-stage_translation_implicit_load_error.S b/isa/hypervisor/2-stage_translation_implicit_load_error.S new file mode 100644 index 0000000..fbd19ae --- /dev/null +++ b/isa/hypervisor/2-stage_translation_implicit_load_error.S @@ -0,0 +1,121 @@ +# See LICENSE for license details. + +#***************************************************************************** +# 2-stage_translation_implicit_load_error.S +#----------------------------------------------------------------------------- +# +# Tests that an implicit G-stage load fault during a VS-stage page table +# walk is correctly reported to M-mode. +# + +#include "riscv_test.h" +#include "test_macros.h" + +#define LOAD_PSEUDOINSTRUCTION_RV64 0x3000 + +# Address used as GVA, GPA, and SPA (identity-mapped gigapage) +#define IDENTITY_ADDR 0x80000000 + +RVTEST_RV64M +RVTEST_CODE_BEGIN + +li TESTNUM, 2 + +# Map GVA 0x80000000 -> GPA 0x80000000 +vs_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_V | PTE_R | PTE_W | PTE_X + la t1, vspt_0 + li t2, 2 # VPN[2] = 2 for GVA 0x80000000 under Sv39 + sll t2, t2, 3 # byte offset = 2 * 8 (PTE size) + add t1, t1, t2 # vspt_0 + byte offset + sd t0, 0(t1) # vspt_0[2] = pte + +init_vsatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, vspt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw vsatp, t1 + hfence.vvma + +# Map GPA 0x80000000 -> SPA 0x80000000 +# +# PTE_V is omitted so that G-stage translation of the VS-stage page table +# itself fails, causing the implicit PTE load during the VS-stage walk to +# fault. +guest_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_R | PTE_W | PTE_X | PTE_U # No PTE_V + la t1, gpt_0 + li t2, 2 + sll t2, t2, 3 + add t1, t1, t2 + sd t0, 0(t1) + +init_hgatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, gpt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw hgatp, t1 + hfence.gvma + +hstatus_init: + li t0, HSTATUS_SPVP + csrs hstatus, t0 + + la t0, check_fault + csrw mtvec, t0 + + li t0, IDENTITY_ADDR + hlv.w t2, 0(t0) + + j fail # Should not reach here + +check_fault: + la t0, trap_vector + csrw mtvec, t0 + + # Check mcause + csrr t0, mcause + li t1, CAUSE_LOAD_GUEST_PAGE_FAULT + bne t0, t1, fail + + # Check mtval + csrr t0, mtval + li t1, IDENTITY_ADDR + bne t0, t1, fail + + # Check mtval2 == faulting PTE address (&vspt_0[2]) >> 2 + la t0, vspt_0 + li t1, 2 + sll t1, t1, 3 + add t0, t0, t1 + srli t0, t0, 2 + csrr t2, mtval2 + bne t2, t0, fail + + # Check mtinst + csrr t0, mtinst + li t1, LOAD_PSEUDOINSTRUCTION_RV64 + bne t0, t1, fail + + RVTEST_PASS + TEST_PASSFAIL + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA + +.align 12 +vspt_0: .dword 0 # 4 KiB, Sv39 root page table + +.align 14 +gpt_0: .fill 512, 8, 0 # 16 KiB, Sv39x4 G-stage root page table + +RVTEST_DATA_END diff --git a/isa/hypervisor/2-stage_translation_implicit_load_error_hs.S b/isa/hypervisor/2-stage_translation_implicit_load_error_hs.S new file mode 100644 index 0000000..2808226 --- /dev/null +++ b/isa/hypervisor/2-stage_translation_implicit_load_error_hs.S @@ -0,0 +1,129 @@ +# See LICENSE for license details. + +#***************************************************************************** +# 2-stage_translation_implicit_load_error_hs.S +#----------------------------------------------------------------------------- +# +# Tests that an implicit G-stage load fault during a VS-stage page table +# walk is correctly delegated to HS-mode. +# + +#include "riscv_test.h" +#include "test_macros.h" + +#define LOAD_PSEUDOINSTRUCTION_RV64 0x3000 + +# Address used as GVA, GPA, and SPA (identity-mapped gigapage) +#define IDENTITY_ADDR 0x80000000 + +RVTEST_RV64M +RVTEST_CODE_BEGIN + +li TESTNUM, 2 + +machine_setup: + RVTEST_ENABLE_SUPERVISOR + la a0, vs_pt_init + csrw mepc, a0 + + # Delegate to HS-Mode + li t0, (1 << CAUSE_LOAD_GUEST_PAGE_FAULT) + csrw medeleg, t0 + + mret + +# Map GVA 0x80000000 -> GPA 0x80000000 +vs_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_V | PTE_R | PTE_W | PTE_X + la t1, vspt_0 + li t2, 2 # VPN[2] = 2 for GVA 0x80000000 under Sv39 + sll t2, t2, 3 # byte offset = 2 * 8 (PTE size) + add t1, t1, t2 # vspt_0 + byte offset + sd t0, 0(t1) # vspt_0[2] = pte + +init_vsatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, vspt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw vsatp, t1 + hfence.vvma + +# Map GPA 0x80000000 -> SPA 0x80000000 +# +# PTE_V is omitted so that G-stage translation of the VS-stage page table +# itself fails, causing the implicit PTE load during the VS-stage walk to +# fault. +guest_pt_init: + li t0, IDENTITY_ADDR + srl t0, t0, RISCV_PGSHIFT - PTE_PPN_SHIFT + ori t0, t0, PTE_R | PTE_W | PTE_X | PTE_U # No PTE_V + la t1, gpt_0 + li t2, 2 + sll t2, t2, 3 + add t1, t1, t2 + sd t0, 0(t1) + +init_hgatp: + li t0, (SATP_MODE & ~(SATP_MODE<<1)) * SATP_MODE_SV39 + la t1, gpt_0 + srl t1, t1, RISCV_PGSHIFT + or t1, t1, t0 + csrw hgatp, t1 + hfence.gvma + +hstatus_init: + li t0, HSTATUS_SPVP + csrs hstatus, t0 + + la t0, check_fault + csrw stvec, t0 + + li t0, IDENTITY_ADDR + hlv.w t2, 0(t0) + + j fail # Should not reach here + +check_fault: + # Check scause + csrr t0, scause + li t1, CAUSE_LOAD_GUEST_PAGE_FAULT + bne t0, t1, fail + + # Check stval + csrr t0, stval + li t1, IDENTITY_ADDR + bne t0, t1, fail + + # Check htval == faulting PTE address (&vspt_0[2]) >> 2 + la t0, vspt_0 + li t1, 2 + sll t1, t1, 3 + add t0, t0, t1 + srli t0, t0, 2 + csrr t2, htval + bne t2, t0, fail + + # Check htinst + csrr t0, htinst + li t1, LOAD_PSEUDOINSTRUCTION_RV64 + bne t0, t1, fail + + RVTEST_PASS + TEST_PASSFAIL + +RVTEST_CODE_END + +.data +RVTEST_DATA_BEGIN + TEST_DATA + +.align 12 +vspt_0: .dword 0 # 4 KiB, Sv39 root page table + +.align 14 +gpt_0: .fill 512, 8, 0 # 16 KiB, Sv39x4 G-stage root page table + +RVTEST_DATA_END diff --git a/isa/hypervisor/Makefrag b/isa/hypervisor/Makefrag index 08f711a..2a48722 100644 --- a/isa/hypervisor/Makefrag +++ b/isa/hypervisor/Makefrag @@ -4,5 +4,7 @@ hypervisor_sc_tests = \ 2-stage_translation \ + 2-stage_translation_implicit_load_error \ + 2-stage_translation_implicit_load_error_hs \ hypervisor_p_tests = $(addprefix hypervisor-p-, $(hypervisor_sc_tests)) |
