xref: /illumos-gate/usr/src/test/bhyve-tests/tests/inst_emul/vcpu_barrier.c (revision c43efa7f6b109f90d7f4962df8c0e1a94008d2d1)
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