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