15103e761SPatrick Mooney /*
25103e761SPatrick Mooney  * This file and its contents are supplied under the terms of the
35103e761SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
45103e761SPatrick Mooney  * You may only use this file in accordance with the terms of version
55103e761SPatrick Mooney  * 1.0 of the CDDL.
65103e761SPatrick Mooney  *
75103e761SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
85103e761SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
95103e761SPatrick Mooney  * http://www.illumos.org/license/CDDL.
105103e761SPatrick Mooney  */
115103e761SPatrick Mooney 
125103e761SPatrick Mooney /*
13a1d41cf9SPatrick Mooney  * Copyright 2023 Oxide Computer Company
145103e761SPatrick Mooney  */
155103e761SPatrick Mooney 
165103e761SPatrick Mooney #include <stdio.h>
175103e761SPatrick Mooney #include <unistd.h>
185103e761SPatrick Mooney #include <stdlib.h>
195103e761SPatrick Mooney #include <strings.h>
205103e761SPatrick Mooney #include <assert.h>
215103e761SPatrick Mooney #include <errno.h>
225103e761SPatrick Mooney 
235103e761SPatrick Mooney #include <sys/types.h>
245103e761SPatrick Mooney #include <sys/segments.h>
255103e761SPatrick Mooney #include <sys/psw.h>
265103e761SPatrick Mooney #include <sys/controlregs.h>
275103e761SPatrick Mooney #include <sys/sysmacros.h>
285103e761SPatrick Mooney #include <sys/varargs.h>
295103e761SPatrick Mooney #include <sys/debug.h>
303f6fd99dSPatrick Mooney #include <sys/mman.h>
315103e761SPatrick Mooney 
325103e761SPatrick Mooney #include <sys/vmm.h>
335103e761SPatrick Mooney #include <sys/vmm_dev.h>
345103e761SPatrick Mooney #include <vmmapi.h>
355103e761SPatrick Mooney 
365103e761SPatrick Mooney #include "in_guest.h"
375103e761SPatrick Mooney 
385103e761SPatrick Mooney 
395103e761SPatrick Mooney #define	PT_VALID	0x01
405103e761SPatrick Mooney #define	PT_WRITABLE	0x02
415103e761SPatrick Mooney #define	PT_WRITETHRU	0x08
425103e761SPatrick Mooney #define	PT_NOCACHE	0x10
435103e761SPatrick Mooney #define	PT_PAGESIZE	0x80
445103e761SPatrick Mooney 
455103e761SPatrick Mooney #define	SEG_ACCESS_TYPE_MASK	0x1f
465103e761SPatrick Mooney #define	SEG_ACCESS_DPL_MASK	0x60
475103e761SPatrick Mooney #define	SEG_ACCESS_P		(1 << 7)
485103e761SPatrick Mooney #define	SEG_ACCESS_AVL		(1 << 12)
495103e761SPatrick Mooney #define	SEG_ACCESS_L		(1 << 13)
505103e761SPatrick Mooney #define	SEG_ACCESS_D		(1 << 14)
515103e761SPatrick Mooney #define	SEG_ACCESS_G		(1 << 15)
525103e761SPatrick Mooney #define	SEG_ACCESS_UNUSABLE	(1 << 16)
535103e761SPatrick Mooney 
545103e761SPatrick Mooney 
555103e761SPatrick Mooney /*
565103e761SPatrick Mooney  * Keep the test name and VM context around so the consumer is not required to
575103e761SPatrick Mooney  * pass either of them to us for subsequent test-related operations after the
585103e761SPatrick Mooney  * initialization has been performed.
595103e761SPatrick Mooney  *
605103e761SPatrick Mooney  * The test code is not designed to be reentrant at this point.
615103e761SPatrick Mooney  */
625103e761SPatrick Mooney static struct vmctx *test_vmctx = NULL;
635103e761SPatrick Mooney static const char *test_name = NULL;
645103e761SPatrick Mooney 
65a1d41cf9SPatrick Mooney static uint64_t test_msg_addr = 0;
66a1d41cf9SPatrick Mooney 
673f6fd99dSPatrick Mooney static int
setup_rom(struct vmctx * ctx)683f6fd99dSPatrick Mooney setup_rom(struct vmctx *ctx)
693f6fd99dSPatrick Mooney {
703f6fd99dSPatrick Mooney 	const size_t seg_sz = 0x1000;
713f6fd99dSPatrick Mooney 	const uintptr_t seg_addr = MEM_LOC_ROM;
723f6fd99dSPatrick Mooney 	const int fd = vm_get_device_fd(ctx);
733f6fd99dSPatrick Mooney 	int err;
743f6fd99dSPatrick Mooney 
753f6fd99dSPatrick Mooney 	struct vm_memseg memseg = {
763f6fd99dSPatrick Mooney 		.segid = VM_BOOTROM,
773f6fd99dSPatrick Mooney 		.len = 0x1000,
783f6fd99dSPatrick Mooney 	};
793f6fd99dSPatrick Mooney 	(void) strlcpy(memseg.name, "testrom", sizeof (memseg.name));
803f6fd99dSPatrick Mooney 	err = ioctl(fd, VM_ALLOC_MEMSEG, &memseg);
813f6fd99dSPatrick Mooney 	if (err != 0) {
823f6fd99dSPatrick Mooney 		return (err);
833f6fd99dSPatrick Mooney 	}
843f6fd99dSPatrick Mooney 	err = vm_mmap_memseg(ctx, seg_addr, VM_BOOTROM, 0, seg_sz,
853f6fd99dSPatrick Mooney 	    PROT_READ | PROT_EXEC);
863f6fd99dSPatrick Mooney 	return (err);
873f6fd99dSPatrick Mooney }
883f6fd99dSPatrick Mooney 
895103e761SPatrick Mooney static void
populate_identity_table(struct vmctx * ctx)905103e761SPatrick Mooney populate_identity_table(struct vmctx *ctx)
915103e761SPatrick Mooney {
925103e761SPatrick Mooney 	uint64_t gpa, pte_loc;
935103e761SPatrick Mooney 
945103e761SPatrick Mooney 	/* Set up 2MiB PTEs for everything up through 0xffffffff */
955103e761SPatrick Mooney 	for (gpa = 0, pte_loc = MEM_LOC_PAGE_TABLE_2M;
965103e761SPatrick Mooney 	    gpa < 0x100000000;
975103e761SPatrick Mooney 	    pte_loc += PAGE_SIZE) {
985103e761SPatrick Mooney 		uint64_t *ptep = vm_map_gpa(ctx, pte_loc, PAGE_SIZE);
995103e761SPatrick Mooney 
1005103e761SPatrick Mooney 		for (uint_t i = 0; i < 512; i++, ptep++, gpa += 0x200000) {
1015103e761SPatrick Mooney 			*ptep =  gpa | PT_VALID | PT_WRITABLE | PT_PAGESIZE;
1025103e761SPatrick Mooney 			/* Make traditional MMIO space uncachable */
1035103e761SPatrick Mooney 			if (gpa >= 0xc0000000) {
1045103e761SPatrick Mooney 				*ptep |= PT_WRITETHRU | PT_NOCACHE;
1055103e761SPatrick Mooney 			}
1065103e761SPatrick Mooney 		}
1075103e761SPatrick Mooney 	}
1085103e761SPatrick Mooney 	assert(gpa == 0x100000000 && pte_loc == MEM_LOC_PAGE_TABLE_1G);
1095103e761SPatrick Mooney 
1105103e761SPatrick Mooney 	uint64_t *pdep = vm_map_gpa(ctx, MEM_LOC_PAGE_TABLE_1G, PAGE_SIZE);
1115103e761SPatrick Mooney 	pdep[0] = MEM_LOC_PAGE_TABLE_2M | PT_VALID | PT_WRITABLE;
1125103e761SPatrick Mooney 	pdep[1] = (MEM_LOC_PAGE_TABLE_2M + PAGE_SIZE) | PT_VALID | PT_WRITABLE;
1135103e761SPatrick Mooney 	pdep[2] =
1145103e761SPatrick Mooney 	    (MEM_LOC_PAGE_TABLE_2M + 2 * PAGE_SIZE) | PT_VALID | PT_WRITABLE;
1155103e761SPatrick Mooney 	pdep[3] =
1165103e761SPatrick Mooney 	    (MEM_LOC_PAGE_TABLE_2M + 3 * PAGE_SIZE) | PT_VALID | PT_WRITABLE;
1175103e761SPatrick Mooney 
1185103e761SPatrick Mooney 	pdep = vm_map_gpa(ctx, MEM_LOC_PAGE_TABLE_512G, PAGE_SIZE);
1195103e761SPatrick Mooney 	pdep[0] = MEM_LOC_PAGE_TABLE_1G | PT_VALID | PT_WRITABLE;
1205103e761SPatrick Mooney }
1215103e761SPatrick Mooney 
1225103e761SPatrick Mooney static void
populate_desc_tables(struct vmctx * ctx)1235103e761SPatrick Mooney populate_desc_tables(struct vmctx *ctx)
1245103e761SPatrick Mooney {
1255103e761SPatrick Mooney 
1265103e761SPatrick Mooney }
1275103e761SPatrick Mooney 
1284ac713daSLuqman Aden void
test_cleanup(bool is_failure)1295103e761SPatrick Mooney test_cleanup(bool is_failure)
1305103e761SPatrick Mooney {
1315103e761SPatrick Mooney 	if (test_vmctx != NULL) {
1325103e761SPatrick Mooney 		bool keep_on_fail = false;
1335103e761SPatrick Mooney 
1345103e761SPatrick Mooney 		const char *keep_var;
1355103e761SPatrick Mooney 		if ((keep_var = getenv("KEEP_ON_FAIL")) != NULL) {
1365103e761SPatrick Mooney 			if (strlen(keep_var) != 0 &&
1375103e761SPatrick Mooney 			    strcmp(keep_var, "0") != 0) {
1385103e761SPatrick Mooney 				keep_on_fail = true;
1395103e761SPatrick Mooney 			}
1405103e761SPatrick Mooney 		}
1415103e761SPatrick Mooney 
1425103e761SPatrick Mooney 		/*
1435103e761SPatrick Mooney 		 * Destroy the instance unless the test failed and it was
1445103e761SPatrick Mooney 		 * requested that we keep it around.
1455103e761SPatrick Mooney 		 */
1465103e761SPatrick Mooney 		if (!is_failure || !keep_on_fail) {
1475103e761SPatrick Mooney 			vm_destroy(test_vmctx);
1485103e761SPatrick Mooney 		}
1494ac713daSLuqman Aden 		test_name = NULL;
1505103e761SPatrick Mooney 		test_vmctx = NULL;
1515103e761SPatrick Mooney 	}
1525103e761SPatrick Mooney }
1535103e761SPatrick Mooney 
fail_finish(void)1545103e761SPatrick Mooney static void fail_finish(void)
1555103e761SPatrick Mooney {
1565103e761SPatrick Mooney 	assert(test_name != NULL);
1575103e761SPatrick Mooney 	(void) printf("FAIL %s\n", test_name);
1585103e761SPatrick Mooney 
1595103e761SPatrick Mooney 	test_cleanup(true);
1605103e761SPatrick Mooney 	exit(EXIT_FAILURE);
1615103e761SPatrick Mooney }
1625103e761SPatrick Mooney 
163a1d41cf9SPatrick Mooney void
test_fail(void)164a1d41cf9SPatrick Mooney test_fail(void)
165a1d41cf9SPatrick Mooney {
166a1d41cf9SPatrick Mooney 	fail_finish();
167a1d41cf9SPatrick Mooney }
168a1d41cf9SPatrick Mooney 
1695103e761SPatrick Mooney void
test_fail_errno(int err,const char * msg)1705103e761SPatrick Mooney test_fail_errno(int err, const char *msg)
1715103e761SPatrick Mooney {
1725103e761SPatrick Mooney 	const char *err_str = strerror(err);
1735103e761SPatrick Mooney 
1745103e761SPatrick Mooney 	(void) fprintf(stderr, "%s: %s\n", msg, err_str);
1755103e761SPatrick Mooney 	fail_finish();
1765103e761SPatrick Mooney }
1775103e761SPatrick Mooney 
1785103e761SPatrick Mooney void
test_fail_msg(const char * fmt,...)1795103e761SPatrick Mooney test_fail_msg(const char *fmt, ...)
1805103e761SPatrick Mooney {
1815103e761SPatrick Mooney 	va_list ap;
1825103e761SPatrick Mooney 
1835103e761SPatrick Mooney 	va_start(ap, fmt);
1845103e761SPatrick Mooney 	(void) vfprintf(stderr, fmt, ap);
1855103e761SPatrick Mooney 
1865103e761SPatrick Mooney 	fail_finish();
1875103e761SPatrick Mooney }
1885103e761SPatrick Mooney 
1895103e761SPatrick Mooney void
test_fail_vmexit(const struct vm_exit * vexit)1905103e761SPatrick Mooney test_fail_vmexit(const struct vm_exit *vexit)
1915103e761SPatrick Mooney {
1925103e761SPatrick Mooney 	const char *hdr_fmt = "Unexpected %s exit:\n\t%%rip: %lx\n";
1935103e761SPatrick Mooney 
1945103e761SPatrick Mooney 	switch (vexit->exitcode) {
1955103e761SPatrick Mooney 	case VM_EXITCODE_INOUT:
1965103e761SPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "IN/OUT", vexit->rip);
1975103e761SPatrick Mooney 		(void) fprintf(stderr,
1985103e761SPatrick Mooney 		    "\teax: %08x\n"
1995103e761SPatrick Mooney 		    "\tport: %04x\n"
2005103e761SPatrick Mooney 		    "\tbytes: %u\n"
2015103e761SPatrick Mooney 		    "\tflags: %x\n",
2025103e761SPatrick Mooney 		    vexit->u.inout.eax,
2035103e761SPatrick Mooney 		    vexit->u.inout.port,
2045103e761SPatrick Mooney 		    vexit->u.inout.bytes,
2055103e761SPatrick Mooney 		    vexit->u.inout.flags);
2065103e761SPatrick Mooney 		break;
207d2f938fdSPatrick Mooney 	case VM_EXITCODE_RDMSR:
208d2f938fdSPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "RDMSR", vexit->rip);
209d2f938fdSPatrick Mooney 		(void) fprintf(stderr, "\tcode: %08x\n", vexit->u.msr.code);
210d2f938fdSPatrick Mooney 		break;
211d2f938fdSPatrick Mooney 	case VM_EXITCODE_WRMSR:
212d2f938fdSPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "WRMSR", vexit->rip);
213d2f938fdSPatrick Mooney 		(void) fprintf(stderr,
214d2f938fdSPatrick Mooney 		    "\tcode: %08x\n"
215d2f938fdSPatrick Mooney 		    "\twval: %016lx\n",
216d2f938fdSPatrick Mooney 		    vexit->u.msr.code, vexit->u.msr.wval);
217d2f938fdSPatrick Mooney 		break;
2185103e761SPatrick Mooney 	case VM_EXITCODE_MMIO:
2195103e761SPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "MMIO", vexit->rip);
2205103e761SPatrick Mooney 		(void) fprintf(stderr,
2215103e761SPatrick Mooney 		    "\tbytes: %u\n"
2225103e761SPatrick Mooney 		    "\ttype: %s\n"
2235103e761SPatrick Mooney 		    "\tgpa: %x\n"
2245103e761SPatrick Mooney 		    "\tdata: %016x\n",
2255103e761SPatrick Mooney 		    vexit->u.mmio.bytes,
2265103e761SPatrick Mooney 		    vexit->u.mmio.read == 0 ? "write" : "read",
2275103e761SPatrick Mooney 		    vexit->u.mmio.gpa,
2285103e761SPatrick Mooney 		    vexit->u.mmio.data);
2295103e761SPatrick Mooney 		break;
2305103e761SPatrick Mooney 	case VM_EXITCODE_VMX:
2315103e761SPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "VMX", vexit->rip);
2325103e761SPatrick Mooney 		(void) fprintf(stderr,
2335103e761SPatrick Mooney 		    "\tstatus: %x\n"
2345103e761SPatrick Mooney 		    "\treason: %x\n"
2355103e761SPatrick Mooney 		    "\tqualification: %lx\n"
2365103e761SPatrick Mooney 		    "\tinst_type: %x\n"
2375103e761SPatrick Mooney 		    "\tinst_error: %x\n",
2385103e761SPatrick Mooney 		    vexit->u.vmx.status,
2395103e761SPatrick Mooney 		    vexit->u.vmx.exit_reason,
2405103e761SPatrick Mooney 		    vexit->u.vmx.exit_qualification,
2415103e761SPatrick Mooney 		    vexit->u.vmx.inst_type,
2425103e761SPatrick Mooney 		    vexit->u.vmx.inst_error);
2435103e761SPatrick Mooney 		break;
2445103e761SPatrick Mooney 	case VM_EXITCODE_SVM:
2455103e761SPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "SVM", vexit->rip);
2465103e761SPatrick Mooney 		break;
2475103e761SPatrick Mooney 	case VM_EXITCODE_INST_EMUL:
2485103e761SPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "instruction emulation",
2495103e761SPatrick Mooney 		    vexit->rip);
2505103e761SPatrick Mooney 		const uint_t len = vexit->u.inst_emul.num_valid > 0 ?
2515103e761SPatrick Mooney 		    vexit->u.inst_emul.num_valid : 15;
2525103e761SPatrick Mooney 		(void) fprintf(stderr, "\tinstruction bytes: [");
2535103e761SPatrick Mooney 		for (uint_t i = 0; i < len; i++) {
2545103e761SPatrick Mooney 			(void) fprintf(stderr, "%s%02x",
2555103e761SPatrick Mooney 			    i == 0 ? "" : ", ",
2565103e761SPatrick Mooney 			    vexit->u.inst_emul.inst[i]);
2575103e761SPatrick Mooney 		}
2585103e761SPatrick Mooney 		(void) fprintf(stderr, "]\n");
2595103e761SPatrick Mooney 		break;
2605103e761SPatrick Mooney 	case VM_EXITCODE_SUSPENDED:
2615103e761SPatrick Mooney 		(void) fprintf(stderr, hdr_fmt, "suspend", vexit->rip);
2625103e761SPatrick Mooney 		switch (vexit->u.suspended.how) {
2635103e761SPatrick Mooney 		case VM_SUSPEND_RESET:
2645103e761SPatrick Mooney 			(void) fprintf(stderr, "\thow: reset");
2655103e761SPatrick Mooney 			break;
2665103e761SPatrick Mooney 		case VM_SUSPEND_POWEROFF:
2675103e761SPatrick Mooney 			(void) fprintf(stderr, "\thow: poweroff");
2685103e761SPatrick Mooney 			break;
2695103e761SPatrick Mooney 		case VM_SUSPEND_HALT:
2705103e761SPatrick Mooney 			(void) fprintf(stderr, "\thow: halt");
2715103e761SPatrick Mooney 			break;
2725103e761SPatrick Mooney 		case VM_SUSPEND_TRIPLEFAULT:
2735103e761SPatrick Mooney 			(void) fprintf(stderr, "\thow: triple-fault");
2745103e761SPatrick Mooney 			break;
2755103e761SPatrick Mooney 		default:
2765103e761SPatrick Mooney 			(void) fprintf(stderr, "\thow: unknown - %d",
2775103e761SPatrick Mooney 			    vexit->u.suspended.how);
2785103e761SPatrick Mooney 			break;
2795103e761SPatrick Mooney 		}
2805103e761SPatrick Mooney 		break;
2815103e761SPatrick Mooney 	default:
2825103e761SPatrick Mooney 		(void) fprintf(stderr, "Unexpected code %d exit:\n"
2835103e761SPatrick Mooney 		    "\t%%rip: %lx\n", vexit->exitcode, vexit->rip);
2845103e761SPatrick Mooney 		break;
2855103e761SPatrick Mooney 	}
2865103e761SPatrick Mooney 	fail_finish();
2875103e761SPatrick Mooney }
2885103e761SPatrick Mooney 
2895103e761SPatrick Mooney void
test_pass(void)2905103e761SPatrick Mooney test_pass(void)
2915103e761SPatrick Mooney {
2925103e761SPatrick Mooney 	assert(test_name != NULL);
2935103e761SPatrick Mooney 	(void) printf("PASS %s\n", test_name);
2945103e761SPatrick Mooney 	test_cleanup(false);
2955103e761SPatrick Mooney 	exit(EXIT_SUCCESS);
2965103e761SPatrick Mooney }
2975103e761SPatrick Mooney 
298a1d41cf9SPatrick Mooney const char *
test_msg_get(struct vmctx * ctx)299a1d41cf9SPatrick Mooney test_msg_get(struct vmctx *ctx)
300a1d41cf9SPatrick Mooney {
301a1d41cf9SPatrick Mooney 	/* Disregard if the message address is still NULL */
302a1d41cf9SPatrick Mooney 	const uint64_t msg_addr = test_msg_addr;
303a1d41cf9SPatrick Mooney 	if (msg_addr == 0) {
304a1d41cf9SPatrick Mooney 		return (NULL);
305a1d41cf9SPatrick Mooney 	}
306a1d41cf9SPatrick Mooney 
307a1d41cf9SPatrick Mooney 	/*
308a1d41cf9SPatrick Mooney 	 * We want to try to map up to one page after the specified message
309a1d41cf9SPatrick Mooney 	 * address, keeping in mind the end of lowmem. (The payload, and
310a1d41cf9SPatrick Mooney 	 * thus message, is assumed to be in lowmem at this time.)
311a1d41cf9SPatrick Mooney 	 */
312a1d41cf9SPatrick Mooney 	const uint64_t lowmem_end = vm_get_lowmem_size(ctx);
313a1d41cf9SPatrick Mooney 	const uint64_t msg_map_end = MIN(msg_addr + PAGE_SIZE, lowmem_end);
314a1d41cf9SPatrick Mooney 
315a1d41cf9SPatrick Mooney 	if (msg_map_end >= lowmem_end || msg_map_end <= msg_addr) {
316a1d41cf9SPatrick Mooney 		return (NULL);
317a1d41cf9SPatrick Mooney 	}
318a1d41cf9SPatrick Mooney 	const uint64_t max_msg_len = msg_map_end - msg_addr;
319a1d41cf9SPatrick Mooney 
320a1d41cf9SPatrick Mooney 	/*
321a1d41cf9SPatrick Mooney 	 * Get the mapping to that guest memory.  This assumes that the payload
322a1d41cf9SPatrick Mooney 	 * has provided a guest-physical address to us.
323a1d41cf9SPatrick Mooney 	 */
324a1d41cf9SPatrick Mooney 	const char *result = vm_map_gpa(ctx, msg_addr, max_msg_len);
325a1d41cf9SPatrick Mooney 	if (result == NULL) {
326a1d41cf9SPatrick Mooney 		return (NULL);
327a1d41cf9SPatrick Mooney 	}
328a1d41cf9SPatrick Mooney 
329a1d41cf9SPatrick Mooney 	/* Demand a NUL-terminated string shorter than the map limit */
330a1d41cf9SPatrick Mooney 	if (strnlen(result, max_msg_len) >= max_msg_len) {
331a1d41cf9SPatrick Mooney 		return (NULL);
332a1d41cf9SPatrick Mooney 	}
333a1d41cf9SPatrick Mooney 
334a1d41cf9SPatrick Mooney 	return (result);
335a1d41cf9SPatrick Mooney }
336a1d41cf9SPatrick Mooney 
337a1d41cf9SPatrick Mooney void
test_msg_print(struct vmctx * ctx)338a1d41cf9SPatrick Mooney test_msg_print(struct vmctx *ctx)
339a1d41cf9SPatrick Mooney {
340a1d41cf9SPatrick Mooney 	const char *payload_msg = test_msg_get(ctx);
341a1d41cf9SPatrick Mooney 
342a1d41cf9SPatrick Mooney 	if (payload_msg != NULL) {
343a1d41cf9SPatrick Mooney 		(void) fprintf(stderr, "MSG: %s\n", payload_msg);
344a1d41cf9SPatrick Mooney 	}
345a1d41cf9SPatrick Mooney }
346a1d41cf9SPatrick Mooney 
3475103e761SPatrick Mooney static int
load_payload(struct vmctx * ctx)3485103e761SPatrick Mooney load_payload(struct vmctx *ctx)
3495103e761SPatrick Mooney {
3505103e761SPatrick Mooney 	extern uint8_t payload_data;
3515103e761SPatrick Mooney 	extern uint32_t payload_size;
3525103e761SPatrick Mooney 
3535103e761SPatrick Mooney 	const uint32_t len = payload_size;
3545103e761SPatrick Mooney 	const uint32_t cap = (MEM_TOTAL_SZ - MEM_LOC_PAYLOAD);
3555103e761SPatrick Mooney 
3565103e761SPatrick Mooney 	if (len > cap) {
3575103e761SPatrick Mooney 		test_fail_msg("Payload size %u > capacity %u\n", len, cap);
3585103e761SPatrick Mooney 	}
3595103e761SPatrick Mooney 
3605103e761SPatrick Mooney 	const size_t map_len = P2ROUNDUP(len, PAGE_SIZE);
3615103e761SPatrick Mooney 	void *outp = vm_map_gpa(ctx, MEM_LOC_PAYLOAD, map_len);
3625103e761SPatrick Mooney 	bcopy(&payload_data, outp, len);
3635103e761SPatrick Mooney 
3645103e761SPatrick Mooney 	return (0);
3655103e761SPatrick Mooney }
3665103e761SPatrick Mooney 
3675103e761SPatrick Mooney struct vmctx *
test_initialize(const char * tname)3685103e761SPatrick Mooney test_initialize(const char *tname)
3694ac713daSLuqman Aden {
3704ac713daSLuqman Aden 	return (test_initialize_flags(tname, 0));
3714ac713daSLuqman Aden }
3724ac713daSLuqman Aden 
3734ac713daSLuqman Aden struct vmctx *
test_initialize_flags(const char * tname,uint64_t create_flags)3744ac713daSLuqman Aden test_initialize_flags(const char *tname, uint64_t create_flags)
3755103e761SPatrick Mooney {
3765103e761SPatrick Mooney 	char vm_name[VM_MAX_NAMELEN];
3775103e761SPatrick Mooney 	int err;
3785103e761SPatrick Mooney 	struct vmctx *ctx;
3795103e761SPatrick Mooney 
3805103e761SPatrick Mooney 	assert(test_vmctx == NULL);
3815103e761SPatrick Mooney 	assert(test_name == NULL);
3825103e761SPatrick Mooney 
3835103e761SPatrick Mooney 	test_name = strdup(tname);
3845103e761SPatrick Mooney 	(void) snprintf(vm_name, sizeof (vm_name), "bhyve-test-%s-%d",
3855103e761SPatrick Mooney 	    test_name, getpid());
3865103e761SPatrick Mooney 
3874ac713daSLuqman Aden 	err = vm_create(vm_name, create_flags);
3885103e761SPatrick Mooney 	if (err != 0) {
3895103e761SPatrick Mooney 		test_fail_errno(err, "Could not create VM");
3905103e761SPatrick Mooney 	}
3915103e761SPatrick Mooney 
3925103e761SPatrick Mooney 	ctx = vm_open(vm_name);
3935103e761SPatrick Mooney 	if (ctx == NULL) {
3945103e761SPatrick Mooney 		test_fail_errno(errno, "Could not open VM");
3955103e761SPatrick Mooney 	}
3965103e761SPatrick Mooney 	test_vmctx = ctx;
3975103e761SPatrick Mooney 
3985103e761SPatrick Mooney 	err = vm_setup_memory(ctx, MEM_TOTAL_SZ, VM_MMAP_ALL);
3995103e761SPatrick Mooney 	if (err != 0) {
4005103e761SPatrick Mooney 		test_fail_errno(err, "Could not set up VM memory");
4015103e761SPatrick Mooney 	}
4025103e761SPatrick Mooney 
4033f6fd99dSPatrick Mooney 	err = setup_rom(ctx);
4043f6fd99dSPatrick Mooney 	if (err != 0) {
4053f6fd99dSPatrick Mooney 		test_fail_errno(err, "Could not set up VM ROM segment");
4063f6fd99dSPatrick Mooney 	}
4073f6fd99dSPatrick Mooney 
4085103e761SPatrick Mooney 	populate_identity_table(ctx);
4095103e761SPatrick Mooney 	populate_desc_tables(ctx);
4105103e761SPatrick Mooney 
4115103e761SPatrick Mooney 	err = load_payload(ctx);
4125103e761SPatrick Mooney 	if (err != 0) {
4135103e761SPatrick Mooney 		test_fail_errno(err, "Could not load payload");
4145103e761SPatrick Mooney 	}
4155103e761SPatrick Mooney 
4165103e761SPatrick Mooney 	return (ctx);
4175103e761SPatrick Mooney }
4185103e761SPatrick Mooney 
41972473353SPatrick Mooney void
test_reinitialize(struct vmctx * ctx,uint64_t flags)42072473353SPatrick Mooney test_reinitialize(struct vmctx *ctx, uint64_t flags)
42172473353SPatrick Mooney {
42272473353SPatrick Mooney 	int err;
42372473353SPatrick Mooney 
42472473353SPatrick Mooney 	if ((err = vm_reinit(ctx, flags)) != 0) {
42572473353SPatrick Mooney 		test_fail_errno(err, "Could not reinit VM");
42672473353SPatrick Mooney 	}
42772473353SPatrick Mooney 
42872473353SPatrick Mooney 	/* Reload tables and payload in case they were altered */
42972473353SPatrick Mooney 
43072473353SPatrick Mooney 	populate_identity_table(ctx);
43172473353SPatrick Mooney 	populate_desc_tables(ctx);
43272473353SPatrick Mooney 
43372473353SPatrick Mooney 	err = load_payload(ctx);
43472473353SPatrick Mooney 	if (err != 0) {
43572473353SPatrick Mooney 		test_fail_errno(err, "Could not load payload");
43672473353SPatrick Mooney 	}
43772473353SPatrick Mooney }
43872473353SPatrick Mooney 
4395103e761SPatrick Mooney int
test_setup_vcpu(struct vcpu * vcpu,uint64_t rip,uint64_t rsp)440*32640292SAndy Fiddaman test_setup_vcpu(struct vcpu *vcpu, uint64_t rip, uint64_t rsp)
4415103e761SPatrick Mooney {
4425103e761SPatrick Mooney 	int err;
4435103e761SPatrick Mooney 
444*32640292SAndy Fiddaman 	err = vm_activate_cpu(vcpu);
4455103e761SPatrick Mooney 	if (err != 0 && err != EBUSY) {
4465103e761SPatrick Mooney 		return (err);
4475103e761SPatrick Mooney 	}
4485103e761SPatrick Mooney 
4495103e761SPatrick Mooney 	/*
4505103e761SPatrick Mooney 	 * Granularity bit important here for VMX validity:
4515103e761SPatrick Mooney 	 * "If any bit in the limit field in the range 31:20 is 1, G must be 1"
4525103e761SPatrick Mooney 	 */
453*32640292SAndy Fiddaman 	err = vm_set_desc(vcpu, VM_REG_GUEST_CS, 0, UINT32_MAX,
4545103e761SPatrick Mooney 	    SDT_MEMERA | SEG_ACCESS_P | SEG_ACCESS_L | SEG_ACCESS_G);
4555103e761SPatrick Mooney 	if (err != 0) {
4565103e761SPatrick Mooney 		return (err);
4575103e761SPatrick Mooney 	}
4585103e761SPatrick Mooney 
459*32640292SAndy Fiddaman 	err = vm_set_desc(vcpu, VM_REG_GUEST_SS, 0, UINT32_MAX,
4605103e761SPatrick Mooney 	    SDT_MEMRWA | SEG_ACCESS_P | SEG_ACCESS_L |
4615103e761SPatrick Mooney 	    SEG_ACCESS_D | SEG_ACCESS_G);
4625103e761SPatrick Mooney 	if (err != 0) {
4635103e761SPatrick Mooney 		return (err);
4645103e761SPatrick Mooney 	}
4655103e761SPatrick Mooney 
466*32640292SAndy Fiddaman 	err = vm_set_desc(vcpu, VM_REG_GUEST_DS, 0, UINT32_MAX,
4675103e761SPatrick Mooney 	    SDT_MEMRWA | SEG_ACCESS_P | SEG_ACCESS_D | SEG_ACCESS_G);
4685103e761SPatrick Mooney 	if (err != 0) {
4695103e761SPatrick Mooney 		return (err);
4705103e761SPatrick Mooney 	}
4715103e761SPatrick Mooney 
4725103e761SPatrick Mooney 	/*
4735103e761SPatrick Mooney 	 * While SVM will happilly run with an otherwise unusable TR, VMX
4745103e761SPatrick Mooney 	 * includes it among its entry checks.
4755103e761SPatrick Mooney 	 */
476*32640292SAndy Fiddaman 	err = vm_set_desc(vcpu, VM_REG_GUEST_TR, MEM_LOC_TSS, 0xff,
4775103e761SPatrick Mooney 	    SDT_SYSTSSBSY | SEG_ACCESS_P);
4785103e761SPatrick Mooney 	if (err != 0) {
4795103e761SPatrick Mooney 		return (err);
4805103e761SPatrick Mooney 	}
481*32640292SAndy Fiddaman 	err = vm_set_desc(vcpu, VM_REG_GUEST_GDTR, MEM_LOC_GDT, 0x1ff, 0);
4825103e761SPatrick Mooney 	if (err != 0) {
4835103e761SPatrick Mooney 		return (err);
4845103e761SPatrick Mooney 	}
485*32640292SAndy Fiddaman 	err = vm_set_desc(vcpu, VM_REG_GUEST_IDTR, MEM_LOC_IDT, 0xfff, 0);
4865103e761SPatrick Mooney 	if (err != 0) {
4875103e761SPatrick Mooney 		return (err);
4885103e761SPatrick Mooney 	}
4895103e761SPatrick Mooney 
4905103e761SPatrick Mooney 	/* Mark unused segments as explicitly unusable (for VMX) */
4915103e761SPatrick Mooney 	const int unsable_segs[] = {
4925103e761SPatrick Mooney 		VM_REG_GUEST_ES,
4935103e761SPatrick Mooney 		VM_REG_GUEST_FS,
4945103e761SPatrick Mooney 		VM_REG_GUEST_GS,
4955103e761SPatrick Mooney 		VM_REG_GUEST_LDTR,
4965103e761SPatrick Mooney 	};
4975103e761SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(unsable_segs); i++) {
498*32640292SAndy Fiddaman 		err = vm_set_desc(vcpu, unsable_segs[i], 0, 0,
4995103e761SPatrick Mooney 		    SEG_ACCESS_UNUSABLE);
5005103e761SPatrick Mooney 		if (err != 0) {
5015103e761SPatrick Mooney 			return (err);
5025103e761SPatrick Mooney 		}
5035103e761SPatrick Mooney 	}
5045103e761SPatrick Mooney 
5055103e761SPatrick Mooney 	/* Place CPU directly in long mode */
5065103e761SPatrick Mooney 	const int regnums[] = {
5075103e761SPatrick Mooney 		VM_REG_GUEST_CR0,
5085103e761SPatrick Mooney 		VM_REG_GUEST_CR3,
5095103e761SPatrick Mooney 		VM_REG_GUEST_CR4,
5105103e761SPatrick Mooney 		VM_REG_GUEST_EFER,
5115103e761SPatrick Mooney 		VM_REG_GUEST_RFLAGS,
5125103e761SPatrick Mooney 		VM_REG_GUEST_RIP,
5135103e761SPatrick Mooney 		VM_REG_GUEST_RSP,
5145103e761SPatrick Mooney 		VM_REG_GUEST_CS,
5155103e761SPatrick Mooney 		VM_REG_GUEST_SS,
5165103e761SPatrick Mooney 		VM_REG_GUEST_DS,
5175103e761SPatrick Mooney 		VM_REG_GUEST_TR,
5185103e761SPatrick Mooney 	};
5195103e761SPatrick Mooney 	uint64_t regvals[] = {
5205103e761SPatrick Mooney 		CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_ET | CR0_TS |
5215103e761SPatrick Mooney 		    CR0_MP | CR0_PE,
5225103e761SPatrick Mooney 		MEM_LOC_PAGE_TABLE_512G,
5235103e761SPatrick Mooney 		CR4_DE | CR4_PSE | CR4_PAE | CR4_MCE | CR4_PGE | CR4_FSGSBASE,
5245103e761SPatrick Mooney 		AMD_EFER_SCE | AMD_EFER_LME | AMD_EFER_LMA | AMD_EFER_NXE,
5255103e761SPatrick Mooney 		/* start with interrupts disabled */
5265103e761SPatrick Mooney 		PS_MB1,
5275103e761SPatrick Mooney 		rip,
5285103e761SPatrick Mooney 		rsp,
5295103e761SPatrick Mooney 		(GDT_KCODE << 3),
5305103e761SPatrick Mooney 		(GDT_KDATA << 3),
5315103e761SPatrick Mooney 		(GDT_KDATA << 3),
5325103e761SPatrick Mooney 		(GDT_KTSS << 3),
5335103e761SPatrick Mooney 	};
5345103e761SPatrick Mooney 	assert(ARRAY_SIZE(regnums) == ARRAY_SIZE(regvals));
5355103e761SPatrick Mooney 
536*32640292SAndy Fiddaman 	err = vm_set_register_set(vcpu, ARRAY_SIZE(regnums), regnums,
5375103e761SPatrick Mooney 	    regvals);
5385103e761SPatrick Mooney 	if (err != 0) {
5395103e761SPatrick Mooney 		return (err);
5405103e761SPatrick Mooney 	}
5415103e761SPatrick Mooney 
542*32640292SAndy Fiddaman 	err = vm_set_run_state(vcpu, VRS_RUN, 0);
5435103e761SPatrick Mooney 	if (err != 0) {
5445103e761SPatrick Mooney 		return (err);
5455103e761SPatrick Mooney 	}
5465103e761SPatrick Mooney 
5475103e761SPatrick Mooney 	return (0);
5485103e761SPatrick Mooney }
5495103e761SPatrick Mooney 
5505103e761SPatrick Mooney static enum vm_exit_kind
which_exit_kind(struct vm_entry * ventry,const struct vm_exit * vexit)5515103e761SPatrick Mooney which_exit_kind(struct vm_entry *ventry, const struct vm_exit *vexit)
5525103e761SPatrick Mooney {
5535103e761SPatrick Mooney 	const struct vm_inout *inout = &vexit->u.inout;
5545103e761SPatrick Mooney 
5555103e761SPatrick Mooney 	switch (vexit->exitcode) {
5565103e761SPatrick Mooney 	case VM_EXITCODE_BOGUS:
5575103e761SPatrick Mooney 		bzero(ventry, sizeof (ventry));
5585103e761SPatrick Mooney 		return (VEK_REENTR);
5595103e761SPatrick Mooney 	case VM_EXITCODE_INOUT:
5605103e761SPatrick Mooney 		if (inout->port == IOP_TEST_RESULT &&
5615103e761SPatrick Mooney 		    (inout->flags & INOUT_IN) == 0) {
562d2f938fdSPatrick Mooney 			if (inout->eax == TEST_RESULT_PASS) {
5635103e761SPatrick Mooney 				return (VEK_TEST_PASS);
5645103e761SPatrick Mooney 			} else {
5655103e761SPatrick Mooney 				return (VEK_TEST_FAIL);
5665103e761SPatrick Mooney 			}
5675103e761SPatrick Mooney 		}
568a1d41cf9SPatrick Mooney 		if (inout->port == IOP_TEST_MSG &&
569a1d41cf9SPatrick Mooney 		    (inout->flags & INOUT_IN) == 0 &&
570a1d41cf9SPatrick Mooney 		    inout->bytes == 4) {
571a1d41cf9SPatrick Mooney 			test_msg_addr = inout->eax;
572a1d41cf9SPatrick Mooney 			ventry_fulfill_inout(vexit, ventry, 0);
573a1d41cf9SPatrick Mooney 			return (VEK_TEST_MSG);
574a1d41cf9SPatrick Mooney 		}
5755103e761SPatrick Mooney 		break;
5765103e761SPatrick Mooney 	default:
5775103e761SPatrick Mooney 		break;
5785103e761SPatrick Mooney 	}
5795103e761SPatrick Mooney 	return (VEK_UNHANDLED);
5805103e761SPatrick Mooney }
5815103e761SPatrick Mooney 
5825103e761SPatrick Mooney enum vm_exit_kind
test_run_vcpu(struct vcpu * vcpu,struct vm_entry * ventry,struct vm_exit * vexit)583*32640292SAndy Fiddaman test_run_vcpu(struct vcpu *vcpu, struct vm_entry *ventry, struct vm_exit *vexit)
5845103e761SPatrick Mooney {
5855103e761SPatrick Mooney 	int err;
5865103e761SPatrick Mooney 
587*32640292SAndy Fiddaman 	err = vm_run(vcpu, ventry, vexit);
5885103e761SPatrick Mooney 	if (err != 0) {
5895103e761SPatrick Mooney 		test_fail_errno(err, "Failure during vcpu entry");
5905103e761SPatrick Mooney 	}
5915103e761SPatrick Mooney 
5925103e761SPatrick Mooney 	return (which_exit_kind(ventry, vexit));
5935103e761SPatrick Mooney }
5945103e761SPatrick Mooney 
5955103e761SPatrick Mooney void
ventry_fulfill_inout(const struct vm_exit * vexit,struct vm_entry * ventry,uint32_t data)5965103e761SPatrick Mooney ventry_fulfill_inout(const struct vm_exit *vexit, struct vm_entry *ventry,
5975103e761SPatrick Mooney     uint32_t data)
5985103e761SPatrick Mooney {
5995103e761SPatrick Mooney 	VERIFY3U(vexit->exitcode, ==, VM_EXITCODE_INOUT);
6005103e761SPatrick Mooney 
6015103e761SPatrick Mooney 	ventry->cmd = VEC_FULFILL_INOUT;
6025103e761SPatrick Mooney 	bcopy(&vexit->u.inout, &ventry->u.inout, sizeof (struct vm_inout));
6035103e761SPatrick Mooney 	if ((ventry->u.inout.flags & INOUT_IN) != 0) {
6045103e761SPatrick Mooney 		ventry->u.inout.eax = data;
6055103e761SPatrick Mooney 	}
6065103e761SPatrick Mooney }
6075103e761SPatrick Mooney 
6085103e761SPatrick Mooney void
ventry_fulfill_mmio(const struct vm_exit * vexit,struct vm_entry * ventry,uint64_t data)6095103e761SPatrick Mooney ventry_fulfill_mmio(const struct vm_exit *vexit, struct vm_entry *ventry,
6105103e761SPatrick Mooney     uint64_t data)
6115103e761SPatrick Mooney {
6125103e761SPatrick Mooney 	VERIFY3U(vexit->exitcode, ==, VM_EXITCODE_MMIO);
6135103e761SPatrick Mooney 
6145103e761SPatrick Mooney 	ventry->cmd = VEC_FULFILL_MMIO;
6155103e761SPatrick Mooney 	bcopy(&vexit->u.mmio, &ventry->u.mmio, sizeof (struct vm_mmio));
6165103e761SPatrick Mooney 	if (ventry->u.mmio.read != 0) {
6175103e761SPatrick Mooney 		ventry->u.mmio.data = data;
6185103e761SPatrick Mooney 	}
6195103e761SPatrick Mooney }
6205103e761SPatrick Mooney 
6215103e761SPatrick Mooney bool
vexit_match_inout(const struct vm_exit * vexit,bool is_read,uint16_t port,uint_t len,uint32_t * valp)6225103e761SPatrick Mooney vexit_match_inout(const struct vm_exit *vexit, bool is_read, uint16_t port,
6235103e761SPatrick Mooney     uint_t len, uint32_t *valp)
6245103e761SPatrick Mooney {
6255103e761SPatrick Mooney 	if (vexit->exitcode != VM_EXITCODE_INOUT) {
6265103e761SPatrick Mooney 		return (false);
6275103e761SPatrick Mooney 	}
6285103e761SPatrick Mooney 
6295103e761SPatrick Mooney 	const uint_t flag = is_read ? INOUT_IN : 0;
6305103e761SPatrick Mooney 	if (vexit->u.inout.port != port ||
6315103e761SPatrick Mooney 	    vexit->u.inout.bytes != len ||
6325103e761SPatrick Mooney 	    (vexit->u.inout.flags & INOUT_IN) != flag) {
6335103e761SPatrick Mooney 		return (false);
6345103e761SPatrick Mooney 	}
6355103e761SPatrick Mooney 
6365103e761SPatrick Mooney 	if (!is_read && valp != NULL) {
6375103e761SPatrick Mooney 		*valp = vexit->u.inout.eax;
6385103e761SPatrick Mooney 	}
6395103e761SPatrick Mooney 	return (true);
6405103e761SPatrick Mooney }
6415103e761SPatrick Mooney 
6425103e761SPatrick Mooney bool
vexit_match_mmio(const struct vm_exit * vexit,bool is_read,uint64_t addr,uint_t len,uint64_t * valp)6435103e761SPatrick Mooney vexit_match_mmio(const struct vm_exit *vexit, bool is_read, uint64_t addr,
6445103e761SPatrick Mooney     uint_t len, uint64_t *valp)
6455103e761SPatrick Mooney {
6465103e761SPatrick Mooney 	if (vexit->exitcode != VM_EXITCODE_MMIO) {
6475103e761SPatrick Mooney 		return (false);
6485103e761SPatrick Mooney 	}
6495103e761SPatrick Mooney 
6505103e761SPatrick Mooney 	if (vexit->u.mmio.gpa != addr ||
6515103e761SPatrick Mooney 	    vexit->u.mmio.bytes != len ||
6525103e761SPatrick Mooney 	    (vexit->u.mmio.read != 0) != is_read) {
6535103e761SPatrick Mooney 		return (false);
6545103e761SPatrick Mooney 	}
6555103e761SPatrick Mooney 
6565103e761SPatrick Mooney 	if (!is_read && valp != NULL) {
6575103e761SPatrick Mooney 		*valp = vexit->u.mmio.data;
6585103e761SPatrick Mooney 	}
6595103e761SPatrick Mooney 	return (true);
6605103e761SPatrick Mooney }
661