1a77feb92SPatrick Mooney /*
2a77feb92SPatrick Mooney  * This file and its contents are supplied under the terms of the
3a77feb92SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
4a77feb92SPatrick Mooney  * You may only use this file in accordance with the terms of version
5a77feb92SPatrick Mooney  * 1.0 of the CDDL.
6a77feb92SPatrick Mooney  *
7a77feb92SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
8a77feb92SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
9a77feb92SPatrick Mooney  * http://www.illumos.org/license/CDDL.
10a77feb92SPatrick Mooney  */
11a77feb92SPatrick Mooney 
12a77feb92SPatrick Mooney /*
13*4bd36be4SPatrick Mooney  * Copyright 2024 Oxide Computer Company
14a77feb92SPatrick Mooney  */
15a77feb92SPatrick Mooney 
16a77feb92SPatrick Mooney #include <stdio.h>
17a77feb92SPatrick Mooney #include <unistd.h>
18a77feb92SPatrick Mooney #include <stdlib.h>
19a77feb92SPatrick Mooney #include <fcntl.h>
20a77feb92SPatrick Mooney #include <libgen.h>
21a77feb92SPatrick Mooney #include <sys/stat.h>
22a77feb92SPatrick Mooney #include <errno.h>
23a77feb92SPatrick Mooney #include <err.h>
24a77feb92SPatrick Mooney #include <assert.h>
25a77feb92SPatrick Mooney #include <sys/sysmacros.h>
26a77feb92SPatrick Mooney #include <stdbool.h>
27a77feb92SPatrick Mooney 
28a77feb92SPatrick Mooney #include <sys/vmm.h>
29a77feb92SPatrick Mooney #include <sys/vmm_dev.h>
30a77feb92SPatrick Mooney #include <sys/vmm_data.h>
31a77feb92SPatrick Mooney #include <vmmapi.h>
32a77feb92SPatrick Mooney 
33a77feb92SPatrick Mooney #include "common.h"
34a77feb92SPatrick Mooney 
35a77feb92SPatrick Mooney static void
should_eq_u32(const char * field_name,uint32_t a,uint32_t b)36a77feb92SPatrick Mooney should_eq_u32(const char *field_name, uint32_t a, uint32_t b)
37a77feb92SPatrick Mooney {
38a77feb92SPatrick Mooney 	if (a != b) {
39a77feb92SPatrick Mooney 		errx(EXIT_FAILURE, "unexpected %s %u != %u",
40a77feb92SPatrick Mooney 		    field_name, a, b);
41a77feb92SPatrick Mooney 	}
42a77feb92SPatrick Mooney }
43a77feb92SPatrick Mooney 
442c83e262SPatrick Mooney static void
test_size_boundaries(int vmfd)452c83e262SPatrick Mooney test_size_boundaries(int vmfd)
46a77feb92SPatrick Mooney {
47a77feb92SPatrick Mooney 	uint8_t buf[sizeof (struct vdi_atpic_v1) + sizeof (int)];
48a77feb92SPatrick Mooney 	struct vm_data_xfer vdx = {
49a77feb92SPatrick Mooney 		.vdx_class = VDC_ATPIC,
50a77feb92SPatrick Mooney 		.vdx_version = 1,
51a77feb92SPatrick Mooney 		.vdx_len = sizeof (struct vdi_atpic_v1),
52a77feb92SPatrick Mooney 		.vdx_data = buf,
53a77feb92SPatrick Mooney 	};
54a77feb92SPatrick Mooney 
55a77feb92SPatrick Mooney 	/* Attempt a valid-sized read first */
56a77feb92SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
572c83e262SPatrick Mooney 		err(EXIT_FAILURE, "valid VM_DATA_READ failed");
58a77feb92SPatrick Mooney 	}
59a77feb92SPatrick Mooney 	should_eq_u32("vdx_result_len", vdx.vdx_result_len,
60a77feb92SPatrick Mooney 	    sizeof (struct vdi_atpic_v1));
61a77feb92SPatrick Mooney 
622c83e262SPatrick Mooney 	/* And check that we can write it back */
632c83e262SPatrick Mooney 	vdx.vdx_result_len = 0;
642c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
652c83e262SPatrick Mooney 		err(EXIT_FAILURE, "valid VM_DATA_WRITE failed");
662c83e262SPatrick Mooney 	}
672c83e262SPatrick Mooney 
68a77feb92SPatrick Mooney 	/* ... then too-small ... */
69a77feb92SPatrick Mooney 	vdx.vdx_len = sizeof (struct vdi_atpic_v1) - sizeof (int);
70a77feb92SPatrick Mooney 	vdx.vdx_result_len = 0;
71a77feb92SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
722c83e262SPatrick Mooney 		errx(EXIT_FAILURE, "invalid VM_DATA_READ should have failed");
73a77feb92SPatrick Mooney 	}
74a77feb92SPatrick Mooney 	int error = errno;
75a77feb92SPatrick Mooney 	if (error != ENOSPC) {
76a77feb92SPatrick Mooney 		errx(EXIT_FAILURE, "expected ENOSPC errno, got %d", error);
77a77feb92SPatrick Mooney 	}
78a77feb92SPatrick Mooney 	/* the "correct" vdx_result_len should still be communicated out */
79a77feb92SPatrick Mooney 	should_eq_u32("vdx_result_len", vdx.vdx_result_len,
80a77feb92SPatrick Mooney 	    sizeof (struct vdi_atpic_v1));
81a77feb92SPatrick Mooney 
822c83e262SPatrick Mooney 	/* Repeat with too-small write */
832c83e262SPatrick Mooney 	vdx.vdx_result_len = 0;
842c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_WRITE, &vdx) == 0) {
852c83e262SPatrick Mooney 		errx(EXIT_FAILURE, "invalid VM_DATA_WRITE should have failed");
862c83e262SPatrick Mooney 	}
872c83e262SPatrick Mooney 	error = errno;
882c83e262SPatrick Mooney 	if (error != ENOSPC) {
892c83e262SPatrick Mooney 		errx(EXIT_FAILURE, "expected ENOSPC errno, got %d", error);
902c83e262SPatrick Mooney 	}
912c83e262SPatrick Mooney 	should_eq_u32("vdx_result_len", vdx.vdx_result_len,
922c83e262SPatrick Mooney 	    sizeof (struct vdi_atpic_v1));
932c83e262SPatrick Mooney 
94a77feb92SPatrick Mooney 	/*
95a77feb92SPatrick Mooney 	 * ... and too-big to round it out.
96a77feb92SPatrick Mooney 	 *
97a77feb92SPatrick Mooney 	 * This should pass, but still set vdx_result_len to the actual length
98a77feb92SPatrick Mooney 	 */
99a77feb92SPatrick Mooney 	vdx.vdx_len = sizeof (struct vdi_atpic_v1) + sizeof (int);
100a77feb92SPatrick Mooney 	vdx.vdx_result_len = 0;
101a77feb92SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
1022c83e262SPatrick Mooney 		err(EXIT_FAILURE, "too-large (but valid) VM_DATA_READ failed");
103a77feb92SPatrick Mooney 	}
104a77feb92SPatrick Mooney 	should_eq_u32("vdx_result_len", vdx.vdx_result_len,
105a77feb92SPatrick Mooney 	    sizeof (struct vdi_atpic_v1));
106a77feb92SPatrick Mooney 
1072c83e262SPatrick Mooney 	/* ... and repeated as a write */
1082c83e262SPatrick Mooney 	vdx.vdx_len = sizeof (struct vdi_atpic_v1) + sizeof (int);
1092c83e262SPatrick Mooney 	vdx.vdx_result_len = 0;
1102c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
1112c83e262SPatrick Mooney 		err(EXIT_FAILURE,
1122c83e262SPatrick Mooney 		    "too-large (but valid) VM_DATA_WRITE failed");
1132c83e262SPatrick Mooney 	}
1142c83e262SPatrick Mooney 	should_eq_u32("vdx_result_len", vdx.vdx_result_len,
1152c83e262SPatrick Mooney 	    sizeof (struct vdi_atpic_v1));
1162c83e262SPatrick Mooney }
1172c83e262SPatrick Mooney 
1182c83e262SPatrick Mooney struct class_test_case {
1192c83e262SPatrick Mooney 	uint16_t	ctc_class;
1202c83e262SPatrick Mooney 	uint16_t	ctc_version;
1212c83e262SPatrick Mooney };
1222c83e262SPatrick Mooney 
1232c83e262SPatrick Mooney static void
test_vm_classes(int vmfd)1242c83e262SPatrick Mooney test_vm_classes(int vmfd)
1252c83e262SPatrick Mooney {
1262c83e262SPatrick Mooney 	const struct class_test_case cases[] = {
1272c83e262SPatrick Mooney 		{ VDC_VERSION, 1 },
1282c83e262SPatrick Mooney 		{ VDC_VMM_ARCH, 1 },
1292c83e262SPatrick Mooney 		{ VDC_IOAPIC, 1 },
1302c83e262SPatrick Mooney 		{ VDC_ATPIT, 1 },
1312c83e262SPatrick Mooney 		{ VDC_ATPIC, 1 },
1322c83e262SPatrick Mooney 		{ VDC_HPET, 1 },
1332c83e262SPatrick Mooney 		{ VDC_PM_TIMER, 1 },
1342c83e262SPatrick Mooney 		{ VDC_RTC, 2 },
1352c83e262SPatrick Mooney 		{ VDC_VMM_TIME, 1 },
1362c83e262SPatrick Mooney 	};
1372c83e262SPatrick Mooney 
1382c83e262SPatrick Mooney 	/* A page should be large enough for all classes (for now) */
1392c83e262SPatrick Mooney 	const size_t bufsz = PAGESIZE;
1402c83e262SPatrick Mooney 	uint8_t *buf = malloc(bufsz);
1412c83e262SPatrick Mooney 
1422c83e262SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(cases); i++) {
1432c83e262SPatrick Mooney 		struct vm_data_xfer vdx = {
1442c83e262SPatrick Mooney 			.vdx_class = cases[i].ctc_class,
1452c83e262SPatrick Mooney 			.vdx_version = cases[i].ctc_version,
1462c83e262SPatrick Mooney 			.vdx_len = bufsz,
1472c83e262SPatrick Mooney 			.vdx_data = buf,
148*4bd36be4SPatrick Mooney 			.vdx_vcpuid = -1,
1492c83e262SPatrick Mooney 		};
1502c83e262SPatrick Mooney 
1512c83e262SPatrick Mooney 		/* First do a read */
1522c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
1532c83e262SPatrick Mooney 			err(EXIT_FAILURE,
1542c83e262SPatrick Mooney 			    "VM_DATA_READ failed class:%u version:%u",
1552c83e262SPatrick Mooney 			    vdx.vdx_class, vdx.vdx_version);
1562c83e262SPatrick Mooney 		}
1572c83e262SPatrick Mooney 		if (vdx.vdx_class == VDC_VERSION ||
1582c83e262SPatrick Mooney 		    vdx.vdx_class == VDC_VMM_ARCH) {
1592c83e262SPatrick Mooney 			/*
1602c83e262SPatrick Mooney 			 * Skip classes which contain some (or all) bits which
1612c83e262SPatrick Mooney 			 * are read-only.
1622c83e262SPatrick Mooney 			 */
1632c83e262SPatrick Mooney 			continue;
1642c83e262SPatrick Mooney 		}
1652c83e262SPatrick Mooney 
1662c83e262SPatrick Mooney 		/* Write the same data back */
1672c83e262SPatrick Mooney 		vdx.vdx_len = vdx.vdx_result_len;
1682c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
1692c83e262SPatrick Mooney 			err(EXIT_FAILURE,
1702c83e262SPatrick Mooney 			    "VM_DATA_WRITE failed class:%u version:%u",
1712c83e262SPatrick Mooney 			    vdx.vdx_class, vdx.vdx_version);
1722c83e262SPatrick Mooney 		}
1732c83e262SPatrick Mooney 	}
1742c83e262SPatrick Mooney 	free(buf);
1752c83e262SPatrick Mooney }
1762c83e262SPatrick Mooney 
1772c83e262SPatrick Mooney static void
test_vcpu_classes(int vmfd)1782c83e262SPatrick Mooney test_vcpu_classes(int vmfd)
1792c83e262SPatrick Mooney {
1802c83e262SPatrick Mooney 	const struct class_test_case cases[] = {
1812c83e262SPatrick Mooney 		{ VDC_MSR, 1 },
1822c83e262SPatrick Mooney 		{ VDC_LAPIC, 1 },
1832c83e262SPatrick Mooney 		{ VDC_VMM_ARCH, 1 },
1842c83e262SPatrick Mooney 
1852c83e262SPatrick Mooney 		/*
1862c83e262SPatrick Mooney 		 * Although these classes are per-vCPU, they have not yet been
1872c83e262SPatrick Mooney 		 * implemented in the vmm-data system, so are ignored for now:
1882c83e262SPatrick Mooney 		 *
1892c83e262SPatrick Mooney 		 * - VDC_REGISTER
1902c83e262SPatrick Mooney 		 * - VDC_FPU
1912c83e262SPatrick Mooney 		 * - VDC_LAPIC
1922c83e262SPatrick Mooney 		 */
1932c83e262SPatrick Mooney 	};
1942c83e262SPatrick Mooney 
1952c83e262SPatrick Mooney 	/* A page should be large enough for all classes (for now) */
1962c83e262SPatrick Mooney 	const size_t bufsz = PAGESIZE;
1972c83e262SPatrick Mooney 	uint8_t *buf = malloc(bufsz);
1982c83e262SPatrick Mooney 
1992c83e262SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(cases); i++) {
2002c83e262SPatrick Mooney 		struct vm_data_xfer vdx = {
2012c83e262SPatrick Mooney 			.vdx_class = cases[i].ctc_class,
2022c83e262SPatrick Mooney 			.vdx_version = cases[i].ctc_version,
2032c83e262SPatrick Mooney 			.vdx_len = bufsz,
2042c83e262SPatrick Mooney 			.vdx_data = buf,
2052c83e262SPatrick Mooney 			.vdx_vcpuid = 0,
2062c83e262SPatrick Mooney 		};
2072c83e262SPatrick Mooney 
2082c83e262SPatrick Mooney 		/* First do a read */
2092c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
2102c83e262SPatrick Mooney 			err(EXIT_FAILURE,
2112c83e262SPatrick Mooney 			    "VM_DATA_READ failed class:%u version:%u",
2122c83e262SPatrick Mooney 			    vdx.vdx_class, vdx.vdx_version);
2132c83e262SPatrick Mooney 		}
2142c83e262SPatrick Mooney 
2152c83e262SPatrick Mooney 		if (vdx.vdx_class == VDC_VMM_ARCH) {
2162c83e262SPatrick Mooney 			/*
2172c83e262SPatrick Mooney 			 * There are some read-only fields in VMM_ARCH which we
2182c83e262SPatrick Mooney 			 * do not want to attempt to write back.
2192c83e262SPatrick Mooney 			 */
2202c83e262SPatrick Mooney 			continue;
2212c83e262SPatrick Mooney 		}
2222c83e262SPatrick Mooney 
2232c83e262SPatrick Mooney 		/* Write the same data back */
2242c83e262SPatrick Mooney 		vdx.vdx_len = vdx.vdx_result_len;
2252c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
2262c83e262SPatrick Mooney 			err(EXIT_FAILURE,
2272c83e262SPatrick Mooney 			    "VM_DATA_WRITE failed class:%u version:%u",
2282c83e262SPatrick Mooney 			    vdx.vdx_class, vdx.vdx_version);
2292c83e262SPatrick Mooney 		}
2302c83e262SPatrick Mooney 	}
2312c83e262SPatrick Mooney 	free(buf);
2322c83e262SPatrick Mooney }
2332c83e262SPatrick Mooney 
2342c83e262SPatrick Mooney static void
test_bogus_class(int vmfd)2352c83e262SPatrick Mooney test_bogus_class(int vmfd)
2362c83e262SPatrick Mooney {
2372c83e262SPatrick Mooney 	const size_t bufsz = PAGESIZE;
2382c83e262SPatrick Mooney 	uint8_t *buf = malloc(bufsz);
2392c83e262SPatrick Mooney 
2402c83e262SPatrick Mooney 	struct vm_data_xfer vdx = {
2412c83e262SPatrick Mooney 		.vdx_class = 10000,
2422c83e262SPatrick Mooney 		.vdx_version = 1,
2432c83e262SPatrick Mooney 		.vdx_len = bufsz,
2442c83e262SPatrick Mooney 		.vdx_data = buf,
2452c83e262SPatrick Mooney 	};
2462c83e262SPatrick Mooney 
2472c83e262SPatrick Mooney 	/* Try to read with an absurd data class */
2482c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
2492c83e262SPatrick Mooney 		errx(EXIT_FAILURE,
2502c83e262SPatrick Mooney 		    "VM_DATA_READ should fail for absurd vdx_class");
2512c83e262SPatrick Mooney 	}
2522c83e262SPatrick Mooney 
2532c83e262SPatrick Mooney 	/* Same for data version */
2542c83e262SPatrick Mooney 	vdx.vdx_class = VDC_VERSION;
2552c83e262SPatrick Mooney 	vdx.vdx_version = 10000;
2562c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
2572c83e262SPatrick Mooney 		errx(EXIT_FAILURE,
2582c83e262SPatrick Mooney 		    "VM_DATA_READ should fail for absurd vdx_version");
2592c83e262SPatrick Mooney 	}
2602c83e262SPatrick Mooney 
2612c83e262SPatrick Mooney 	free(buf);
2622c83e262SPatrick Mooney }
2632c83e262SPatrick Mooney 
2642c83e262SPatrick Mooney static void
test_vcpuid_combos(int vmfd)2652c83e262SPatrick Mooney test_vcpuid_combos(int vmfd)
2662c83e262SPatrick Mooney {
2672c83e262SPatrick Mooney 	const size_t bufsz = PAGESIZE;
2682c83e262SPatrick Mooney 	uint8_t *buf = malloc(bufsz);
2692c83e262SPatrick Mooney 
2702c83e262SPatrick Mooney 	struct vm_data_xfer vdx = {
2712c83e262SPatrick Mooney 		.vdx_class = VDC_LAPIC,
2722c83e262SPatrick Mooney 		.vdx_version = 1,
2732c83e262SPatrick Mooney 		.vdx_len = bufsz,
2742c83e262SPatrick Mooney 		.vdx_data = buf,
2752c83e262SPatrick Mooney 	};
2762c83e262SPatrick Mooney 
2772c83e262SPatrick Mooney 	/* Try with -1 sentinel, too-negative, and too-positive values */
2782c83e262SPatrick Mooney 	const int bad_per_vcpu[] = { -1, -5, 1000 };
2792c83e262SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(bad_per_vcpu); i++) {
2802c83e262SPatrick Mooney 		vdx.vdx_vcpuid = bad_per_vcpu[i];
2812c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
2822c83e262SPatrick Mooney 			errx(EXIT_FAILURE,
2832c83e262SPatrick Mooney 			    "VM_DATA_READ should fail for bad vcpuid %d",
2842c83e262SPatrick Mooney 			    vdx.vdx_vcpuid);
2852c83e262SPatrick Mooney 		}
2862c83e262SPatrick Mooney 	}
2872c83e262SPatrick Mooney 
2882c83e262SPatrick Mooney 	/*
2892c83e262SPatrick Mooney 	 * Valid vcpuid should be fine still.  Reading valid data into the
2902c83e262SPatrick Mooney 	 * buffer will be useful to subsequently test writes.
2912c83e262SPatrick Mooney 	 */
2922c83e262SPatrick Mooney 	vdx.vdx_vcpuid = 0;
2932c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
2942c83e262SPatrick Mooney 		err(EXIT_FAILURE, "failed VM_DATA_READ with valid vcpuid");
2952c83e262SPatrick Mooney 	}
2962c83e262SPatrick Mooney 
2972c83e262SPatrick Mooney 	/* Repeat the same checks for writes */
2982c83e262SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(bad_per_vcpu); i++) {
2992c83e262SPatrick Mooney 		vdx.vdx_vcpuid = bad_per_vcpu[i];
3002c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_WRITE, &vdx) == 0) {
3012c83e262SPatrick Mooney 			errx(EXIT_FAILURE,
3022c83e262SPatrick Mooney 			    "VM_DATA_WRITE should fail for bad vcpuid %d",
3032c83e262SPatrick Mooney 			    vdx.vdx_vcpuid);
3042c83e262SPatrick Mooney 		}
3052c83e262SPatrick Mooney 	}
3062c83e262SPatrick Mooney 
3072c83e262SPatrick Mooney 	vdx.vdx_vcpuid = 0;
3082c83e262SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
3092c83e262SPatrick Mooney 		err(EXIT_FAILURE, "failed VM_DATA_WRITE with valid vcpuid");
3102c83e262SPatrick Mooney 	}
3112c83e262SPatrick Mooney 
3122c83e262SPatrick Mooney 	vdx.vdx_class = VDC_VERSION;
3132c83e262SPatrick Mooney 	vdx.vdx_version = 1;
3142c83e262SPatrick Mooney 
3152c83e262SPatrick Mooney 	/*
3162c83e262SPatrick Mooney 	 * VM-wide classes should work fine with the -1 sentinel.  For now,
3172c83e262SPatrick Mooney 	 * passing an otherwise valid vcpuid will still work, but that id is
3182c83e262SPatrick Mooney 	 * ignored.
3192c83e262SPatrick Mooney 	 */
3202c83e262SPatrick Mooney 	const int good_vm_wide[] = { -1, 0, 1 };
3212c83e262SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(good_vm_wide); i++) {
3222c83e262SPatrick Mooney 		vdx.vdx_vcpuid = good_vm_wide[i];
3232c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
3242c83e262SPatrick Mooney 			err(EXIT_FAILURE,
3252c83e262SPatrick Mooney 			    "failed VM-wide VM_DATA_READ with vcpuid %d",
3262c83e262SPatrick Mooney 			    vdx.vdx_vcpuid);
3272c83e262SPatrick Mooney 		}
3282c83e262SPatrick Mooney 	}
3292c83e262SPatrick Mooney 
3302c83e262SPatrick Mooney 	/* Bogus values should still fail */
3312c83e262SPatrick Mooney 	const int bad_vm_wide[] = { -5, 1000 };
3322c83e262SPatrick Mooney 	for (uint_t i = 0; i < ARRAY_SIZE(bad_vm_wide); i++) {
3332c83e262SPatrick Mooney 		vdx.vdx_vcpuid = bad_vm_wide[i];
3342c83e262SPatrick Mooney 		if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
3352c83e262SPatrick Mooney 			errx(EXIT_FAILURE,
3362c83e262SPatrick Mooney 			    "VM_DATA_READ should fail for bad vcpuid %d",
3372c83e262SPatrick Mooney 			    vdx.vdx_vcpuid);
3382c83e262SPatrick Mooney 		}
3392c83e262SPatrick Mooney 	}
3402c83e262SPatrick Mooney 
3412c83e262SPatrick Mooney 	free(buf);
3422c83e262SPatrick Mooney }
3432c83e262SPatrick Mooney 
344*4bd36be4SPatrick Mooney static void
test_vcpuid_time(int vmfd)345*4bd36be4SPatrick Mooney test_vcpuid_time(int vmfd)
346*4bd36be4SPatrick Mooney {
347*4bd36be4SPatrick Mooney 	struct vdi_time_info_v1 data;
348*4bd36be4SPatrick Mooney 	struct vm_data_xfer vdx = {
349*4bd36be4SPatrick Mooney 		.vdx_class = VDC_VMM_TIME,
350*4bd36be4SPatrick Mooney 		.vdx_version = 1,
351*4bd36be4SPatrick Mooney 		.vdx_len = sizeof (data),
352*4bd36be4SPatrick Mooney 		.vdx_data = &data,
353*4bd36be4SPatrick Mooney 	};
354*4bd36be4SPatrick Mooney 
355*4bd36be4SPatrick Mooney 	/* This should work with the system-wide vcpuid */
356*4bd36be4SPatrick Mooney 	vdx.vdx_vcpuid = -1;
357*4bd36be4SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) != 0) {
358*4bd36be4SPatrick Mooney 		err(EXIT_FAILURE, "VM_DATA_READ failed for valid vcpuid");
359*4bd36be4SPatrick Mooney 	}
360*4bd36be4SPatrick Mooney 
361*4bd36be4SPatrick Mooney 	/* But fail for other vcpuids */
362*4bd36be4SPatrick Mooney 	vdx.vdx_vcpuid = 0;
363*4bd36be4SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_READ, &vdx) == 0) {
364*4bd36be4SPatrick Mooney 		err(EXIT_FAILURE, "VM_DATA_READ should fail for vcpuid %d",
365*4bd36be4SPatrick Mooney 		    vdx.vdx_vcpuid);
366*4bd36be4SPatrick Mooney 	}
367*4bd36be4SPatrick Mooney 
368*4bd36be4SPatrick Mooney 	/*
369*4bd36be4SPatrick Mooney 	 * Perform same check for writes
370*4bd36be4SPatrick Mooney 	 *
371*4bd36be4SPatrick Mooney 	 * Normally this would require care to handle hosts which lack frequency
372*4bd36be4SPatrick Mooney 	 * scaling functionality, but since we are writing back the same data,
373*4bd36be4SPatrick Mooney 	 * the guest frequency should match that of the host, requiring no real
374*4bd36be4SPatrick Mooney 	 * scaling be done for the instance.
375*4bd36be4SPatrick Mooney 	 */
376*4bd36be4SPatrick Mooney 	vdx.vdx_vcpuid = -1;
377*4bd36be4SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_WRITE, &vdx) != 0) {
378*4bd36be4SPatrick Mooney 		err(EXIT_FAILURE, "VM_DATA_WRITE failed for valid vcpuid");
379*4bd36be4SPatrick Mooney 	}
380*4bd36be4SPatrick Mooney 	vdx.vdx_vcpuid = 0;
381*4bd36be4SPatrick Mooney 	if (ioctl(vmfd, VM_DATA_WRITE, &vdx) == 0) {
382*4bd36be4SPatrick Mooney 		errx(EXIT_FAILURE, "VM_DATA_READ should fail for vcpuid %d",
383*4bd36be4SPatrick Mooney 		    vdx.vdx_vcpuid);
384*4bd36be4SPatrick Mooney 	}
385*4bd36be4SPatrick Mooney }
386*4bd36be4SPatrick Mooney 
3872c83e262SPatrick Mooney int
main(int argc,char * argv[])3882c83e262SPatrick Mooney main(int argc, char *argv[])
3892c83e262SPatrick Mooney {
3902c83e262SPatrick Mooney 	const char *suite_name = basename(argv[0]);
3912c83e262SPatrick Mooney 	struct vmctx *ctx;
3922c83e262SPatrick Mooney 
3932c83e262SPatrick Mooney 	ctx = create_test_vm(suite_name);
3942c83e262SPatrick Mooney 	if (ctx == NULL) {
3952c83e262SPatrick Mooney 		errx(EXIT_FAILURE, "could not open test VM");
3962c83e262SPatrick Mooney 	}
3972c83e262SPatrick Mooney 
398a77feb92SPatrick Mooney 	/*
3992c83e262SPatrick Mooney 	 * Check that vmm_data import/export facility is robust in the face of
4002c83e262SPatrick Mooney 	 * potentially invalid inputs
401a77feb92SPatrick Mooney 	 */
4022c83e262SPatrick Mooney 	const int vmfd = vm_get_device_fd(ctx);
4032c83e262SPatrick Mooney 
4042c83e262SPatrick Mooney 	/* Test varies edge cases around data transfer sizes */
4052c83e262SPatrick Mooney 	test_size_boundaries(vmfd);
4062c83e262SPatrick Mooney 
4072c83e262SPatrick Mooney 	/* Check that known VM-wide data classes can be accessed */
4082c83e262SPatrick Mooney 	test_vm_classes(vmfd);
4092c83e262SPatrick Mooney 
4102c83e262SPatrick Mooney 	/* Check that known per-vCPU data classes can be accessed */
4112c83e262SPatrick Mooney 	test_vcpu_classes(vmfd);
4122c83e262SPatrick Mooney 
4132c83e262SPatrick Mooney 	/* Try some bogus class/version combos */
4142c83e262SPatrick Mooney 	test_bogus_class(vmfd);
4152c83e262SPatrick Mooney 
4162c83e262SPatrick Mooney 	/* Try some weird vdx_vcpuid cases */
4172c83e262SPatrick Mooney 	test_vcpuid_combos(vmfd);
418a77feb92SPatrick Mooney 
419*4bd36be4SPatrick Mooney 	/* VMM_TIME is picky about vcpuid */
420*4bd36be4SPatrick Mooney 	test_vcpuid_time(vmfd);
421*4bd36be4SPatrick Mooney 
422a77feb92SPatrick Mooney 	vm_destroy(ctx);
423a77feb92SPatrick Mooney 	(void) printf("%s\tPASS\n", suite_name);
424a77feb92SPatrick Mooney 	return (EXIT_SUCCESS);
425a77feb92SPatrick Mooney }
426