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 2023 Oxide Computer Company
14 */
15
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <strings.h>
19 #include <fcntl.h>
20 #include <errno.h>
21
22 #include <sys/types.h>
23 #include <sys/vmm.h>
24 #include <sys/vmm_dev.h>
25 #include <vmmapi.h>
26
27
28 /*
29 * Generate name for test VM based on the name of the test suite (and the pid).
30 */
31 void
name_test_vm(const char * test_suite_name,char * outp)32 name_test_vm(const char *test_suite_name, char *outp)
33 {
34 (void) snprintf(outp, VM_MAX_NAMELEN, "bhyve-test-%s-%d",
35 test_suite_name, getpid());
36 }
37
38 /*
39 * Create a test VM. The name of the test suite will be used to derive the name
40 * of the instance.
41 */
42 struct vmctx *
create_test_vm(const char * test_suite_name)43 create_test_vm(const char *test_suite_name)
44 {
45 char name[VM_MAX_NAMELEN];
46 int res;
47
48 name_test_vm(test_suite_name, name);
49
50 res = vm_create(name, 0);
51 if (res != 0) {
52 return (NULL);
53 }
54
55 return (vm_open(name));
56 }
57
58 /*
59 * Given a segment ID, length, and name, allocate a memseg in the given VM.
60 */
61 int
alloc_memseg(struct vmctx * ctx,int segid,size_t len,const char * name)62 alloc_memseg(struct vmctx *ctx, int segid, size_t len, const char *name)
63 {
64 struct vm_memseg memseg = {
65 .segid = segid,
66 .len = len,
67 };
68 (void) strlcpy(memseg.name, name, sizeof (memseg.name));
69
70 int fd = vm_get_device_fd(ctx);
71
72 return (ioctl(fd, VM_ALLOC_MEMSEG, &memseg));
73 }
74
75 /*
76 * Open the vmm_drv_test device.
77 */
78 int
open_drv_test(void)79 open_drv_test(void)
80 {
81 return (open("/dev/vmm_drv_test", O_RDWR));
82 }
83
84
85 /*
86 * Test if VMM instance exists (and is not being destroyed).
87 */
88 bool
check_instance_usable(const char * suite_name)89 check_instance_usable(const char *suite_name)
90 {
91 char vm_name[VM_MAX_NAMELEN];
92 char vm_path[MAXPATHLEN];
93
94 name_test_vm(suite_name, vm_name);
95 (void) snprintf(vm_path, sizeof (vm_path), "/dev/vmm/%s", vm_name);
96
97 int fd = open(vm_path, O_RDWR, 0);
98 if (fd < 0) {
99 return (false);
100 }
101
102 const int destroy_pending = ioctl(fd, VM_DESTROY_PENDING, 0);
103 (void) close(fd);
104
105 return (destroy_pending == 0);
106 }
107
108 /*
109 * Does an instance exist in /dev/vmm? (No check for in-progress destroy)
110 */
111 bool
check_instance_exists(const char * suite_name)112 check_instance_exists(const char *suite_name)
113 {
114 char vm_name[VM_MAX_NAMELEN];
115 char vm_path[MAXPATHLEN];
116
117 name_test_vm(suite_name, vm_name);
118 (void) snprintf(vm_path, sizeof (vm_path), "/dev/vmm/%s", vm_name);
119
120 return (access(vm_path, F_OK) == 0);
121 }
122
123
124 /*
125 * Destroy a VMM instance via the vmmctl device.
126 */
127 int
destroy_instance(const char * suite_name)128 destroy_instance(const char *suite_name)
129 {
130 int ctl_fd = open(VMM_CTL_DEV, O_EXCL | O_RDWR);
131 if (ctl_fd < 0) {
132 return (-1);
133 }
134
135 struct vm_destroy_req req;
136 name_test_vm(suite_name, req.name);
137
138 if (ioctl(ctl_fd, VMM_DESTROY_VM, &req) != 0) {
139 /* Preserve the destroy error across the close() */
140 int err = errno;
141 (void) close(ctl_fd);
142 errno = err;
143 return (-1);
144 } else {
145 (void) close(ctl_fd);
146 return (0);
147 }
148 }
149
150 /*
151 * Returns true if running on AMD
152 */
153 bool
cpu_vendor_amd(void)154 cpu_vendor_amd(void)
155 {
156 uint_t regs[4];
157 char cpu_vendor[13];
158
159 do_cpuid(0, regs);
160 ((uint_t *)&cpu_vendor)[0] = regs[1];
161 ((uint_t *)&cpu_vendor)[1] = regs[3];
162 ((uint_t *)&cpu_vendor)[2] = regs[2];
163 cpu_vendor[12] = '\0';
164
165 return (strcmp(cpu_vendor, "AuthenticAMD") == 0 ||
166 strcmp(cpu_vendor, "HygonGenuine") == 0);
167 }
168