xref: /illumos-gate/usr/src/test/bhyve-tests/tests/vmm/cpuid_ioctl.c (revision 578d9a563f6dc0f4f8a56447931b36db474172da)
1*578d9a56SPatrick Mooney /*
2*578d9a56SPatrick Mooney  * This file and its contents are supplied under the terms of the
3*578d9a56SPatrick Mooney  * Common Development and Distribution License ("CDDL"), version 1.0.
4*578d9a56SPatrick Mooney  * You may only use this file in accordance with the terms of version
5*578d9a56SPatrick Mooney  * 1.0 of the CDDL.
6*578d9a56SPatrick Mooney  *
7*578d9a56SPatrick Mooney  * A full copy of the text of the CDDL should have accompanied this
8*578d9a56SPatrick Mooney  * source.  A copy of the CDDL is also available via the Internet at
9*578d9a56SPatrick Mooney  * http://www.illumos.org/license/CDDL.
10*578d9a56SPatrick Mooney  */
11*578d9a56SPatrick Mooney 
12*578d9a56SPatrick Mooney /*
13*578d9a56SPatrick Mooney  * Copyright 2022 Oxide Computer Company
14*578d9a56SPatrick Mooney  */
15*578d9a56SPatrick Mooney 
16*578d9a56SPatrick Mooney #include <stdio.h>
17*578d9a56SPatrick Mooney #include <unistd.h>
18*578d9a56SPatrick Mooney #include <stdlib.h>
19*578d9a56SPatrick Mooney #include <fcntl.h>
20*578d9a56SPatrick Mooney #include <libgen.h>
21*578d9a56SPatrick Mooney #include <err.h>
22*578d9a56SPatrick Mooney #include <errno.h>
23*578d9a56SPatrick Mooney #include <strings.h>
24*578d9a56SPatrick Mooney 
25*578d9a56SPatrick Mooney #include <sys/vmm.h>
26*578d9a56SPatrick Mooney #include <sys/vmm_dev.h>
27*578d9a56SPatrick Mooney #include <vmmapi.h>
28*578d9a56SPatrick Mooney 
29*578d9a56SPatrick Mooney #include "common.h"
30*578d9a56SPatrick Mooney 
31*578d9a56SPatrick Mooney int
main(int argc,char * argv[])32*578d9a56SPatrick Mooney main(int argc, char *argv[])
33*578d9a56SPatrick Mooney {
34*578d9a56SPatrick Mooney 	const char *suite_name = basename(argv[0]);
35*578d9a56SPatrick Mooney 	struct vmctx *ctx;
36*578d9a56SPatrick Mooney 
37*578d9a56SPatrick Mooney 	ctx = create_test_vm(suite_name);
38*578d9a56SPatrick Mooney 	if (ctx == NULL) {
39*578d9a56SPatrick Mooney 		perror("could open test VM");
40*578d9a56SPatrick Mooney 		return (EXIT_FAILURE);
41*578d9a56SPatrick Mooney 	}
42*578d9a56SPatrick Mooney 	int vmfd = vm_get_device_fd(ctx);
43*578d9a56SPatrick Mooney 
44*578d9a56SPatrick Mooney 	struct vm_vcpu_cpuid_config cfg = { 0 };
45*578d9a56SPatrick Mooney 	struct vcpu_cpuid_entry *entries = NULL;
46*578d9a56SPatrick Mooney 
47*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_GET_CPUID, &cfg) != 0) {
48*578d9a56SPatrick Mooney 		err(EXIT_FAILURE, "ioctl(VM_GET_CPUID) failed");
49*578d9a56SPatrick Mooney 	}
50*578d9a56SPatrick Mooney 	if (cfg.vvcc_flags != VCC_FLAG_LEGACY_HANDLING) {
51*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
52*578d9a56SPatrick Mooney 		    "cpuid handling did not default to legacy-style");
53*578d9a56SPatrick Mooney 	}
54*578d9a56SPatrick Mooney 
55*578d9a56SPatrick Mooney 	cfg.vvcc_flags = ~VCC_FLAG_LEGACY_HANDLING;
56*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_SET_CPUID, &cfg) == 0) {
57*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
58*578d9a56SPatrick Mooney 		    "ioctl(VM_SET_CPUID) did not reject invalid flags");
59*578d9a56SPatrick Mooney 	}
60*578d9a56SPatrick Mooney 
61*578d9a56SPatrick Mooney 	entries = calloc(VMM_MAX_CPUID_ENTRIES + 1,
62*578d9a56SPatrick Mooney 	    sizeof (struct vcpu_cpuid_entry));
63*578d9a56SPatrick Mooney 	if (entries == NULL) {
64*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE, "could not allocate cpuid entries");
65*578d9a56SPatrick Mooney 	}
66*578d9a56SPatrick Mooney 
67*578d9a56SPatrick Mooney 	cfg.vvcc_flags = VCC_FLAG_LEGACY_HANDLING;
68*578d9a56SPatrick Mooney 	cfg.vvcc_nent = 1;
69*578d9a56SPatrick Mooney 	cfg.vvcc_entries = entries;
70*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_SET_CPUID, &cfg) == 0) {
71*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
72*578d9a56SPatrick Mooney 		    "ioctl(VM_SET_CPUID) did not reject entries when "
73*578d9a56SPatrick Mooney 		    "legacy-style handling was requested");
74*578d9a56SPatrick Mooney 	}
75*578d9a56SPatrick Mooney 
76*578d9a56SPatrick Mooney 	cfg.vvcc_flags = 0;
77*578d9a56SPatrick Mooney 	cfg.vvcc_nent = VMM_MAX_CPUID_ENTRIES + 1;
78*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_SET_CPUID, &cfg) == 0) {
79*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
80*578d9a56SPatrick Mooney 		    "ioctl(VM_SET_CPUID) did not reject excessive entry count");
81*578d9a56SPatrick Mooney 	}
82*578d9a56SPatrick Mooney 
83*578d9a56SPatrick Mooney 	cfg.vvcc_nent = 1;
84*578d9a56SPatrick Mooney 	entries[0].vce_flags = ~0;
85*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_SET_CPUID, &cfg) == 0) {
86*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
87*578d9a56SPatrick Mooney 		    "ioctl(VM_SET_CPUID) did not invalid entry flags");
88*578d9a56SPatrick Mooney 	}
89*578d9a56SPatrick Mooney 	entries[0].vce_flags = 0;
90*578d9a56SPatrick Mooney 
91*578d9a56SPatrick Mooney 	/* Actually set some entries to use for GET_CPUID testing */
92*578d9a56SPatrick Mooney 	const uint_t valid_entries = (VMM_MAX_CPUID_ENTRIES / 2);
93*578d9a56SPatrick Mooney 	for (uint_t i = 0; i < valid_entries; i++) {
94*578d9a56SPatrick Mooney 		entries[i].vce_function = i;
95*578d9a56SPatrick Mooney 	}
96*578d9a56SPatrick Mooney 	cfg.vvcc_nent = valid_entries;
97*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_SET_CPUID, &cfg) != 0) {
98*578d9a56SPatrick Mooney 		err(EXIT_FAILURE,
99*578d9a56SPatrick Mooney 		    "ioctl(VM_SET_CPUID) unable to set valid entries");
100*578d9a56SPatrick Mooney 	}
101*578d9a56SPatrick Mooney 
102*578d9a56SPatrick Mooney 	/* Try with no entries buffer */
103*578d9a56SPatrick Mooney 	bzero(&cfg, sizeof (cfg));
104*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_GET_CPUID, &cfg) == 0 || errno != E2BIG) {
105*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
106*578d9a56SPatrick Mooney 		    "ioctl(VM_GET_CPUID) did not fail absent buffer");
107*578d9a56SPatrick Mooney 	}
108*578d9a56SPatrick Mooney 	if (cfg.vvcc_nent != valid_entries) {
109*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
110*578d9a56SPatrick Mooney 		    "ioctl(VM_GET_CPUID) did not emit entry count "
111*578d9a56SPatrick Mooney 		    "(expected %u, got %u)", valid_entries, cfg.vvcc_nent);
112*578d9a56SPatrick Mooney 	}
113*578d9a56SPatrick Mooney 
114*578d9a56SPatrick Mooney 	/* Try with too-small entries buffer */
115*578d9a56SPatrick Mooney 	cfg.vvcc_nent = 1;
116*578d9a56SPatrick Mooney 	cfg.vvcc_entries = entries;
117*578d9a56SPatrick Mooney 	bzero(entries, valid_entries * sizeof (struct vcpu_cpuid_entry));
118*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_GET_CPUID, &cfg) == 0 || errno != E2BIG) {
119*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
120*578d9a56SPatrick Mooney 		    "ioctl(VM_GET_CPUID) did not fail too-small buffer");
121*578d9a56SPatrick Mooney 	}
122*578d9a56SPatrick Mooney 	if (cfg.vvcc_nent != valid_entries) {
123*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
124*578d9a56SPatrick Mooney 		    "ioctl(VM_GET_CPUID) did not emit entry count "
125*578d9a56SPatrick Mooney 		    "(expected %u, got %u)", valid_entries, cfg.vvcc_nent);
126*578d9a56SPatrick Mooney 	}
127*578d9a56SPatrick Mooney 
128*578d9a56SPatrick Mooney 	/* Try with adequate entries buffer */
129*578d9a56SPatrick Mooney 	cfg.vvcc_nent = valid_entries;
130*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_GET_CPUID, &cfg) != 0) {
131*578d9a56SPatrick Mooney 		err(EXIT_FAILURE, "ioctl(VM_GET_CPUID) failed");
132*578d9a56SPatrick Mooney 	}
133*578d9a56SPatrick Mooney 	if (cfg.vvcc_nent != valid_entries) {
134*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE,
135*578d9a56SPatrick Mooney 		    "ioctl(VM_GET_CPUID) did not emit entry count "
136*578d9a56SPatrick Mooney 		    "(expected %u, got %u)", valid_entries, cfg.vvcc_nent);
137*578d9a56SPatrick Mooney 	}
138*578d9a56SPatrick Mooney 	for (uint_t i = 0; i < valid_entries; i++) {
139*578d9a56SPatrick Mooney 		if (entries[i].vce_function != i) {
140*578d9a56SPatrick Mooney 			errx(EXIT_FAILURE, "unexpected entry contents");
141*578d9a56SPatrick Mooney 		}
142*578d9a56SPatrick Mooney 	}
143*578d9a56SPatrick Mooney 
144*578d9a56SPatrick Mooney 	/*
145*578d9a56SPatrick Mooney 	 * The legacy handling is simply using the host values with certain
146*578d9a56SPatrick Mooney 	 * modifications (masking, etc) applied.  The base leaf should be
147*578d9a56SPatrick Mooney 	 * exactly the same as we read from the host.
148*578d9a56SPatrick Mooney 	 *
149*578d9a56SPatrick Mooney 	 * Since a bhyve compat header has an inline-asm cpuid wrapper, use that
150*578d9a56SPatrick Mooney 	 * for now for querying the host
151*578d9a56SPatrick Mooney 	 */
152*578d9a56SPatrick Mooney 	struct vm_legacy_cpuid legacy  = { 0 };
153*578d9a56SPatrick Mooney 	if (ioctl(vmfd, VM_LEGACY_CPUID, &legacy) != 0) {
154*578d9a56SPatrick Mooney 		err(EXIT_FAILURE, "ioctl(VM_CPUID_LEGACY) failed");
155*578d9a56SPatrick Mooney 	}
156*578d9a56SPatrick Mooney 
157*578d9a56SPatrick Mooney 	uint32_t basic_cpuid[4];
158*578d9a56SPatrick Mooney 	cpuid_count(0, 0, basic_cpuid);
159*578d9a56SPatrick Mooney 	if (basic_cpuid[0] != legacy.vlc_eax ||
160*578d9a56SPatrick Mooney 	    basic_cpuid[1] != legacy.vlc_ebx ||
161*578d9a56SPatrick Mooney 	    basic_cpuid[2] != legacy.vlc_ecx ||
162*578d9a56SPatrick Mooney 	    basic_cpuid[3] != legacy.vlc_edx) {
163*578d9a56SPatrick Mooney 		errx(EXIT_FAILURE, "legacy cpuid mismatch");
164*578d9a56SPatrick Mooney 	}
165*578d9a56SPatrick Mooney 
166*578d9a56SPatrick Mooney 	vm_destroy(ctx);
167*578d9a56SPatrick Mooney 	(void) printf("%s\tPASS\n", suite_name);
168*578d9a56SPatrick Mooney 	return (EXIT_SUCCESS);
169*578d9a56SPatrick Mooney }
170