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