172473353SPatrick Mooney /*
272473353SPatrick Mooney  * This file and its contents are supplied under the terms of the
372473353SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
472473353SPatrick Mooney  * You may only use this file in accordance with the terms of version
572473353SPatrick Mooney  * 1.0 of the CDDL.
672473353SPatrick Mooney  *
772473353SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
872473353SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
972473353SPatrick Mooney  * http://www.illumos.org/license/CDDL.
1072473353SPatrick Mooney  */
1172473353SPatrick Mooney 
1272473353SPatrick Mooney /*
1372473353SPatrick Mooney  * Copyright 2023 Oxide Computer Company
1472473353SPatrick Mooney  */
1572473353SPatrick Mooney 
1672473353SPatrick Mooney #include <stdio.h>
1772473353SPatrick Mooney #include <unistd.h>
1872473353SPatrick Mooney #include <stdlib.h>
1972473353SPatrick Mooney #include <fcntl.h>
2072473353SPatrick Mooney #include <libgen.h>
2172473353SPatrick Mooney #include <sys/stat.h>
2272473353SPatrick Mooney #include <errno.h>
2372473353SPatrick Mooney #include <err.h>
2472473353SPatrick Mooney #include <assert.h>
2572473353SPatrick Mooney #include <sys/sysmacros.h>
2672473353SPatrick Mooney #include <stdbool.h>
2772473353SPatrick Mooney #include <pthread.h>
2872473353SPatrick Mooney #include <signal.h>
2972473353SPatrick Mooney 
3072473353SPatrick Mooney #include <sys/vmm.h>
3172473353SPatrick Mooney #include <sys/vmm_dev.h>
3272473353SPatrick Mooney #include <sys/vmm_data.h>
3372473353SPatrick Mooney #include <vmmapi.h>
3472473353SPatrick Mooney 
3572473353SPatrick Mooney #include "common.h"
3672473353SPatrick Mooney #include "in_guest.h"
3772473353SPatrick Mooney 
3872473353SPatrick Mooney static pthread_t vcpu0_tid;
3972473353SPatrick Mooney static bool timed_out = false;
4072473353SPatrick Mooney 
4172473353SPatrick Mooney static void *
vcpu0_thread(void * arg)4272473353SPatrick Mooney vcpu0_thread(void *arg)
4372473353SPatrick Mooney {
44*32640292SAndy Fiddaman 	struct vcpu *vcpu = arg;
4572473353SPatrick Mooney 
4672473353SPatrick Mooney 	struct vm_entry ventry = { 0 };
4772473353SPatrick Mooney 	struct vm_exit vexit = { 0 };
4872473353SPatrick Mooney 
4972473353SPatrick Mooney 	do {
50*32640292SAndy Fiddaman 		int err = vm_run(vcpu, &ventry, &vexit);
5172473353SPatrick Mooney 		if (err != 0) {
5272473353SPatrick Mooney 			test_fail_errno(err, "error during vm_run()");
5372473353SPatrick Mooney 		}
5472473353SPatrick Mooney 		switch (vexit.exitcode) {
5572473353SPatrick Mooney 		case VM_EXITCODE_BOGUS:
5672473353SPatrick Mooney 			/* We expect a BOGUS exit from the barrier */
5772473353SPatrick Mooney 			return (NULL);
5872473353SPatrick Mooney 		default:
5972473353SPatrick Mooney 			test_fail_vmexit(&vexit);
6072473353SPatrick Mooney 		}
6172473353SPatrick Mooney 	} while (true);
6272473353SPatrick Mooney }
6372473353SPatrick Mooney 
6472473353SPatrick Mooney static void
sigalrm_handler(int sig)6572473353SPatrick Mooney sigalrm_handler(int sig)
6672473353SPatrick Mooney {
6772473353SPatrick Mooney 	(void) pthread_cancel(vcpu0_tid);
6872473353SPatrick Mooney 	timed_out = true;
6972473353SPatrick Mooney }
7072473353SPatrick Mooney 
7172473353SPatrick Mooney static void
configure_timeout(void)7272473353SPatrick Mooney configure_timeout(void)
7372473353SPatrick Mooney {
7472473353SPatrick Mooney 	struct sigaction sa = {
7572473353SPatrick Mooney 		.sa_handler = sigalrm_handler,
7672473353SPatrick Mooney 	};
7772473353SPatrick Mooney 	struct sigaction old_sa;
7872473353SPatrick Mooney 	if (sigaction(SIGALRM, &sa, &old_sa) != 0) {
7972473353SPatrick Mooney 		test_fail_errno(errno,
8072473353SPatrick Mooney 		    "could not prep signal handling for bad access");
8172473353SPatrick Mooney 	}
8272473353SPatrick Mooney 
8372473353SPatrick Mooney 	/* set a simple 1s-in-the-future alarm */
8472473353SPatrick Mooney 	(void) alarm(1);
8572473353SPatrick Mooney }
8672473353SPatrick Mooney 
8772473353SPatrick Mooney int
main(int argc,char * argv[])8872473353SPatrick Mooney main(int argc, char *argv[])
8972473353SPatrick Mooney {
9072473353SPatrick Mooney 	const char *suite_name = basename(argv[0]);
9172473353SPatrick Mooney 	struct vmctx *ctx;
92*32640292SAndy Fiddaman 	struct vcpu *vcpu;
9372473353SPatrick Mooney 	int err;
9472473353SPatrick Mooney 
9572473353SPatrick Mooney 	ctx = test_initialize(suite_name);
9672473353SPatrick Mooney 	assert(ctx != NULL);
9772473353SPatrick Mooney 
98*32640292SAndy Fiddaman 	if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
99*32640292SAndy Fiddaman 		test_fail_errno(errno, "Could not open vcpu0");
100*32640292SAndy Fiddaman 	}
101*32640292SAndy Fiddaman 
10272473353SPatrick Mooney 	/* Activate vcpu0 as if it were running */
103*32640292SAndy Fiddaman 	err = vm_activate_cpu(vcpu);
10472473353SPatrick Mooney 	if (err != 0) {
10572473353SPatrick Mooney 		test_fail_errno(err, "could not activate vcpu0");
10672473353SPatrick Mooney 	}
10772473353SPatrick Mooney 
10872473353SPatrick Mooney 	/*
10972473353SPatrick Mooney 	 * Set unorthodox run-state for vcpu0: wait-for-SIPI
11072473353SPatrick Mooney 	 * This way it will dawdle in the kernel during VM_RUN, despite there
11172473353SPatrick Mooney 	 * being no code to execute.  Normally the emulated APIC would not allow
11272473353SPatrick Mooney 	 * a CPU to SIPI itself, making this state impossible to reach.
11372473353SPatrick Mooney 	 */
114*32640292SAndy Fiddaman 	err = vm_set_run_state(vcpu, VRS_INIT, 0);
11572473353SPatrick Mooney 	if (err != 0) {
11672473353SPatrick Mooney 		test_fail_errno(err, "could not set vcpu0 run_state");
11772473353SPatrick Mooney 	}
11872473353SPatrick Mooney 
11972473353SPatrick Mooney 	/* Get the vCPU thread running (and stuck in the kernel)... */
120*32640292SAndy Fiddaman 	if (pthread_create(&vcpu0_tid, NULL, vcpu0_thread, (void *)vcpu) != 0) {
12172473353SPatrick Mooney 		test_fail_errno(errno, "could not create thread for vcpu0");
12272473353SPatrick Mooney 	}
12372473353SPatrick Mooney 
12472473353SPatrick Mooney 	/* configure a timeout in case the barrier failed */
12572473353SPatrick Mooney 	configure_timeout();
12672473353SPatrick Mooney 
12772473353SPatrick Mooney 	/* ... then issue our barrier: */
128*32640292SAndy Fiddaman 	err = vm_vcpu_barrier(vcpu);
12972473353SPatrick Mooney 	if (err != 0) {
13072473353SPatrick Mooney 		test_fail_errno(err, "failed to issue vcpu barrier");
13172473353SPatrick Mooney 	}
13272473353SPatrick Mooney 
13372473353SPatrick Mooney 	void *status = NULL;
13472473353SPatrick Mooney 	if (pthread_join(vcpu0_tid, &status) != 0) {
13572473353SPatrick Mooney 		test_fail_errno(errno, "could not join thread for vcpu0");
13672473353SPatrick Mooney 	}
13772473353SPatrick Mooney 
13872473353SPatrick Mooney 	/* cancel any timeout now that thread was joined */
13972473353SPatrick Mooney 	(void) alarm(0);
14072473353SPatrick Mooney 
14172473353SPatrick Mooney 	if (timed_out) {
14272473353SPatrick Mooney 		test_fail_msg("timed out while waiting for barrier\n");
14372473353SPatrick Mooney 	}
14472473353SPatrick Mooney 
14572473353SPatrick Mooney 	test_pass();
14672473353SPatrick Mooney }
147