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 /*
1772473353SPatrick Mooney  * Check that supporting information for a VM_EXITCODE_SUSPENDED exit is correct
1872473353SPatrick Mooney  * for a vCPU-specific event (triple-fault).
1972473353SPatrick Mooney  */
2072473353SPatrick Mooney 
2172473353SPatrick Mooney #include <stdio.h>
2272473353SPatrick Mooney #include <unistd.h>
2372473353SPatrick Mooney #include <stdlib.h>
2472473353SPatrick Mooney #include <strings.h>
2572473353SPatrick Mooney #include <libgen.h>
2672473353SPatrick Mooney #include <assert.h>
2772473353SPatrick Mooney #include <pthread.h>
2872473353SPatrick Mooney #include <errno.h>
2972473353SPatrick Mooney 
3072473353SPatrick Mooney #include <sys/types.h>
3172473353SPatrick Mooney #include <sys/sysmacros.h>
3272473353SPatrick Mooney #include <sys/debug.h>
3372473353SPatrick Mooney #include <sys/vmm.h>
3472473353SPatrick Mooney #include <sys/vmm_dev.h>
3572473353SPatrick Mooney #include <vmmapi.h>
3672473353SPatrick Mooney 
3772473353SPatrick Mooney #include "in_guest.h"
3872473353SPatrick Mooney 
3972473353SPatrick Mooney #define	VCPU0_STACK	(MEM_LOC_STACK)
4072473353SPatrick Mooney #define	VCPU1_STACK	(MEM_LOC_STACK - 0x1000)
4172473353SPatrick Mooney 
4272473353SPatrick Mooney struct vcpu_thread_ctx {
43*32640292SAndy Fiddaman 	struct vcpu *vcpu;
4472473353SPatrick Mooney 	enum vm_suspend_how *howp;
4572473353SPatrick Mooney 	int *sourcep;
4672473353SPatrick Mooney };
4772473353SPatrick Mooney 
4872473353SPatrick Mooney static void *
vcpu0_thread(void * arg)4972473353SPatrick Mooney vcpu0_thread(void *arg)
5072473353SPatrick Mooney {
5172473353SPatrick Mooney 	struct vcpu_thread_ctx *vtc = arg;
52*32640292SAndy Fiddaman 	struct vcpu *vcpu = vtc->vcpu;
5372473353SPatrick Mooney 
5472473353SPatrick Mooney 	struct vm_entry ventry = { 0 };
5572473353SPatrick Mooney 	struct vm_exit vexit = { 0 };
5672473353SPatrick Mooney 
57*32640292SAndy Fiddaman 
5872473353SPatrick Mooney 	do {
5972473353SPatrick Mooney 		const enum vm_exit_kind kind =
60*32640292SAndy Fiddaman 		    test_run_vcpu(vcpu, &ventry, &vexit);
6172473353SPatrick Mooney 		switch (kind) {
6272473353SPatrick Mooney 		case VEK_REENTR:
6372473353SPatrick Mooney 			break;
6472473353SPatrick Mooney 		case VEK_UNHANDLED:
6572473353SPatrick Mooney 			if (vexit.exitcode != VM_EXITCODE_SUSPENDED) {
6672473353SPatrick Mooney 				test_fail_vmexit(&vexit);
6772473353SPatrick Mooney 			}
6872473353SPatrick Mooney 			*vtc->howp = vexit.u.suspended.how;
6972473353SPatrick Mooney 			*vtc->sourcep = vexit.u.suspended.source;
7072473353SPatrick Mooney 			return (NULL);
7172473353SPatrick Mooney 		default:
7272473353SPatrick Mooney 			test_fail_vmexit(&vexit);
7372473353SPatrick Mooney 		}
7472473353SPatrick Mooney 	} while (true);
7572473353SPatrick Mooney }
7672473353SPatrick Mooney 
7772473353SPatrick Mooney static void
vcpu0_setup(struct vcpu * vcpu)78*32640292SAndy Fiddaman vcpu0_setup(struct vcpu *vcpu)
7972473353SPatrick Mooney {
8072473353SPatrick Mooney 	int err;
8172473353SPatrick Mooney 
82*32640292SAndy Fiddaman 	err = test_setup_vcpu(vcpu, MEM_LOC_PAYLOAD, VCPU0_STACK);
8372473353SPatrick Mooney 	if (err != 0) {
8472473353SPatrick Mooney 		test_fail_errno(err, "Could not initialize vcpu0");
8572473353SPatrick Mooney 	}
86*32640292SAndy Fiddaman 	err = vm_set_register(vcpu, VM_REG_GUEST_RDI, 0);
8772473353SPatrick Mooney 	if (err != 0) {
8872473353SPatrick Mooney 		test_fail_errno(err, "failed to set %rdi");
8972473353SPatrick Mooney 	}
9072473353SPatrick Mooney }
9172473353SPatrick Mooney 
9272473353SPatrick Mooney static pthread_t
vcpu0_spawn(struct vcpu_thread_ctx * vtc)9372473353SPatrick Mooney vcpu0_spawn(struct vcpu_thread_ctx *vtc)
9472473353SPatrick Mooney {
9572473353SPatrick Mooney 	pthread_t tid;
9672473353SPatrick Mooney 	if (pthread_create(&tid, NULL, vcpu0_thread, (void *)vtc) != 0) {
9772473353SPatrick Mooney 		test_fail_errno(errno, "could not create thread for vcpu0");
9872473353SPatrick Mooney 	}
9972473353SPatrick Mooney 
10072473353SPatrick Mooney 	return (tid);
10172473353SPatrick Mooney }
10272473353SPatrick Mooney 
10372473353SPatrick Mooney static void
vcpu0_join(pthread_t tid)10472473353SPatrick Mooney vcpu0_join(pthread_t tid)
10572473353SPatrick Mooney {
10672473353SPatrick Mooney 	void *status = NULL;
10772473353SPatrick Mooney 	if (pthread_join(tid, &status) != 0) {
10872473353SPatrick Mooney 		test_fail_errno(errno, "could not join thread for vcpu0");
10972473353SPatrick Mooney 	}
11072473353SPatrick Mooney 	assert(status == NULL);
11172473353SPatrick Mooney }
11272473353SPatrick Mooney 
11372473353SPatrick Mooney static void
test_plain_suspend(struct vmctx * ctx,struct vcpu * vcpu,enum vm_suspend_how test_how)114*32640292SAndy Fiddaman test_plain_suspend(struct vmctx *ctx, struct vcpu *vcpu,
115*32640292SAndy Fiddaman     enum vm_suspend_how test_how)
11672473353SPatrick Mooney {
11772473353SPatrick Mooney 	enum vm_suspend_how how;
11872473353SPatrick Mooney 	int source;
11972473353SPatrick Mooney 	struct vcpu_thread_ctx vcpu0 = {
120*32640292SAndy Fiddaman 		.vcpu = vcpu,
12172473353SPatrick Mooney 		.howp = &how,
12272473353SPatrick Mooney 		.sourcep = &source,
12372473353SPatrick Mooney 	};
12472473353SPatrick Mooney 	pthread_t tid;
12572473353SPatrick Mooney 	int err;
12672473353SPatrick Mooney 
127*32640292SAndy Fiddaman 	vcpu0_setup(vcpu);
12872473353SPatrick Mooney 	tid = vcpu0_spawn(&vcpu0);
12972473353SPatrick Mooney 	err = vm_suspend(ctx, test_how);
13072473353SPatrick Mooney 	if (err != 0) {
13172473353SPatrick Mooney 		test_fail_errno(err, "vm_suspend() failure");
13272473353SPatrick Mooney 	}
13372473353SPatrick Mooney 	vcpu0_join(tid);
13472473353SPatrick Mooney 
13572473353SPatrick Mooney 	if (how != test_how) {
13672473353SPatrick Mooney 		test_fail_msg("Unexpected suspend how %d != %d\n",
13772473353SPatrick Mooney 		    how, test_how);
13872473353SPatrick Mooney 	}
13972473353SPatrick Mooney 	if (source != -1) {
14072473353SPatrick Mooney 		test_fail_msg("Unexpected suspend source %d != %d\n",
14172473353SPatrick Mooney 		    source, -1);
14272473353SPatrick Mooney 	}
14372473353SPatrick Mooney 
14472473353SPatrick Mooney 	/* Reset VM for another test */
14572473353SPatrick Mooney 	test_reinitialize(ctx, 0);
14672473353SPatrick Mooney }
14772473353SPatrick Mooney 
14872473353SPatrick Mooney static void
test_emitted_triplefault(struct vmctx * ctx,struct vcpu * vcpu)149*32640292SAndy Fiddaman test_emitted_triplefault(struct vmctx *ctx, struct vcpu *vcpu)
15072473353SPatrick Mooney {
15172473353SPatrick Mooney 	enum vm_suspend_how vcpu0_how;
15272473353SPatrick Mooney 	int vcpu0_source;
15372473353SPatrick Mooney 	struct vcpu_thread_ctx vcpu0 = {
154*32640292SAndy Fiddaman 		.vcpu = vcpu,
15572473353SPatrick Mooney 		.howp = &vcpu0_how,
15672473353SPatrick Mooney 		.sourcep = &vcpu0_source,
15772473353SPatrick Mooney 	};
158*32640292SAndy Fiddaman 	struct vcpu *vcpu1;
15972473353SPatrick Mooney 	int err;
16072473353SPatrick Mooney 	pthread_t tid;
16172473353SPatrick Mooney 
162*32640292SAndy Fiddaman 	vcpu0_setup(vcpu);
163*32640292SAndy Fiddaman 
164*32640292SAndy Fiddaman 	if ((vcpu1 = vm_vcpu_open(ctx, 1)) == NULL) {
165*32640292SAndy Fiddaman 		test_fail_errno(errno, "Could not open vcpu1");
166*32640292SAndy Fiddaman 	}
16772473353SPatrick Mooney 
16872473353SPatrick Mooney 	/* Setup vCPU1 like vCPU0, but with ID of 1 in %rdi */
169*32640292SAndy Fiddaman 	err = test_setup_vcpu(vcpu1, MEM_LOC_PAYLOAD, VCPU1_STACK);
17072473353SPatrick Mooney 	if (err != 0) {
17172473353SPatrick Mooney 		test_fail_errno(err, "Could not initialize vcpu1");
17272473353SPatrick Mooney 	}
173*32640292SAndy Fiddaman 	err = vm_set_register(vcpu1, VM_REG_GUEST_RDI, 1);
17472473353SPatrick Mooney 	if (err != 0) {
17572473353SPatrick Mooney 		test_fail_errno(err, "failed to set %rdi");
17672473353SPatrick Mooney 	}
17772473353SPatrick Mooney 
17872473353SPatrick Mooney 	/*
17972473353SPatrick Mooney 	 * Get vcpu0 running on a separate thread, ready to have its day
18072473353SPatrick Mooney 	 * "ruined" by a triple-fault on vcpu1
18172473353SPatrick Mooney 	 */
18272473353SPatrick Mooney 	tid = vcpu0_spawn(&vcpu0);
18372473353SPatrick Mooney 
18472473353SPatrick Mooney 	struct vm_entry ventry = { 0 };
18572473353SPatrick Mooney 	struct vm_exit vexit = { 0 };
18672473353SPatrick Mooney 	do {
18772473353SPatrick Mooney 		const enum vm_exit_kind kind =
188*32640292SAndy Fiddaman 		    test_run_vcpu(vcpu1, &ventry, &vexit);
18972473353SPatrick Mooney 		switch (kind) {
19072473353SPatrick Mooney 		case VEK_REENTR:
19172473353SPatrick Mooney 			break;
19272473353SPatrick Mooney 		case VEK_UNHANDLED: {
19372473353SPatrick Mooney 			/* expect immediate triple-fault from ud2a */
19472473353SPatrick Mooney 			if (vexit.exitcode != VM_EXITCODE_SUSPENDED) {
19572473353SPatrick Mooney 				test_fail_vmexit(&vexit);
19672473353SPatrick Mooney 			}
19772473353SPatrick Mooney 			vcpu0_join(tid);
19872473353SPatrick Mooney 			const enum vm_suspend_how vcpu1_how =
19972473353SPatrick Mooney 			    vexit.u.suspended.how;
20072473353SPatrick Mooney 			const int vcpu1_source = vexit.u.suspended.source;
20172473353SPatrick Mooney 
20272473353SPatrick Mooney 			if (vcpu0_how != VM_SUSPEND_TRIPLEFAULT ||
20372473353SPatrick Mooney 			    vcpu0_how != vcpu1_how) {
20472473353SPatrick Mooney 				test_fail_msg("Unexpected 'how' for "
20572473353SPatrick Mooney 				    "triple-fault: vcpu0=%d, vcpu1=%d, "
20672473353SPatrick Mooney 				    "expected=%d",
20772473353SPatrick Mooney 				    vcpu0_how, vcpu1_how,
20872473353SPatrick Mooney 				    VM_SUSPEND_TRIPLEFAULT);
20972473353SPatrick Mooney 			}
21072473353SPatrick Mooney 			if (vcpu0_source != 1 ||
21172473353SPatrick Mooney 			    vcpu0_source != vcpu1_source) {
21272473353SPatrick Mooney 				test_fail_msg("Unexpected 'source' for "
21372473353SPatrick Mooney 				    "triple-fault: vcpu0=%d, vcpu1=%d, "
21472473353SPatrick Mooney 				    "expected=%d",
21572473353SPatrick Mooney 				    vcpu0_source, vcpu1_source, 1);
21672473353SPatrick Mooney 			}
21772473353SPatrick Mooney 			return;
21872473353SPatrick Mooney 		}
21972473353SPatrick Mooney 
22072473353SPatrick Mooney 		default:
22172473353SPatrick Mooney 			test_fail_vmexit(&vexit);
22272473353SPatrick Mooney 			break;
22372473353SPatrick Mooney 		}
22472473353SPatrick Mooney 	} while (true);
22572473353SPatrick Mooney }
22672473353SPatrick Mooney 
22772473353SPatrick Mooney int
main(int argc,char * argv[])22872473353SPatrick Mooney main(int argc, char *argv[])
22972473353SPatrick Mooney {
23072473353SPatrick Mooney 	const char *test_suite_name = basename(argv[0]);
23172473353SPatrick Mooney 	struct vmctx *ctx = NULL;
232*32640292SAndy Fiddaman 	struct vcpu *vcpu;
23372473353SPatrick Mooney 
23472473353SPatrick Mooney 	ctx = test_initialize(test_suite_name);
23572473353SPatrick Mooney 
236*32640292SAndy Fiddaman 	if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
237*32640292SAndy Fiddaman 		test_fail_errno(errno, "Could not open vcpu0");
238*32640292SAndy Fiddaman 	}
239*32640292SAndy Fiddaman 
24072473353SPatrick Mooney 	/*
24172473353SPatrick Mooney 	 * Try injecting the various suspend types, and confirm that vcpu0 exits
24272473353SPatrick Mooney 	 * with the expected details.
24372473353SPatrick Mooney 	 */
244*32640292SAndy Fiddaman 	test_plain_suspend(ctx, vcpu, VM_SUSPEND_RESET);
245*32640292SAndy Fiddaman 	test_plain_suspend(ctx, vcpu, VM_SUSPEND_POWEROFF);
246*32640292SAndy Fiddaman 	test_plain_suspend(ctx, vcpu, VM_SUSPEND_HALT);
24772473353SPatrick Mooney 
24872473353SPatrick Mooney 	/*
24972473353SPatrick Mooney 	 * Let vCPU1 generate a triple-fault, and confirm that it is emitted by
25072473353SPatrick Mooney 	 * both exiting vCPU threads, with the proper details.
25172473353SPatrick Mooney 	 */
252*32640292SAndy Fiddaman 	test_emitted_triplefault(ctx, vcpu);
25372473353SPatrick Mooney 
25472473353SPatrick Mooney 	test_pass();
25572473353SPatrick Mooney }
256