Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/all-tests.dox
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,6 @@ states.
@subpage test-nested-svm - Nested SVM tests.

@subpage test-nested-vmx - Nested VT-x tests.

@subpage test-nested-svm-vmrun - Nested SVM VMRUN tests.
*/
11 changes: 11 additions & 0 deletions tests/nested-svm-vmrun/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
include $(ROOT)/build/common.mk

NAME := nested-svm-vmrun
CATEGORY := in-development
TEST-ENVS := hvm64

TEST-EXTRA-CFG := extra.cfg.in

obj-perenv += main.o entry.o

include $(ROOT)/build/gen.mk
37 changes: 37 additions & 0 deletions tests/nested-svm-vmrun/entry.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* SVM nested-virt VMRUN trampoline.
*
* Calling convention (System V AMD64):
* %rdi - physical address of the L2 (guest) VMCB
*
* - VMRUN auto-saves a subset of host state to the area
* pointed to by MSR_VM_HSAVE_PA and reloads it on #VMEXIT.
*
* - Fields outside that subset
* (FS/GS/LDTR/TR base, KernelGSBase, syscall MSRs, SYSENTER_*)
* are exchanged via VMSAVE/VMLOAD on the L2 VMCB itself:
*
* - VMLOAD before VMRUN loads L2's copy;
* - VMSAVE after #VMEXIT writes L2's back.
*
* Shared memory:
* - This minimal test relies on L1 and L2 sharing compatible flat FS/GS state.
* so host-side preservation of FS/GS is not required for this minimal test.
*
* Enables interrupt handling:
* 1. Nested guest execution requires GIF enabled for normal interrupt
* delivery. En entry, we STGI (enable interrupts: GIF=1) before VMRUN.
# 2. #VMEXIT leaves GIF clear again, so we it by STGI before returning to L1.
*/
#include <xtf/asm_macros.h>

ENTRY(svm_vmrun)
mov %rdi, %rax
vmload %rax
stgi
vmrun %rax
vmsave %rax
stgi

ret
ENDFUNC(svm_vmrun)
1 change: 1 addition & 0 deletions tests/nested-svm-vmrun/extra.cfg.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nestedhvm = 1
72 changes: 72 additions & 0 deletions tests/nested-svm-vmrun/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
Nested SVM VMRUN
================

This test exercises a minimal AMD nested-SVM guest entry path on hvm64.

An L1 guest enables SVM, builds an L2 VMCB that reuses L1's page tables and
descriptor tables, mirrors the active TR and LDTR state into the VMCB, and
enters L2 with VMRUN. The L2 payload runs on its own stack, writes a sentinel
into shared memory, and halts.

The test passes when L1 observes a HLT VMEXIT from L2 and the expected
sentinel value.

Why This Test Matters
---------------------

Nested-SVM support is only useful if an L1 guest can construct a valid VMCB
and successfully launch an L2 guest with VMRUN. This test covers that minimum
working path without depending on a large L2 payload or complex guest state.

That makes it a focused regression test for the architectural plumbing needed
for nested guest entry: SVM enablement in EFER, host-save area setup through
MSR_VM_HSAVE_PA, VMCB population, and the return path back to L1 via a nested
VMEXIT.

What It Verifies
----------------

The test verifies that Xen's nested-SVM implementation accepts a minimal but
architecturally valid L2 VMCB built by an L1 guest running in long mode.

More specifically, it verifies that:

* L1 can enable SVM and program the host-save area needed by VMRUN.
* L1 can populate an L2 VMCB with inherited control state, descriptor-table
state, and system-segment state taken from the live L1 environment.
* VMRUN succeeds in entering L2 rather than failing immediately because of
malformed guest state.
* L2 executes the supplied payload on its own stack, updates shared memory,
and exits through HLT.
* L1 receives a VMEXIT with exit code VMEXIT_HLT and observes the sentinel
value written by L2.

How The Verification Functions Work
-----------------------------------

The verification logic is split between the helpers that build a
valid VMCB and the final checks performed after returning from VMRUN.

``build_l2_vmcb()`` prepares the nested guest state. It programs the
required intercepts, reuses L1's paging and descriptor-table state,
assigns the L2 RIP and stack, and copies LDTR and TR from the current
GDT into the VMCB.

The helper ``vmcb_set_seg_desc()`` translates an L1 selector into the
VMCB segment format, while rejecting selectors that are null, LDT-based,
or out of bounds for the current GDT limit. This matters because VMRUN
consumes the VMCB's segment state directly, including the system
descriptors needed for long-mode execution.

``l2_entry()`` is the L2 payload. It avoids using VMMCALL, because
in Xen's nested-SVM model that would unconditionally cause a VMEXIT
to L1. Instead, it writes a known sentinel value into shared memory
and halts in a loop so that L1 sees a clean HLT exit reason.

``test_main()`` performs the end-to-end verification. It enables SVM,
writes the host-save area address to MSR_VM_HSAVE_PA, builds the VMCB,
and enters L2 through ``svm_vmrun()``.

When execution returns to L1, the test checks two conditions: the VMEXIT
reason must be ``VMEXIT_HLT``, and the shared handshake value must match
``L2_SENTINEL``. Both checks must pass for the test to report success.
Loading
Loading