aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadime Barhoumi <nadime@riscv.org>2026-04-24 16:46:28 -0400
committerGitHub <noreply@github.com>2026-04-24 13:46:28 -0700
commit0bbecd1a01c61a16ad45fdfd89f29ebfdb493d1d (patch)
tree44c99b561b63858df13f11463a140352d22d695a
parent933a897d8631773f385d45938facc466dddc7514 (diff)
downloadriscv-tests-master.tar.gz
riscv-tests-master.tar.bz2
riscv-tests-master.zip
hypervisor: add tests for implicit G-stage faults during VS-stage walks (#651)HEADmaster
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/Makefile2
-rw-r--r--isa/hypervisor-svadu/2-stage_translation_implicit_store_error.S131
-rw-r--r--isa/hypervisor-svadu/2-stage_translation_implicit_store_error_hs.S138
-rw-r--r--isa/hypervisor-svadu/Makefrag9
-rw-r--r--isa/hypervisor/2-stage_translation_implicit_load_error.S121
-rw-r--r--isa/hypervisor/2-stage_translation_implicit_load_error_hs.S129
-rw-r--r--isa/hypervisor/Makefrag2
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))