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