1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2022 Oxide Computer Company
14  */
15 
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <strings.h>
20 #include <libgen.h>
21 #include <assert.h>
22 #include <errno.h>
23 
24 #include <sys/types.h>
25 #include <sys/sysmacros.h>
26 #include <sys/debug.h>
27 #include <sys/vmm.h>
28 #include <sys/vmm_dev.h>
29 #include <vmmapi.h>
30 
31 #include "in_guest.h"
32 
33 static const struct vcpu_cpuid_entry test_entries[] = {
34 	{
35 		.vce_function = 0,
36 		.vce_eax = 5,
37 		.vce_ebx = 0x74737552,
38 		.vce_edx = 0x4f206465,
39 		.vce_ecx = 0x65646978,
40 	},
41 	/* basic "std" leaf */
42 	{
43 		.vce_function = 1,
44 		.vce_eax = 0x100,
45 	},
46 
47 	/* skip 2 for a hole */
48 
49 	/* leaf with index matching */
50 	{
51 		.vce_function = 3,
52 		.vce_index = 0,
53 		.vce_flags = VCE_FLAG_MATCH_INDEX,
54 		.vce_eax = 0x300,
55 	},
56 	{
57 		.vce_function = 3,
58 		.vce_index = 1,
59 		.vce_flags = VCE_FLAG_MATCH_INDEX,
60 		.vce_eax = 0x301,
61 	},
62 
63 	/* leaf with index matching and a hole */
64 	{
65 		.vce_function = 4,
66 		.vce_index = 0,
67 		.vce_flags = VCE_FLAG_MATCH_INDEX,
68 		.vce_eax = 0x400,
69 	},
70 	{
71 		.vce_function = 4,
72 		.vce_index = 2,
73 		.vce_flags = VCE_FLAG_MATCH_INDEX,
74 		.vce_eax = 0x402,
75 	},
76 
77 	/* terminal "std" leaf */
78 	{
79 		.vce_function = 5,
80 		.vce_eax = 5,
81 		.vce_ebx = 5,
82 		.vce_edx = 5,
83 		.vce_ecx = 5,
84 	},
85 
86 	/* base "extended" leaf */
87 	{
88 		.vce_function = 0x80000000,
89 		.vce_eax = 0x80000001,
90 	},
91 	/* index-match "extended" leaves */
92 	{
93 		.vce_function = 0x80000001,
94 		.vce_index = 0x0,
95 		.vce_flags = VCE_FLAG_MATCH_INDEX,
96 		.vce_eax = 0x8000,
97 	},
98 	{
99 		.vce_function = 0x80000001,
100 		.vce_index = 0x1,
101 		.vce_flags = VCE_FLAG_MATCH_INDEX,
102 		.vce_eax = 0x8001,
103 	},
104 };
105 
106 int
main(int argc,char * argv[])107 main(int argc, char *argv[])
108 {
109 	const char *test_suite_name = basename(argv[0]);
110 	struct vmctx *ctx = NULL;
111 	struct vcpu *vcpu;
112 	int err;
113 
114 	ctx = test_initialize(test_suite_name);
115 
116 	if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
117 		test_fail_errno(errno, "Could not open vcpu0");
118 	}
119 
120 	err = test_setup_vcpu(vcpu, MEM_LOC_PAYLOAD, MEM_LOC_STACK);
121 	if (err != 0) {
122 		test_fail_errno(err, "Could not initialize vcpu0");
123 	}
124 
125 
126 	/* Start with test data using Intel-style fallback */
127 	int vmfd = vm_get_device_fd(ctx);
128 
129 	struct vm_vcpu_cpuid_config cfg = {
130 		.vvcc_vcpuid = 0,
131 		.vvcc_flags = VCC_FLAG_INTEL_FALLBACK,
132 		.vvcc_nent = ARRAY_SIZE(test_entries),
133 		/* We trust the ioctl not to alter this const value */
134 		.vvcc_entries = (struct vcpu_cpuid_entry *)test_entries,
135 	};
136 	err = ioctl(vmfd, VM_SET_CPUID, &cfg);
137 	if (err != 0) {
138 		test_fail_errno(err, "ioctl(VM_SET_CPUID) failed");
139 	}
140 
141 	struct vm_entry ventry = { 0 };
142 	struct vm_exit vexit = { 0 };
143 
144 	do {
145 		const enum vm_exit_kind kind =
146 		    test_run_vcpu(vcpu, &ventry, &vexit);
147 		switch (kind) {
148 		case VEK_REENTR:
149 			break;
150 		case VEK_TEST_PASS:
151 			test_pass();
152 			break;
153 		case VEK_TEST_FAIL:
154 			test_fail_msg("failed result %rip: %x", vexit.rip);
155 			break;
156 		case VEK_UNHANDLED: {
157 			uint32_t val;
158 			if (vexit_match_inout(&vexit, false, IOP_TEST_VALUE, 4,
159 			    &val)) {
160 				/*
161 				 * The payload has requested switch to AMD-style
162 				 * fallback to run the second half of the test.
163 				 */
164 				cfg.vvcc_flags = 0;
165 				err = ioctl(vmfd, VM_SET_CPUID, &cfg);
166 				if (err != 0) {
167 					test_fail_errno(err,
168 					    "ioctl(VM_SET_CPUID) failed");
169 				}
170 				ventry_fulfill_inout(&vexit, &ventry, 0);
171 			} else {
172 				test_fail_vmexit(&vexit);
173 			}
174 			break;
175 		}
176 
177 		default:
178 			test_fail_vmexit(&vexit);
179 			break;
180 		}
181 	} while (true);
182 }
183