xref: /linux/tools/testing/selftests/kvm/x86/nested_invalid_cr3_test.c (revision 2bee2e6c983baa3605765621f26173ff0fa40365)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2025, Google LLC.
4  *
5  * This test verifies that L1 fails to enter L2 with an invalid CR3, and
6  * succeeds otherwise.
7  */
8 #include "kvm_util.h"
9 #include "vmx.h"
10 #include "svm_util.h"
11 #include "kselftest.h"
12 
13 
14 static void l2_guest_code(void)
15 {
16 	vmcall();
17 }
18 
19 static void l1_svm_code(struct svm_test_data *svm)
20 {
21 	uintptr_t save_cr3;
22 
23 	generic_svm_setup(svm, l2_guest_code);
24 
25 	/* Try to run L2 with invalid CR3 and make sure it fails */
26 	save_cr3 = svm->vmcb->save.cr3;
27 	svm->vmcb->save.cr3 = -1ull;
28 	run_guest(svm->vmcb, svm->vmcb_gpa);
29 	GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_ERR);
30 
31 	/* Now restore CR3 and make sure L2 runs successfully */
32 	svm->vmcb->save.cr3 = save_cr3;
33 	run_guest(svm->vmcb, svm->vmcb_gpa);
34 	GUEST_ASSERT(svm->vmcb->control.exit_code == SVM_EXIT_VMMCALL);
35 
36 	GUEST_DONE();
37 }
38 
39 static void l1_vmx_code(struct vmx_pages *vmx_pages)
40 {
41 	uintptr_t save_cr3;
42 
43 	GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages));
44 	GUEST_ASSERT(load_vmcs(vmx_pages));
45 
46 	prepare_vmcs(vmx_pages, l2_guest_code);
47 
48 	/* Try to run L2 with invalid CR3 and make sure it fails */
49 	save_cr3 = vmreadz(GUEST_CR3);
50 	vmwrite(GUEST_CR3, -1ull);
51 	GUEST_ASSERT(!vmlaunch());
52 	GUEST_ASSERT(vmreadz(VM_EXIT_REASON) ==
53 		     (EXIT_REASON_FAILED_VMENTRY | EXIT_REASON_INVALID_STATE));
54 
55 	/* Now restore CR3 and make sure L2 runs successfully */
56 	vmwrite(GUEST_CR3, save_cr3);
57 	GUEST_ASSERT(!vmlaunch());
58 	GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_VMCALL);
59 
60 	GUEST_DONE();
61 }
62 
63 static void l1_guest_code(void *data)
64 {
65 	if (this_cpu_has(X86_FEATURE_VMX))
66 		l1_vmx_code(data);
67 	else
68 		l1_svm_code(data);
69 }
70 
71 int main(int argc, char *argv[])
72 {
73 	struct kvm_vcpu *vcpu;
74 	struct kvm_vm *vm;
75 	gva_t guest_gva = 0;
76 
77 	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) ||
78 		     kvm_cpu_has(X86_FEATURE_SVM));
79 
80 	vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
81 
82 	if (kvm_cpu_has(X86_FEATURE_VMX))
83 		vcpu_alloc_vmx(vm, &guest_gva);
84 	else
85 		vcpu_alloc_svm(vm, &guest_gva);
86 
87 	vcpu_args_set(vcpu, 1, guest_gva);
88 
89 	for (;;) {
90 		struct ucall uc;
91 
92 		vcpu_run(vcpu);
93 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
94 
95 		switch (get_ucall(vcpu, &uc)) {
96 		case UCALL_ABORT:
97 			REPORT_GUEST_ASSERT(uc);
98 		case UCALL_SYNC:
99 			break;
100 		case UCALL_DONE:
101 			goto done;
102 		default:
103 			TEST_FAIL("Unknown ucall %lu", uc.cmd);
104 		}
105 	}
106 
107 done:
108 	kvm_vm_free(vm);
109 	return 0;
110 }
111