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 vmctx *ctx = arg; 45 46 struct vm_entry ventry = { 0 }; 47 struct vm_exit vexit = { 0 }; 48 49 do { 50 int err = vm_run(ctx, 0, &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 int err; 93 94 ctx = test_initialize(suite_name); 95 assert(ctx != NULL); 96 97 /* Activate vcpu0 as if it were running */ 98 err = vm_activate_cpu(ctx, 0); 99 if (err != 0) { 100 test_fail_errno(err, "could not activate vcpu0"); 101 } 102 103 /* 104 * Set unorthodox run-state for vcpu0: wait-for-SIPI 105 * This way it will dawdle in the kernel during VM_RUN, despite there 106 * being no code to execute. Normally the emulated APIC would not allow 107 * a CPU to SIPI itself, making this state impossible to reach. 108 */ 109 err = vm_set_run_state(ctx, 0, VRS_INIT, 0); 110 if (err != 0) { 111 test_fail_errno(err, "could not set vcpu0 run_state"); 112 } 113 114 /* Get the vCPU thread running (and stuck in the kernel)... */ 115 if (pthread_create(&vcpu0_tid, NULL, vcpu0_thread, (void *)ctx) != 0) { 116 test_fail_errno(errno, "could not create thread for vcpu0"); 117 } 118 119 /* configure a timeout in case the barrier failed */ 120 configure_timeout(); 121 122 /* ... then issue our barrier: */ 123 err = vm_vcpu_barrier(ctx, 0); 124 if (err != 0) { 125 test_fail_errno(err, "failed to issue vcpu barrier"); 126 } 127 128 void *status = NULL; 129 if (pthread_join(vcpu0_tid, &status) != 0) { 130 test_fail_errno(errno, "could not join thread for vcpu0"); 131 } 132 133 /* cancel any timeout now that thread was joined */ 134 (void) alarm(0); 135 136 if (timed_out) { 137 test_fail_msg("timed out while waiting for barrier\n"); 138 } 139 140 test_pass(); 141 } 142