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 *
vcpu0_thread(void * arg)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
sigalrm_handler(int sig)65 sigalrm_handler(int sig)
66 {
67 (void) pthread_cancel(vcpu0_tid);
68 timed_out = true;
69 }
70
71 static void
configure_timeout(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
main(int argc,char * argv[])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