xref: /linux/tools/testing/selftests/kvm/x86/evmcs_smm_controls_test.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2026, Red Hat, Inc.
4  *
5  * Test that vmx_leave_smm() validates vmcs12 controls before re-entering
6  * nested guest mode on RSM.
7  */
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/ioctl.h>
13 
14 #include "test_util.h"
15 #include "kvm_util.h"
16 #include "smm.h"
17 #include "hyperv.h"
18 #include "vmx.h"
19 
20 #define SMRAM_GPA	0x1000000
21 #define SMRAM_STAGE	0xfe
22 
23 #define SYNC_PORT	0xe
24 
25 /*
26  * SMI handler: runs in real-address mode.
27  * Reports SMRAM_STAGE via port IO, then does RSM.
28  */
29 static u8 smi_handler[] = {
30 	0xb0, SMRAM_STAGE,    /* mov $SMRAM_STAGE, %al */
31 	0xe4, SYNC_PORT,      /* in $SYNC_PORT, %al */
32 	0x0f, 0xaa,           /* rsm */
33 };
34 
35 static inline void sync_with_host(u64 phase)
36 {
37 	asm volatile("in $" __stringify(SYNC_PORT) ", %%al \n"
38 		     : "+a" (phase));
39 }
40 
41 static void l2_guest_code(void)
42 {
43 	sync_with_host(1);
44 
45 	/* After SMI+RSM with invalid controls, we should not reach here. */
46 	vmcall();
47 }
48 
49 static void guest_code(struct vmx_pages *vmx_pages,
50 		       struct hyperv_test_pages *hv_pages)
51 {
52 
53 	/* Set up Hyper-V enlightenments and eVMCS */
54 	wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
55 	enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist);
56 	evmcs_enable();
57 
58 	GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
59 	GUEST_ASSERT(load_evmcs(hv_pages));
60 	prepare_vmcs(vmx_pages, l2_guest_code);
61 
62 	GUEST_ASSERT(!vmlaunch());
63 
64 	/* L2 exits via vmcall if test fails */
65 	sync_with_host(2);
66 }
67 
68 int main(int argc, char *argv[])
69 {
70 	gva_t vmx_pages_gva = 0, hv_pages_gva = 0;
71 	struct hyperv_test_pages *hv;
72 	struct hv_enlightened_vmcs *evmcs;
73 	struct kvm_vcpu *vcpu;
74 	struct kvm_vm *vm;
75 	struct kvm_regs regs;
76 	int stage_reported;
77 
78 	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX));
79 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE));
80 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS));
81 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_SMM));
82 
83 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
84 
85 	setup_smram(vm, vcpu, SMRAM_GPA, smi_handler, sizeof(smi_handler));
86 
87 	vcpu_set_hv_cpuid(vcpu);
88 	vcpu_enable_evmcs(vcpu);
89 	vcpu_alloc_vmx(vm, &vmx_pages_gva);
90 	hv = vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva);
91 	vcpu_args_set(vcpu, 2, vmx_pages_gva, hv_pages_gva);
92 
93 	vcpu_run(vcpu);
94 
95 	/* L2 is running and syncs with host.  */
96 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
97 	vcpu_regs_get(vcpu, &regs);
98 	stage_reported = regs.rax & 0xff;
99 	TEST_ASSERT(stage_reported == 1,
100 		    "Expected stage 1, got %d", stage_reported);
101 
102 	/* Inject SMI while L2 is running.  */
103 	inject_smi(vcpu);
104 	vcpu_run(vcpu);
105 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
106 	vcpu_regs_get(vcpu, &regs);
107 	stage_reported = regs.rax & 0xff;
108 	TEST_ASSERT(stage_reported == SMRAM_STAGE,
109 		    "Expected SMM handler stage %#x, got %#x",
110 		    SMRAM_STAGE, stage_reported);
111 
112 	/*
113 	 * Guest is now paused in the SMI handler, about to execute RSM.
114 	 * Hack the eVMCS page to set-up invalid pin-based execution
115 	 * control (PIN_BASED_VIRTUAL_NMIS without PIN_BASED_NMI_EXITING).
116 	 */
117 	evmcs = hv->enlightened_vmcs_hva;
118 	evmcs->pin_based_vm_exec_control |= PIN_BASED_VIRTUAL_NMIS;
119 	evmcs->hv_clean_fields = 0;
120 
121 	/*
122 	 * Trigger copy_enlightened_to_vmcs12() via KVM_GET_NESTED_STATE,
123 	 * copying the invalid pin_based_vm_exec_control into cached_vmcs12.
124 	 */
125 	union {
126 		struct kvm_nested_state state;
127 		char state_[16384];
128 	} nested_state_buf;
129 
130 	memset(&nested_state_buf, 0, sizeof(nested_state_buf));
131 	nested_state_buf.state.size = sizeof(nested_state_buf);
132 	vcpu_nested_state_get(vcpu, &nested_state_buf.state);
133 
134 	/*
135 	 * Resume the guest.  The SMI handler executes RSM, which calls
136 	 * vmx_leave_smm().  nested_vmx_check_controls() should detect
137 	 * VIRTUAL_NMIS without NMI_EXITING and cause a triple fault.
138 	 */
139 	vcpu_run(vcpu);
140 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
141 
142 	kvm_vm_free(vm);
143 	return 0;
144 }
145