xref: /openbsd-src/regress/sys/arch/amd64/vmm/vcpu.c (revision 6822f9c8033774f6aab00bcb43e8718e7034e67d)
1*6822f9c8Santon /*	$OpenBSD: vcpu.c,v 1.8 2024/08/23 12:56:26 anton Exp $	*/
2a944cd34Sdv 
3a944cd34Sdv /*
4a944cd34Sdv  * Copyright (c) 2022 Dave Voutila <dv@openbsd.org>
5a944cd34Sdv  *
6a944cd34Sdv  * Permission to use, copy, modify, and distribute this software for any
7a944cd34Sdv  * purpose with or without fee is hereby granted, provided that the above
8a944cd34Sdv  * copyright notice and this permission notice appear in all copies.
9a944cd34Sdv  *
10a944cd34Sdv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a944cd34Sdv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a944cd34Sdv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a944cd34Sdv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a944cd34Sdv  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a944cd34Sdv  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a944cd34Sdv  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a944cd34Sdv  */
18a944cd34Sdv 
19a944cd34Sdv #include <sys/types.h>
20a944cd34Sdv #include <sys/ioctl.h>
21a944cd34Sdv #include <sys/mman.h>
22a944cd34Sdv 
23a944cd34Sdv #include <machine/specialreg.h>
24a944cd34Sdv #include <machine/vmmvar.h>
25a944cd34Sdv 
26a6c21945Santon #include <dev/vmm/vmm.h>
27a6c21945Santon 
28a944cd34Sdv #include <err.h>
29a944cd34Sdv #include <errno.h>
30a944cd34Sdv #include <fcntl.h>
31a944cd34Sdv #include <stdio.h>
32a944cd34Sdv #include <stdlib.h>
33a944cd34Sdv #include <string.h>
34a944cd34Sdv #include <unistd.h>
35a944cd34Sdv 
36a944cd34Sdv #define KIB		1024
371ebbcee8Sdv #define MIB		(1UL << 20)
381ebbcee8Sdv #define GIB		(1024 * MIB)
39a944cd34Sdv #define VMM_NODE	"/dev/vmm"
40a944cd34Sdv 
411ebbcee8Sdv #define LOW_MEM		0
421ebbcee8Sdv #define UPPER_MEM	1
431ebbcee8Sdv 
4430bbc2f6Stb #define PCKBC_AUX	0x61
451ebbcee8Sdv #define PCJR_DISKCTRL	0xF0
4630bbc2f6Stb 
47a944cd34Sdv const char 		*VM_NAME = "regress";
48a944cd34Sdv 
491ebbcee8Sdv const uint8_t PUSHW_DX[] = { 0x66, 0x52 };		 // pushw %dx
501ebbcee8Sdv const uint8_t INS[] = { 0x6C };				 // ins es:[di],dx
511ebbcee8Sdv const uint8_t IN_PCJR[] = { 0xE4, 0xF0 };		 // in 0xF0
521ebbcee8Sdv 
53a944cd34Sdv /* Originally from vmd(8)'s vm.c */
54a944cd34Sdv const struct vcpu_reg_state vcpu_init_flat16 = {
55a944cd34Sdv 	.vrs_gprs[VCPU_REGS_RFLAGS] = 0x2,
56a944cd34Sdv 	.vrs_gprs[VCPU_REGS_RIP] = 0xFFF0,
571ebbcee8Sdv 	.vrs_gprs[VCPU_REGS_RDX] = PCKBC_AUX,	/* Port used by INS */
581ebbcee8Sdv 	.vrs_gprs[VCPU_REGS_RSP] =  0x800,	/* Set our stack in low mem. */
59a944cd34Sdv 	.vrs_crs[VCPU_REGS_CR0] = 0x60000010,
601ebbcee8Sdv 	.vrs_sregs[VCPU_REGS_CS] = { 0xF000, 0xFFFF, 0x0093, 0xFFFF0000},
611ebbcee8Sdv 	.vrs_sregs[VCPU_REGS_DS] = { 0x0, 0xFFFF, 0x0093, 0x0},
621ebbcee8Sdv 	.vrs_sregs[VCPU_REGS_ES] = { 0x0, 0xFFFF, 0x0093, 0x0},
631ebbcee8Sdv 	.vrs_sregs[VCPU_REGS_FS] = { 0x0, 0xFFFF, 0x0093, 0x0},
641ebbcee8Sdv 	.vrs_sregs[VCPU_REGS_GS] = { 0x0, 0xFFFF, 0x0093, 0x0},
651ebbcee8Sdv 	.vrs_sregs[VCPU_REGS_SS] = { 0x0, 0xFFFF, 0x0093, 0x0},
661ebbcee8Sdv 	.vrs_gdtr = { 0x0, 0xFFFF, 0x0082, 0x0},
671ebbcee8Sdv 	.vrs_idtr = { 0x0, 0xFFFF, 0x0082, 0x0},
68a944cd34Sdv 	.vrs_sregs[VCPU_REGS_LDTR] = { 0x0, 0xFFFF, 0x0082, 0x0},
69a944cd34Sdv 	.vrs_sregs[VCPU_REGS_TR] = { 0x0, 0xFFFF, 0x008B, 0x0},
70a944cd34Sdv 	.vrs_drs[VCPU_REGS_DR6] = 0xFFFF0FF0,
71a944cd34Sdv 	.vrs_drs[VCPU_REGS_DR7] = 0x400,
721ebbcee8Sdv 	.vrs_crs[VCPU_REGS_XCR0] = XFEATURE_X87,
731ebbcee8Sdv };
741ebbcee8Sdv 
751ebbcee8Sdv struct intr_handler {
761ebbcee8Sdv 	uint16_t	offset;
771ebbcee8Sdv 	uint16_t	segment;
781ebbcee8Sdv };
791ebbcee8Sdv 
801ebbcee8Sdv const struct intr_handler ivt[256] = {
811ebbcee8Sdv 	[VMM_EX_GP] = { .segment = 0x0, .offset = 0x0B5D },
82a944cd34Sdv };
83a944cd34Sdv 
84a944cd34Sdv int
85a944cd34Sdv main(int argc, char **argv)
86a944cd34Sdv {
87a944cd34Sdv 	struct vm_create_params		 vcp;
88a944cd34Sdv 	struct vm_exit			*exit = NULL;
89a944cd34Sdv 	struct vm_info_params		 vip;
90a944cd34Sdv 	struct vm_info_result		*info = NULL, *ours = NULL;
91a944cd34Sdv 	struct vm_resetcpu_params	 vresetp;
92a944cd34Sdv 	struct vm_run_params		 vrunp;
93a944cd34Sdv 	struct vm_terminate_params	 vtp;
943c817da7Sdv 	struct vm_sharemem_params	 vsp;
95a944cd34Sdv 
96a944cd34Sdv 	struct vm_mem_range		*vmr;
97a944cd34Sdv 	int				 fd, ret = 1;
981ebbcee8Sdv 	size_t				 i;
991ebbcee8Sdv 	off_t				 off, reset = 0xFFFFFFF0, stack = 0x800;
100a944cd34Sdv 	void				*p;
101a944cd34Sdv 
102a944cd34Sdv 	fd = open(VMM_NODE, O_RDWR);
1037775ce89Sbluhm 	if (fd == -1)
1047775ce89Sbluhm 		err(1, "open %s", VMM_NODE);
105a944cd34Sdv 
106a944cd34Sdv 	/*
1071ebbcee8Sdv 	 * 1. Create our VM with 1 vcpu and 64 MiB of memory.
108a944cd34Sdv 	 */
109a944cd34Sdv 	memset(&vcp, 0, sizeof(vcp));
110a944cd34Sdv 	strlcpy(vcp.vcp_name, VM_NAME, sizeof(vcp.vcp_name));
111a944cd34Sdv 	vcp.vcp_ncpus = 1;
112a944cd34Sdv 
113a944cd34Sdv 	/* Split into two ranges, similar to how vmd(8) might do it. */
114a944cd34Sdv 	vcp.vcp_nmemranges = 2;
1151ebbcee8Sdv 	vcp.vcp_memranges[LOW_MEM].vmr_gpa = 0x0;
1161ebbcee8Sdv 	vcp.vcp_memranges[LOW_MEM].vmr_size = 640 * KIB;
1171ebbcee8Sdv 	vcp.vcp_memranges[UPPER_MEM].vmr_size = (64 * MIB) - (640 * KIB);
1181ebbcee8Sdv 	vcp.vcp_memranges[UPPER_MEM].vmr_gpa = (4 * GIB)
1191ebbcee8Sdv 	    - vcp.vcp_memranges[UPPER_MEM].vmr_size;
120a944cd34Sdv 
1211ebbcee8Sdv 	/* Allocate and Initialize our guest memory. */
122a944cd34Sdv 	for (i = 0; i < vcp.vcp_nmemranges; i++) {
123a944cd34Sdv 		vmr = &vcp.vcp_memranges[i];
1241ebbcee8Sdv 		if (vmr->vmr_size % 2 != 0)
1251ebbcee8Sdv 			errx(1, "memory ranges must be multiple of 2");
1261ebbcee8Sdv 
127a944cd34Sdv 		p = mmap(NULL, vmr->vmr_size, PROT_READ | PROT_WRITE,
128a944cd34Sdv 		    MAP_PRIVATE | MAP_ANON, -1, 0);
129a944cd34Sdv 		if (p == MAP_FAILED)
130a944cd34Sdv 			err(1, "mmap");
131a944cd34Sdv 
132a944cd34Sdv 		vmr->vmr_va = (vaddr_t)p;
1333c817da7Sdv 		printf("created mapped region %zu: { gpa: 0x%08lx, size: %lu,"
1343c817da7Sdv 		    " hva: 0x%lx }\n", i, vmr->vmr_gpa, vmr->vmr_size,
1353c817da7Sdv 		    vmr->vmr_va);
1361ebbcee8Sdv 
1371ebbcee8Sdv 		/* Fill with int3 instructions. */
1381ebbcee8Sdv 		memset(p, 0xcc, vmr->vmr_size);
1391ebbcee8Sdv 
1401ebbcee8Sdv 		if (i == LOW_MEM) {
1411ebbcee8Sdv 			/* Write our IVT. */
1421ebbcee8Sdv 			memcpy(p, &ivt, sizeof(ivt));
1431ebbcee8Sdv 
1441ebbcee8Sdv 			/*
1451ebbcee8Sdv 			 * Set up a #GP handler that does a read from a
1461ebbcee8Sdv 			 * non-existent PC Jr. Disk Controller.
1471ebbcee8Sdv 			 */
1481ebbcee8Sdv 			p = (uint8_t*)((uint8_t*)p + 0xb5d);
1491ebbcee8Sdv 			memcpy(p, IN_PCJR, sizeof(IN_PCJR));
1501ebbcee8Sdv 		} else {
1511ebbcee8Sdv 			/*
1521ebbcee8Sdv 			 * Write our code to the reset vector:
1531ebbcee8Sdv 			 *   PUSHW %dx        ; inits the stack
1541ebbcee8Sdv 			 *   INS dx, es:[di]  ; read from port in dx
1551ebbcee8Sdv 			 */
1561ebbcee8Sdv 			off = reset - vmr->vmr_gpa;
1571ebbcee8Sdv 			p = (uint8_t*)p + off;
1581ebbcee8Sdv 			memcpy(p, PUSHW_DX, sizeof(PUSHW_DX));
1591ebbcee8Sdv 			p = (uint8_t*)p + sizeof(PUSHW_DX);
1601ebbcee8Sdv 			memcpy(p, INS, sizeof(INS));
1611ebbcee8Sdv 		}
162a944cd34Sdv 	}
163a944cd34Sdv 
164a944cd34Sdv 	if (ioctl(fd, VMM_IOC_CREATE, &vcp) == -1)
165a944cd34Sdv 		err(1, "VMM_IOC_CREATE");
166a944cd34Sdv 	printf("created vm %d named \"%s\"\n", vcp.vcp_id, vcp.vcp_name);
167a944cd34Sdv 
168a944cd34Sdv 	/*
1693c817da7Sdv 	 * 2. Check we can create shared memory mappings.
1703c817da7Sdv 	 */
1713c817da7Sdv 	memset(&vsp, 0, sizeof(vsp));
1723c817da7Sdv 	vsp.vsp_nmemranges = vcp.vcp_nmemranges;
1733c817da7Sdv 	memcpy(&vsp.vsp_memranges, &vcp.vcp_memranges,
1743c817da7Sdv 	    sizeof(vsp.vsp_memranges));
1753c817da7Sdv 	vsp.vsp_vm_id = vcp.vcp_id;
1763c817da7Sdv 
1773c817da7Sdv 	/* Find some new va ranges... */
1783c817da7Sdv 	for (i = 0; i < vsp.vsp_nmemranges; i++) {
1793c817da7Sdv 		vmr = &vsp.vsp_memranges[i];
1803c817da7Sdv 		p = mmap(NULL, vmr->vmr_size, PROT_READ | PROT_WRITE,
1813c817da7Sdv 		    MAP_PRIVATE | MAP_ANON, -1, 0);
1823c817da7Sdv 		if (p == MAP_FAILED)
1833c817da7Sdv 			err(1, "mmap");
1843c817da7Sdv 		vmr->vmr_va = (vaddr_t)p;
1853c817da7Sdv 	}
1863c817da7Sdv 
1873c817da7Sdv 	/* Release our mappings so vmm can replace them. */
1883c817da7Sdv 	for (i = 0; i < vsp.vsp_nmemranges; i++) {
1893c817da7Sdv 		vmr = &vsp.vsp_memranges[i];
1903c817da7Sdv 		munmap((void*)vmr->vmr_va, vmr->vmr_size);
1913c817da7Sdv 	}
1923c817da7Sdv 
1933c817da7Sdv 	/* Perform the shared mapping. */
1943c817da7Sdv 	if (ioctl(fd, VMM_IOC_SHAREMEM, &vsp) == -1)
1953c817da7Sdv 		err(1, "VMM_IOC_SHAREMEM");
1963c817da7Sdv 	printf("created shared memory mappings\n");
1973c817da7Sdv 
1983c817da7Sdv 	/* We should see our reset vector instructions in the new mappings. */
1993c817da7Sdv 	for (i = 0; i < vsp.vsp_nmemranges; i++) {
2003c817da7Sdv 		vmr = &vsp.vsp_memranges[i];
2013c817da7Sdv 		p = (void*)vmr->vmr_va;
2023c817da7Sdv 
2031ebbcee8Sdv 		if (i == LOW_MEM) {
2041ebbcee8Sdv 			/* Check if our IVT is there. */
2051ebbcee8Sdv 			if (memcmp(&ivt, p, sizeof(ivt)) != 0) {
2061ebbcee8Sdv 				warnx("invalid ivt");
2071ebbcee8Sdv 				goto out;
2081ebbcee8Sdv 			}
2091ebbcee8Sdv 		} else {
2101ebbcee8Sdv 			/* Check our code at the reset vector. */
2111ebbcee8Sdv 
2123c817da7Sdv 		}
2133c817da7Sdv 		printf("checked shared region %zu: { gpa: 0x%08lx, size: %lu,"
2143c817da7Sdv 		    " hva: 0x%lx }\n", i, vmr->vmr_gpa, vmr->vmr_size,
2153c817da7Sdv 		    vmr->vmr_va);
2163c817da7Sdv 	}
2173c817da7Sdv 	printf("validated shared memory mappings\n");
2183c817da7Sdv 
2193c817da7Sdv 	/*
2203c817da7Sdv 	 * 3. Check that our VM exists.
221a944cd34Sdv 	 */
222a944cd34Sdv 	memset(&vip, 0, sizeof(vip));
223a944cd34Sdv 	vip.vip_size = 0;
224a944cd34Sdv 	info = NULL;
225a944cd34Sdv 
226a944cd34Sdv 	if (ioctl(fd, VMM_IOC_INFO, &vip) == -1) {
227a944cd34Sdv 		warn("VMM_IOC_INFO(1)");
228a944cd34Sdv 		goto out;
229a944cd34Sdv 	}
230a944cd34Sdv 
231a944cd34Sdv 	if (vip.vip_size == 0) {
232a944cd34Sdv 		warn("no vms found");
233a944cd34Sdv 		goto out;
234a944cd34Sdv 	}
235a944cd34Sdv 
236a944cd34Sdv 	info = malloc(vip.vip_size);
237a944cd34Sdv 	if (info == NULL) {
238a944cd34Sdv 		warn("malloc");
239a944cd34Sdv 		goto out;
240a944cd34Sdv 	}
241a944cd34Sdv 
242a944cd34Sdv 	/* Second request that retrieves the VMs. */
243a944cd34Sdv 	vip.vip_info = info;
244a944cd34Sdv 	if (ioctl(fd, VMM_IOC_INFO, &vip) == -1) {
245a944cd34Sdv 		warn("VMM_IOC_INFO(2)");
246a944cd34Sdv 		goto out;
247a944cd34Sdv 	}
248a944cd34Sdv 
2497775ce89Sbluhm 	for (i = 0; i * sizeof(*info) < vip.vip_size; i++) {
250a944cd34Sdv 		if (info[i].vir_id == vcp.vcp_id) {
251a944cd34Sdv 			ours = &info[i];
252a944cd34Sdv 			break;
253a944cd34Sdv 		}
254a944cd34Sdv 	}
255a944cd34Sdv 	if (ours == NULL) {
256*6822f9c8Santon 		warn("failed to find vm %uz", vcp.vcp_id);
257a944cd34Sdv 		goto out;
258a944cd34Sdv 	}
259a944cd34Sdv 
260a944cd34Sdv 	if (ours->vir_id != vcp.vcp_id) {
261a944cd34Sdv 		warnx("expected vm id %uz, got %uz", vcp.vcp_id, ours->vir_id);
262a944cd34Sdv 		goto out;
263a944cd34Sdv 	}
264a944cd34Sdv 	if (strncmp(ours->vir_name, VM_NAME, strlen(VM_NAME)) != 0) {
265a944cd34Sdv 		warnx("expected vm name \"%s\", got \"%s\"", VM_NAME,
266a944cd34Sdv 		    ours->vir_name);
267a944cd34Sdv 		goto out;
268a944cd34Sdv 	}
269a944cd34Sdv 	printf("found vm %d named \"%s\"\n", vcp.vcp_id, ours->vir_name);
270a944cd34Sdv 	ours = NULL;
271a944cd34Sdv 
272a944cd34Sdv 	/*
2733c817da7Sdv 	 * 4. Reset our VCPU and initialize register state.
274a944cd34Sdv 	 */
275a944cd34Sdv 	memset(&vresetp, 0, sizeof(vresetp));
276a944cd34Sdv 	vresetp.vrp_vm_id = vcp.vcp_id;
277a944cd34Sdv 	vresetp.vrp_vcpu_id = 0;	/* XXX SP */
278a944cd34Sdv 	memcpy(&vresetp.vrp_init_state, &vcpu_init_flat16,
279a944cd34Sdv 	    sizeof(vcpu_init_flat16));
280a944cd34Sdv 
281a944cd34Sdv 	if (ioctl(fd, VMM_IOC_RESETCPU, &vresetp) == -1) {
282a944cd34Sdv 		warn("VMM_IOC_RESETCPU");
283a944cd34Sdv 		goto out;
284a944cd34Sdv 	}
285a944cd34Sdv 	printf("reset vcpu %d for vm %d\n", vresetp.vrp_vcpu_id,
286a944cd34Sdv 	    vresetp.vrp_vm_id);
287a944cd34Sdv 
288a944cd34Sdv 	/*
2893c817da7Sdv 	 * 5. Run the vcpu, expecting an immediate exit for IO assist.
290a944cd34Sdv 	 */
291a944cd34Sdv 	exit = malloc(sizeof(*exit));
292a944cd34Sdv 	if (exit == NULL) {
293a944cd34Sdv 		warn("failed to allocate memory for vm_exit");
294a944cd34Sdv 		goto out;
295a944cd34Sdv 	}
296a944cd34Sdv 
297a944cd34Sdv 	memset(&vrunp, 0, sizeof(vrunp));
298a944cd34Sdv 	vrunp.vrp_exit = exit;
299a944cd34Sdv 	vrunp.vrp_vcpu_id = 0;		/* XXX SP */
300a944cd34Sdv 	vrunp.vrp_vm_id = vcp.vcp_id;
301a944cd34Sdv 	vrunp.vrp_irqready = 1;
302a944cd34Sdv 
303a944cd34Sdv 	if (ioctl(fd, VMM_IOC_RUN, &vrunp) == -1) {
304a944cd34Sdv 		warn("VMM_IOC_RUN");
305a944cd34Sdv 		goto out;
306a944cd34Sdv 	}
307a944cd34Sdv 
308a944cd34Sdv 	if (vrunp.vrp_vm_id != vcp.vcp_id) {
309*6822f9c8Santon 		warnx("expected vm id %uz, got %uz", vcp.vcp_id,
310a944cd34Sdv 		    vrunp.vrp_vm_id);
311a944cd34Sdv 		goto out;
312a944cd34Sdv 	}
313a944cd34Sdv 
314a944cd34Sdv 	switch (vrunp.vrp_exit_reason) {
315a944cd34Sdv 	case SVM_VMEXIT_IOIO:
316a944cd34Sdv 	case VMX_EXIT_IO:
3171ebbcee8Sdv 		printf("vcpu %d on vm %d exited for io assist @ ip = 0x%llx, "
3181ebbcee8Sdv 		    "cs.base = 0x%llx, ss.base = 0x%llx, rsp = 0x%llx\n",
3191ebbcee8Sdv 		    vrunp.vrp_vcpu_id, vrunp.vrp_vm_id,
3201ebbcee8Sdv 		    vrunp.vrp_exit->vrs.vrs_gprs[VCPU_REGS_RIP],
3211ebbcee8Sdv 		    vrunp.vrp_exit->vrs.vrs_sregs[VCPU_REGS_CS].vsi_base,
3221ebbcee8Sdv 		    vrunp.vrp_exit->vrs.vrs_sregs[VCPU_REGS_SS].vsi_base,
3231ebbcee8Sdv 		    vrunp.vrp_exit->vrs.vrs_gprs[VCPU_REGS_RSP]);
324a944cd34Sdv 		break;
325a944cd34Sdv 	default:
326a944cd34Sdv 		warnx("unexpected vm exit reason: 0%04x",
327a944cd34Sdv 		    vrunp.vrp_exit_reason);
328a944cd34Sdv 		goto out;
329a944cd34Sdv 	}
330a944cd34Sdv 
331a944cd34Sdv 	exit = vrunp.vrp_exit;
332a944cd34Sdv 	if (exit->vei.vei_port != PCKBC_AUX) {
333a944cd34Sdv 		warnx("expected io port to be PCKBC_AUX, got 0x%02x",
334a944cd34Sdv 		    exit->vei.vei_port);
335a944cd34Sdv 		goto out;
336a944cd34Sdv 	}
3371ebbcee8Sdv 	if (exit->vei.vei_string != 1) {
3381ebbcee8Sdv 		warnx("expected string instruction (INS)");
3391ebbcee8Sdv 		goto out;
3401ebbcee8Sdv 	} else
3411ebbcee8Sdv 		printf("got expected string instruction\n");
3421ebbcee8Sdv 
3431ebbcee8Sdv 	/* Advance RIP? */
3441ebbcee8Sdv 	printf("insn_len = %u\n", exit->vei.vei_insn_len);
3451ebbcee8Sdv 	exit->vrs.vrs_gprs[VCPU_REGS_RIP] += exit->vei.vei_insn_len;
3461ebbcee8Sdv 
3471ebbcee8Sdv 	/*
3481ebbcee8Sdv 	 * Inject a #GP and see if we end up at our isr.
3491ebbcee8Sdv 	 */
3501ebbcee8Sdv 	vrunp.vrp_inject.vie_vector = VMM_EX_GP;
3511ebbcee8Sdv 	vrunp.vrp_inject.vie_errorcode = 0x11223344;
3521ebbcee8Sdv 	vrunp.vrp_inject.vie_type = VCPU_INJECT_EX;
3531ebbcee8Sdv 	printf("injecting exception 0x%x\n", vrunp.vrp_inject.vie_vector);
3541ebbcee8Sdv 	if (ioctl(fd, VMM_IOC_RUN, &vrunp) == -1) {
3551ebbcee8Sdv 		warn("VMM_IOC_RUN 2");
3561ebbcee8Sdv 		goto out;
3571ebbcee8Sdv 	}
3581ebbcee8Sdv 
3591ebbcee8Sdv 	switch (vrunp.vrp_exit_reason) {
3601ebbcee8Sdv 	case SVM_VMEXIT_IOIO:
3611ebbcee8Sdv 	case VMX_EXIT_IO:
3621ebbcee8Sdv 		printf("vcpu %d on vm %d exited for io assist @ ip = 0x%llx, "
3631ebbcee8Sdv 		    "cs.base = 0x%llx\n", vrunp.vrp_vcpu_id, vrunp.vrp_vm_id,
3641ebbcee8Sdv 		    vrunp.vrp_exit->vrs.vrs_gprs[VCPU_REGS_RIP],
3651ebbcee8Sdv 		    vrunp.vrp_exit->vrs.vrs_sregs[VCPU_REGS_CS].vsi_base);
3661ebbcee8Sdv 		break;
3671ebbcee8Sdv 	default:
3681ebbcee8Sdv 		warnx("unexpected vm exit reason: 0%04x",
3691ebbcee8Sdv 		    vrunp.vrp_exit_reason);
3701ebbcee8Sdv 		goto out;
3711ebbcee8Sdv 	}
3721ebbcee8Sdv 
3731ebbcee8Sdv 	if (exit->vei.vei_port != PCJR_DISKCTRL) {
3741ebbcee8Sdv 		warnx("expected NMI handler to poke PCJR_DISKCTLR, got 0x%02x",
3751ebbcee8Sdv 		    exit->vei.vei_port);
3761ebbcee8Sdv 		printf("rip = 0x%llx\n", exit->vrs.vrs_gprs[VCPU_REGS_RIP]);
3771ebbcee8Sdv 		goto out;
3781ebbcee8Sdv 	}
3791ebbcee8Sdv 	printf("exception handler called\n");
380a944cd34Sdv 
381a944cd34Sdv 	/*
382a944cd34Sdv 	 * If we made it here, we're close to passing. Any failures during
383a944cd34Sdv 	 * cleanup will reset ret back to non-zero.
384a944cd34Sdv 	 */
385a944cd34Sdv 	ret = 0;
386a944cd34Sdv 
387a944cd34Sdv out:
3881ebbcee8Sdv 	printf("--- RESET VECTOR @ gpa 0x%llx ---\n", reset);
3891ebbcee8Sdv 	for (i=0; i<10; i++) {
3901ebbcee8Sdv 		if (i > 0)
3911ebbcee8Sdv 			printf(" ");
3921ebbcee8Sdv 		printf("%02x", *(uint8_t*)
3931ebbcee8Sdv 		    (vsp.vsp_memranges[UPPER_MEM].vmr_va + off + i));
3941ebbcee8Sdv 	}
3951ebbcee8Sdv 	printf("\n--- STACK @ gpa 0x%llx ---\n", stack);
3961ebbcee8Sdv 	for (i=0; i<16; i++) {
3971ebbcee8Sdv 		if (i > 0)
3981ebbcee8Sdv 			printf(" ");
3991ebbcee8Sdv 		printf("%02x", *(uint8_t*)(vsp.vsp_memranges[LOW_MEM].vmr_va
4001ebbcee8Sdv 			+ stack - i - 1));
4011ebbcee8Sdv 	}
4021ebbcee8Sdv 	printf("\n");
4031ebbcee8Sdv 
404a944cd34Sdv 	/*
4053c817da7Sdv 	 * 6. Terminate our VM and clean up.
406a944cd34Sdv 	 */
407a944cd34Sdv 	memset(&vtp, 0, sizeof(vtp));
408a944cd34Sdv 	vtp.vtp_vm_id = vcp.vcp_id;
409a944cd34Sdv 	if (ioctl(fd, VMM_IOC_TERM, &vtp) == -1) {
410a944cd34Sdv 		warn("VMM_IOC_TERM");
411a944cd34Sdv 		ret = 1;
412a944cd34Sdv 	} else
413a944cd34Sdv 		printf("terminated vm %d\n", vtp.vtp_vm_id);
414a944cd34Sdv 
415a944cd34Sdv 	close(fd);
416a944cd34Sdv 	free(info);
417a944cd34Sdv 	free(exit);
418a944cd34Sdv 
419a944cd34Sdv 	/* Unmap memory. */
420a944cd34Sdv 	for (i = 0; i < vcp.vcp_nmemranges; i++) {
421a944cd34Sdv 		vmr = &vcp.vcp_memranges[i];
422a944cd34Sdv 		if (vmr->vmr_va) {
423a944cd34Sdv 			if (munmap((void *)vmr->vmr_va, vmr->vmr_size)) {
4243c817da7Sdv 				warn("failed to unmap orginal region %zu @ hva "
4253c817da7Sdv 				    "0x%lx", i, vmr->vmr_va);
426a944cd34Sdv 				ret = 1;
427a944cd34Sdv 			} else
4283c817da7Sdv 				printf("unmapped origin region %zu @ hva "
4293c817da7Sdv 				    "0x%lx\n", i, vmr->vmr_va);
4303c817da7Sdv 		}
4313c817da7Sdv 		vmr = &vsp.vsp_memranges[i];
4323c817da7Sdv 		if (vmr->vmr_va) {
4333c817da7Sdv 			if (munmap((void *)vmr->vmr_va, vmr->vmr_size)) {
4343c817da7Sdv 				warn("failed to unmap shared region %zu @ hva "
4353c817da7Sdv 				    "0x%lx", i, vmr->vmr_va);
4363c817da7Sdv 				ret = 1;
4373c817da7Sdv 			} else
4383c817da7Sdv 				printf("unmapped shared region %zu @ hva "
4393c817da7Sdv 				    "0x%lx\n", i, vmr->vmr_va);
440a944cd34Sdv 		}
441a944cd34Sdv 	}
442a944cd34Sdv 
443a944cd34Sdv 	return (ret);
444a944cd34Sdv }
445