1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2023 Oxide Computer Company 14 */ 15 16 #include <stdio.h> 17 #include <unistd.h> 18 #include <stdlib.h> 19 #include <fcntl.h> 20 #include <libgen.h> 21 #include <sys/stat.h> 22 #include <errno.h> 23 #include <err.h> 24 #include <assert.h> 25 #include <sys/sysmacros.h> 26 #include <stdbool.h> 27 #include <pthread.h> 28 #include <signal.h> 29 30 #include <sys/vmm.h> 31 #include <sys/vmm_dev.h> 32 #include <sys/vmm_data.h> 33 #include <vmmapi.h> 34 35 #include "common.h" 36 #include "in_guest.h" 37 38 static pthread_t vcpu0_tid; 39 static bool timed_out = false; 40 41 static void * 42 vcpu0_thread(void *arg) 43 { 44 struct vcpu *vcpu = arg; 45 46 struct vm_entry ventry = { 0 }; 47 struct vm_exit vexit = { 0 }; 48 49 do { 50 int err = vm_run(vcpu, &ventry, &vexit); 51 if (err != 0) { 52 test_fail_errno(err, "error during vm_run()"); 53 } 54 switch (vexit.exitcode) { 55 case VM_EXITCODE_BOGUS: 56 /* We expect a BOGUS exit from the barrier */ 57 return (NULL); 58 default: 59 test_fail_vmexit(&vexit); 60 } 61 } while (true); 62 } 63 64 static void 65 sigalrm_handler(int sig) 66 { 67 (void) pthread_cancel(vcpu0_tid); 68 timed_out = true; 69 } 70 71 static void 72 configure_timeout(void) 73 { 74 struct sigaction sa = { 75 .sa_handler = sigalrm_handler, 76 }; 77 struct sigaction old_sa; 78 if (sigaction(SIGALRM, &sa, &old_sa) != 0) { 79 test_fail_errno(errno, 80 "could not prep signal handling for bad access"); 81 } 82 83 /* set a simple 1s-in-the-future alarm */ 84 (void) alarm(1); 85 } 86 87 int 88 main(int argc, char *argv[]) 89 { 90 const char *suite_name = basename(argv[0]); 91 struct vmctx *ctx; 92 struct vcpu *vcpu; 93 int err; 94 95 ctx = test_initialize(suite_name); 96 assert(ctx != NULL); 97 98 if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) { 99 test_fail_errno(errno, "Could not open vcpu0"); 100 } 101 102 /* Activate vcpu0 as if it were running */ 103 err = vm_activate_cpu(vcpu); 104 if (err != 0) { 105 test_fail_errno(err, "could not activate vcpu0"); 106 } 107 108 /* 109 * Set unorthodox run-state for vcpu0: wait-for-SIPI 110 * This way it will dawdle in the kernel during VM_RUN, despite there 111 * being no code to execute. Normally the emulated APIC would not allow 112 * a CPU to SIPI itself, making this state impossible to reach. 113 */ 114 err = vm_set_run_state(vcpu, VRS_INIT, 0); 115 if (err != 0) { 116 test_fail_errno(err, "could not set vcpu0 run_state"); 117 } 118 119 /* Get the vCPU thread running (and stuck in the kernel)... */ 120 if (pthread_create(&vcpu0_tid, NULL, vcpu0_thread, (void *)vcpu) != 0) { 121 test_fail_errno(errno, "could not create thread for vcpu0"); 122 } 123 124 /* configure a timeout in case the barrier failed */ 125 configure_timeout(); 126 127 /* ... then issue our barrier: */ 128 err = vm_vcpu_barrier(vcpu); 129 if (err != 0) { 130 test_fail_errno(err, "failed to issue vcpu barrier"); 131 } 132 133 void *status = NULL; 134 if (pthread_join(vcpu0_tid, &status) != 0) { 135 test_fail_errno(errno, "could not join thread for vcpu0"); 136 } 137 138 /* cancel any timeout now that thread was joined */ 139 (void) alarm(0); 140 141 if (timed_out) { 142 test_fail_msg("timed out while waiting for barrier\n"); 143 } 144 145 test_pass(); 146 } 147