1  // SPDX-License-Identifier: GPL-2.0-only
2  #include "test_util.h"
3  #include "kvm_util.h"
4  #include "processor.h"
5  #include "vmx.h"
6  #include "svm_util.h"
7  
8  #include <string.h>
9  #include <sys/ioctl.h>
10  
11  #include "kselftest.h"
12  
13  #define ARBITRARY_IO_PORT	0x2000
14  
15  /* The virtual machine object. */
16  static struct kvm_vm *vm;
17  
l2_guest_code(void)18  static void l2_guest_code(void)
19  {
20  	asm volatile("inb %%dx, %%al"
21  		     : : [port] "d" (ARBITRARY_IO_PORT) : "rax");
22  }
23  
24  #define L2_GUEST_STACK_SIZE 64
25  unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
26  
l1_guest_code_vmx(struct vmx_pages * vmx)27  void l1_guest_code_vmx(struct vmx_pages *vmx)
28  {
29  
30  	GUEST_ASSERT(vmx->vmcs_gpa);
31  	GUEST_ASSERT(prepare_for_vmx_operation(vmx));
32  	GUEST_ASSERT(load_vmcs(vmx));
33  
34  	prepare_vmcs(vmx, l2_guest_code,
35  		     &l2_guest_stack[L2_GUEST_STACK_SIZE]);
36  
37  	GUEST_ASSERT(!vmlaunch());
38  	/* L2 should triple fault after a triple fault event injected. */
39  	GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT);
40  	GUEST_DONE();
41  }
42  
l1_guest_code_svm(struct svm_test_data * svm)43  void l1_guest_code_svm(struct svm_test_data *svm)
44  {
45  	struct vmcb *vmcb = svm->vmcb;
46  
47  	generic_svm_setup(svm, l2_guest_code,
48  			&l2_guest_stack[L2_GUEST_STACK_SIZE]);
49  
50  	/* don't intercept shutdown to test the case of SVM allowing to do so */
51  	vmcb->control.intercept &= ~(BIT(INTERCEPT_SHUTDOWN));
52  
53  	run_guest(vmcb, svm->vmcb_gpa);
54  
55  	/* should not reach here, L1 should crash  */
56  	GUEST_ASSERT(0);
57  }
58  
main(void)59  int main(void)
60  {
61  	struct kvm_vcpu *vcpu;
62  	struct kvm_run *run;
63  	struct kvm_vcpu_events events;
64  	struct ucall uc;
65  
66  	bool has_vmx = kvm_cpu_has(X86_FEATURE_VMX);
67  	bool has_svm = kvm_cpu_has(X86_FEATURE_SVM);
68  
69  	TEST_REQUIRE(has_vmx || has_svm);
70  
71  	TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_TRIPLE_FAULT_EVENT));
72  
73  
74  	if (has_vmx) {
75  		vm_vaddr_t vmx_pages_gva;
76  
77  		vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code_vmx);
78  		vcpu_alloc_vmx(vm, &vmx_pages_gva);
79  		vcpu_args_set(vcpu, 1, vmx_pages_gva);
80  	} else {
81  		vm_vaddr_t svm_gva;
82  
83  		vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code_svm);
84  		vcpu_alloc_svm(vm, &svm_gva);
85  		vcpu_args_set(vcpu, 1, svm_gva);
86  	}
87  
88  	vm_enable_cap(vm, KVM_CAP_X86_TRIPLE_FAULT_EVENT, 1);
89  	run = vcpu->run;
90  	vcpu_run(vcpu);
91  
92  	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
93  	TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT,
94  		    "Expected IN from port %d from L2, got port %d",
95  		    ARBITRARY_IO_PORT, run->io.port);
96  	vcpu_events_get(vcpu, &events);
97  	events.flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT;
98  	events.triple_fault.pending = true;
99  	vcpu_events_set(vcpu, &events);
100  	run->immediate_exit = true;
101  	vcpu_run_complete_io(vcpu);
102  
103  	vcpu_events_get(vcpu, &events);
104  	TEST_ASSERT(events.flags & KVM_VCPUEVENT_VALID_TRIPLE_FAULT,
105  		    "Triple fault event invalid");
106  	TEST_ASSERT(events.triple_fault.pending,
107  		    "No triple fault pending");
108  	vcpu_run(vcpu);
109  
110  
111  	if (has_svm) {
112  		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
113  	} else {
114  		switch (get_ucall(vcpu, &uc)) {
115  		case UCALL_DONE:
116  			break;
117  		case UCALL_ABORT:
118  			REPORT_GUEST_ASSERT(uc);
119  		default:
120  			TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
121  		}
122  	}
123  	return 0;
124  }
125