xref: /illumos-gate/usr/src/test/bhyve-tests/tests/inst_emul/exit_consistent.c (revision 32640292339b07090f10ce34d455f98711077343)
123d9a8faSPatrick Mooney /*
223d9a8faSPatrick Mooney  * This file and its contents are supplied under the terms of the
323d9a8faSPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
423d9a8faSPatrick Mooney  * You may only use this file in accordance with the terms of version
523d9a8faSPatrick Mooney  * 1.0 of the CDDL.
623d9a8faSPatrick Mooney  *
723d9a8faSPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
823d9a8faSPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
923d9a8faSPatrick Mooney  * http://www.illumos.org/license/CDDL.
1023d9a8faSPatrick Mooney  */
1123d9a8faSPatrick Mooney 
1223d9a8faSPatrick Mooney /*
1323d9a8faSPatrick Mooney  * Copyright 2023 Oxide Computer Company
1423d9a8faSPatrick Mooney  */
1523d9a8faSPatrick Mooney 
1623d9a8faSPatrick Mooney #include <stdio.h>
1723d9a8faSPatrick Mooney #include <unistd.h>
1823d9a8faSPatrick Mooney #include <stdlib.h>
1923d9a8faSPatrick Mooney #include <strings.h>
2023d9a8faSPatrick Mooney #include <libgen.h>
2123d9a8faSPatrick Mooney #include <assert.h>
2223d9a8faSPatrick Mooney #include <errno.h>
2323d9a8faSPatrick Mooney 
2423d9a8faSPatrick Mooney #include <sys/types.h>
2523d9a8faSPatrick Mooney #include <sys/sysmacros.h>
2623d9a8faSPatrick Mooney #include <sys/debug.h>
2723d9a8faSPatrick Mooney #include <sys/vmm.h>
2823d9a8faSPatrick Mooney #include <sys/vmm_dev.h>
2923d9a8faSPatrick Mooney #include <vmmapi.h>
3023d9a8faSPatrick Mooney 
3123d9a8faSPatrick Mooney #include "in_guest.h"
3223d9a8faSPatrick Mooney 
3323d9a8faSPatrick Mooney static void
run_until_unhandled(struct vcpu * vcpu,struct vm_entry * ventry,struct vm_exit * vexit)34*32640292SAndy Fiddaman run_until_unhandled(struct vcpu *vcpu, struct vm_entry *ventry,
3523d9a8faSPatrick Mooney     struct vm_exit *vexit)
3623d9a8faSPatrick Mooney {
3723d9a8faSPatrick Mooney 	do {
3823d9a8faSPatrick Mooney 		const enum vm_exit_kind kind =
39*32640292SAndy Fiddaman 		    test_run_vcpu(vcpu, ventry, vexit);
4023d9a8faSPatrick Mooney 		switch (kind) {
4123d9a8faSPatrick Mooney 		case VEK_REENTR:
4223d9a8faSPatrick Mooney 			break;
4323d9a8faSPatrick Mooney 		case VEK_UNHANDLED:
4423d9a8faSPatrick Mooney 			return;
4523d9a8faSPatrick Mooney 		default:
4623d9a8faSPatrick Mooney 			/*
4723d9a8faSPatrick Mooney 			 * We are not expecting the payload to use any of the
4823d9a8faSPatrick Mooney 			 * pass/fail/messaging facilities during this test.
4923d9a8faSPatrick Mooney 			 */
5023d9a8faSPatrick Mooney 			test_fail_vmexit(vexit);
5123d9a8faSPatrick Mooney 			break;
5223d9a8faSPatrick Mooney 		}
5323d9a8faSPatrick Mooney 	} while (true);
5423d9a8faSPatrick Mooney }
5523d9a8faSPatrick Mooney 
5623d9a8faSPatrick Mooney static void
repeat_consistent_exit(struct vcpu * vcpu,struct vm_entry * ventry,struct vm_exit * vexit,uint64_t expected_rip)57*32640292SAndy Fiddaman repeat_consistent_exit(struct vcpu *vcpu, struct vm_entry *ventry,
5823d9a8faSPatrick Mooney     struct vm_exit *vexit, uint64_t expected_rip)
5923d9a8faSPatrick Mooney {
6023d9a8faSPatrick Mooney 	ventry->cmd = VEC_DEFAULT | VEC_FLAG_EXIT_CONSISTENT;
61*32640292SAndy Fiddaman 	if (vm_run(vcpu, ventry, vexit) != 0) {
6223d9a8faSPatrick Mooney 		test_fail_errno(errno, "Failure during vcpu entry");
6323d9a8faSPatrick Mooney 	}
6423d9a8faSPatrick Mooney 	if (vexit->rip != expected_rip) {
6523d9a8faSPatrick Mooney 		test_fail_msg(
6623d9a8faSPatrick Mooney 		    "Unexpected forward progress when vCPU already consistent");
6723d9a8faSPatrick Mooney 	}
6823d9a8faSPatrick Mooney }
6923d9a8faSPatrick Mooney 
7023d9a8faSPatrick Mooney int
main(int argc,char * argv[])7123d9a8faSPatrick Mooney main(int argc, char *argv[])
7223d9a8faSPatrick Mooney {
7323d9a8faSPatrick Mooney 	const char *test_suite_name = basename(argv[0]);
7423d9a8faSPatrick Mooney 	struct vmctx *ctx = NULL;
75*32640292SAndy Fiddaman 	struct vcpu *vcpu;
7623d9a8faSPatrick Mooney 	int err;
7723d9a8faSPatrick Mooney 
7823d9a8faSPatrick Mooney 	ctx = test_initialize(test_suite_name);
7923d9a8faSPatrick Mooney 
80*32640292SAndy Fiddaman 	if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
81*32640292SAndy Fiddaman 		test_fail_errno(errno, "Could not open vcpu0");
82*32640292SAndy Fiddaman 	}
83*32640292SAndy Fiddaman 	err = test_setup_vcpu(vcpu, MEM_LOC_PAYLOAD, MEM_LOC_STACK);
8423d9a8faSPatrick Mooney 	if (err != 0) {
8523d9a8faSPatrick Mooney 		test_fail_errno(err, "Could not initialize vcpu0");
8623d9a8faSPatrick Mooney 	}
8723d9a8faSPatrick Mooney 
8823d9a8faSPatrick Mooney 	struct vm_entry ventry = { 0 };
8923d9a8faSPatrick Mooney 	struct vm_exit vexit = { 0 };
9023d9a8faSPatrick Mooney 
9123d9a8faSPatrick Mooney 	/*
9223d9a8faSPatrick Mooney 	 * Let the payload run until it reaches the first userspace exit which
9323d9a8faSPatrick Mooney 	 * requires actual handling
9423d9a8faSPatrick Mooney 	 */
95*32640292SAndy Fiddaman 	run_until_unhandled(vcpu, &ventry, &vexit);
9623d9a8faSPatrick Mooney 	if (vexit.exitcode != VM_EXITCODE_RDMSR) {
9723d9a8faSPatrick Mooney 		test_fail_vmexit(&vexit);
9823d9a8faSPatrick Mooney 	}
9923d9a8faSPatrick Mooney 	uint64_t rcx = 0, rip = 0;
100*32640292SAndy Fiddaman 	if (vm_get_register(vcpu, VM_REG_GUEST_RCX, &rcx) != 0) {
10123d9a8faSPatrick Mooney 		test_fail_errno(errno, "Could not read guest %rcx");
10223d9a8faSPatrick Mooney 	}
103*32640292SAndy Fiddaman 	if (vm_get_register(vcpu, VM_REG_GUEST_RIP, &rip) != 0) {
10423d9a8faSPatrick Mooney 		test_fail_errno(errno, "Could not read guest %rip");
10523d9a8faSPatrick Mooney 	}
10623d9a8faSPatrick Mooney 	/* Paranoia: confirm that in-register %rip matches vm_exit data */
10723d9a8faSPatrick Mooney 	if (rip != vexit.rip) {
10823d9a8faSPatrick Mooney 		test_fail_msg(
10923d9a8faSPatrick Mooney 		    "vm_exit`rip does not match in-kernel %rip: %lx != %lx",
11023d9a8faSPatrick Mooney 		    rip, vexit.rip);
11123d9a8faSPatrick Mooney 	}
11223d9a8faSPatrick Mooney 
11323d9a8faSPatrick Mooney 	/* Request a consistent exit */
11423d9a8faSPatrick Mooney 	ventry.cmd = VEC_DEFAULT | VEC_FLAG_EXIT_CONSISTENT;
115*32640292SAndy Fiddaman 	if (vm_run(vcpu, &ventry, &vexit) != 0) {
11623d9a8faSPatrick Mooney 		test_fail_errno(errno, "Failure during vcpu entry");
11723d9a8faSPatrick Mooney 	}
11823d9a8faSPatrick Mooney 
11923d9a8faSPatrick Mooney 	/*
12023d9a8faSPatrick Mooney 	 * We expect the consistent exit to have completed the instruction
12123d9a8faSPatrick Mooney 	 * emulation for the rdmsr (just move the %rip forward, since its left
12223d9a8faSPatrick Mooney 	 * to userspace to update %rax:%rdx) and emit the BOGUS exitcode.
12323d9a8faSPatrick Mooney 	 */
12423d9a8faSPatrick Mooney 	if (vexit.exitcode != VM_EXITCODE_BOGUS) {
12523d9a8faSPatrick Mooney 		test_fail_msg("Unexpected exitcode: %d != %d",
12623d9a8faSPatrick Mooney 		    vexit.exitcode, VM_EXITCODE_BOGUS);
12723d9a8faSPatrick Mooney 	}
12823d9a8faSPatrick Mooney 
12923d9a8faSPatrick Mooney 	/*
13023d9a8faSPatrick Mooney 	 * Check that the %rip moved forward only the 2 bytes expected for a
13123d9a8faSPatrick Mooney 	 * rdmsr opcode.
13223d9a8faSPatrick Mooney 	 */
13323d9a8faSPatrick Mooney 	if (vexit.rip != (rip + 2)) {
13423d9a8faSPatrick Mooney 		test_fail_msg("Exited at unexpected %rip: %lx != %lx",
13523d9a8faSPatrick Mooney 		    vexit.rip, rip + 2);
13623d9a8faSPatrick Mooney 	}
13723d9a8faSPatrick Mooney 
13823d9a8faSPatrick Mooney 	/*
13923d9a8faSPatrick Mooney 	 * Repeat entry with consistency request.  This should not make any
14023d9a8faSPatrick Mooney 	 * forward progress since the vCPU is already in a consistent state.
14123d9a8faSPatrick Mooney 	 */
142*32640292SAndy Fiddaman 	repeat_consistent_exit(vcpu, &ventry, &vexit, vexit.rip);
14323d9a8faSPatrick Mooney 
14423d9a8faSPatrick Mooney 	/* Let the vCPU continue on to the next exit condition */
14523d9a8faSPatrick Mooney 	ventry.cmd = VEC_DEFAULT;
146*32640292SAndy Fiddaman 	run_until_unhandled(vcpu, &ventry, &vexit);
14723d9a8faSPatrick Mooney 
14823d9a8faSPatrick Mooney 	const uint64_t read_addr = 0xc0000000;
14923d9a8faSPatrick Mooney 	const uint_t read_len = 4;
15023d9a8faSPatrick Mooney 	if (!vexit_match_mmio(&vexit, true, read_addr, read_len, NULL)) {
15123d9a8faSPatrick Mooney 		test_fail_vmexit(&vexit);
15223d9a8faSPatrick Mooney 	}
15323d9a8faSPatrick Mooney 	rip = vexit.rip;
15423d9a8faSPatrick Mooney 
15523d9a8faSPatrick Mooney 	/*
15623d9a8faSPatrick Mooney 	 * An attempt to push the vCPU to a consistent state without first
15723d9a8faSPatrick Mooney 	 * fulfilling the MMIO should just result in the same MMIO exit.
15823d9a8faSPatrick Mooney 	 */
15923d9a8faSPatrick Mooney 	ventry.cmd = VEC_DEFAULT | VEC_FLAG_EXIT_CONSISTENT;
160*32640292SAndy Fiddaman 	if (vm_run(vcpu, &ventry, &vexit) != 0) {
16123d9a8faSPatrick Mooney 		test_fail_errno(errno, "Failure during vcpu entry");
16223d9a8faSPatrick Mooney 	}
16323d9a8faSPatrick Mooney 	if (vexit.rip != rip ||
16423d9a8faSPatrick Mooney 	    !vexit_match_mmio(&vexit, true, read_addr, read_len, NULL)) {
16523d9a8faSPatrick Mooney 		test_fail_msg(
16623d9a8faSPatrick Mooney 		    "Unexpected forward progress during MMIO emulation");
16723d9a8faSPatrick Mooney 	}
16823d9a8faSPatrick Mooney 
16923d9a8faSPatrick Mooney 	/* Fulfill the MMIO and attempt another consistent exit */
17023d9a8faSPatrick Mooney 	ventry_fulfill_mmio(&vexit, &ventry, 0);
17123d9a8faSPatrick Mooney 	ventry.cmd |= VEC_FLAG_EXIT_CONSISTENT;
172*32640292SAndy Fiddaman 	if (vm_run(vcpu, &ventry, &vexit) != 0) {
17323d9a8faSPatrick Mooney 		test_fail_errno(errno, "Failure during vcpu entry");
17423d9a8faSPatrick Mooney 	}
17523d9a8faSPatrick Mooney 
17623d9a8faSPatrick Mooney 	/* With current payload, we expect a 3-byte mov instruction */
17723d9a8faSPatrick Mooney 	if (vexit.rip != (rip + 3)) {
17823d9a8faSPatrick Mooney 		test_fail_msg("Exited at unexpected %rip: %lx != %lx",
17923d9a8faSPatrick Mooney 		    vexit.rip, rip + 3);
18023d9a8faSPatrick Mooney 	}
18123d9a8faSPatrick Mooney 
18223d9a8faSPatrick Mooney 	/*
18323d9a8faSPatrick Mooney 	 * And again, check that vCPU remains at that %rip once its state has
18423d9a8faSPatrick Mooney 	 * been made consistent.
18523d9a8faSPatrick Mooney 	 */
186*32640292SAndy Fiddaman 	repeat_consistent_exit(vcpu, &ventry, &vexit, vexit.rip);
18723d9a8faSPatrick Mooney 
18823d9a8faSPatrick Mooney 	test_pass();
18923d9a8faSPatrick Mooney }
190