1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "test_util.h"
3 #include "kvm_util.h"
4 #include "processor.h"
5 
6 #include <signal.h>
7 #include <string.h>
8 #include <sys/ioctl.h>
9 #include <sys/time.h>
10 
11 #include "kselftest.h"
12 
13 #define VCPU_ID	0
14 
15 static struct kvm_vm *vm;
16 
17 static void guest_ud_handler(struct ex_regs *regs)
18 {
19 	/* Loop on the ud2 until guest state is made invalid. */
20 }
21 
22 static void guest_code(void)
23 {
24 	asm volatile("ud2");
25 }
26 
27 static void __run_vcpu_with_invalid_state(void)
28 {
29 	struct kvm_run *run = vcpu_state(vm, VCPU_ID);
30 
31 	vcpu_run(vm, VCPU_ID);
32 
33 	TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
34 		    "Expected KVM_EXIT_INTERNAL_ERROR, got %d (%s)\n",
35 		    run->exit_reason, exit_reason_str(run->exit_reason));
36 	TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
37 		    "Expected emulation failure, got %d\n",
38 		    run->emulation_failure.suberror);
39 }
40 
41 static void run_vcpu_with_invalid_state(void)
42 {
43 	/*
44 	 * Always run twice to verify KVM handles the case where _KVM_ queues
45 	 * an exception with invalid state and then exits to userspace, i.e.
46 	 * that KVM doesn't explode if userspace ignores the initial error.
47 	 */
48 	__run_vcpu_with_invalid_state();
49 	__run_vcpu_with_invalid_state();
50 }
51 
52 static void set_timer(void)
53 {
54 	struct itimerval timer;
55 
56 	timer.it_value.tv_sec  = 0;
57 	timer.it_value.tv_usec = 200;
58 	timer.it_interval = timer.it_value;
59 	ASSERT_EQ(setitimer(ITIMER_REAL, &timer, NULL), 0);
60 }
61 
62 static void set_or_clear_invalid_guest_state(bool set)
63 {
64 	static struct kvm_sregs sregs;
65 
66 	if (!sregs.cr0)
67 		vcpu_sregs_get(vm, VCPU_ID, &sregs);
68 	sregs.tr.unusable = !!set;
69 	vcpu_sregs_set(vm, VCPU_ID, &sregs);
70 }
71 
72 static void set_invalid_guest_state(void)
73 {
74 	set_or_clear_invalid_guest_state(true);
75 }
76 
77 static void clear_invalid_guest_state(void)
78 {
79 	set_or_clear_invalid_guest_state(false);
80 }
81 
82 static void sigalrm_handler(int sig)
83 {
84 	struct kvm_vcpu_events events;
85 
86 	TEST_ASSERT(sig == SIGALRM, "Unexpected signal = %d", sig);
87 
88 	vcpu_events_get(vm, VCPU_ID, &events);
89 
90 	/*
91 	 * If an exception is pending, attempt KVM_RUN with invalid guest,
92 	 * otherwise rearm the timer and keep doing so until the timer fires
93 	 * between KVM queueing an exception and re-entering the guest.
94 	 */
95 	if (events.exception.pending) {
96 		set_invalid_guest_state();
97 		run_vcpu_with_invalid_state();
98 	} else {
99 		set_timer();
100 	}
101 }
102 
103 int main(int argc, char *argv[])
104 {
105 	if (!is_intel_cpu() || vm_is_unrestricted_guest(NULL)) {
106 		print_skip("Must be run with kvm_intel.unrestricted_guest=0");
107 		exit(KSFT_SKIP);
108 	}
109 
110 	vm = vm_create_default(VCPU_ID, 0, (void *)guest_code);
111 
112 	vm_init_descriptor_tables(vm);
113 	vcpu_init_descriptor_tables(vm, VCPU_ID);
114 
115 	vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler);
116 
117 	/*
118 	 * Stuff invalid guest state for L2 by making TR unusuable.  The next
119 	 * KVM_RUN should induce a TRIPLE_FAULT in L2 as KVM doesn't support
120 	 * emulating invalid guest state for L2.
121 	 */
122 	set_invalid_guest_state();
123 	run_vcpu_with_invalid_state();
124 
125 	/*
126 	 * Verify KVM also handles the case where userspace gains control while
127 	 * an exception is pending and stuffs invalid state.  Run with valid
128 	 * guest state and a timer firing every 200us, and attempt to enter the
129 	 * guest with invalid state when the handler interrupts KVM with an
130 	 * exception pending.
131 	 */
132 	clear_invalid_guest_state();
133 	TEST_ASSERT(signal(SIGALRM, sigalrm_handler) != SIG_ERR,
134 		    "Failed to register SIGALRM handler, errno = %d (%s)",
135 		    errno, strerror(errno));
136 
137 	set_timer();
138 	run_vcpu_with_invalid_state();
139 }
140