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 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 * 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 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 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 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 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 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 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