14ac713daSLuqman Aden /*
24ac713daSLuqman Aden * This file and its contents are supplied under the terms of the
34ac713daSLuqman Aden * Common Development and Distribution License ("CDDL"), version 1.0.
44ac713daSLuqman Aden * You may only use this file in accordance with the terms of version
54ac713daSLuqman Aden * 1.0 of the CDDL.
64ac713daSLuqman Aden *
74ac713daSLuqman Aden * A full copy of the text of the CDDL should have accompanied this
84ac713daSLuqman Aden * source. A copy of the CDDL is also available via the Internet at
94ac713daSLuqman Aden * http://www.illumos.org/license/CDDL.
104ac713daSLuqman Aden */
114ac713daSLuqman Aden
124ac713daSLuqman Aden /*
13*717646f7SJordan Paige Hendricks * Copyright 2023 Oxide Computer Company
144ac713daSLuqman Aden */
154ac713daSLuqman Aden
164ac713daSLuqman Aden #include <stdio.h>
174ac713daSLuqman Aden #include <unistd.h>
184ac713daSLuqman Aden #include <strings.h>
194ac713daSLuqman Aden #include <fcntl.h>
204ac713daSLuqman Aden #include <errno.h>
214ac713daSLuqman Aden
224ac713daSLuqman Aden #include <sys/types.h>
234ac713daSLuqman Aden #include <sys/vmm.h>
244ac713daSLuqman Aden #include <sys/vmm_dev.h>
254ac713daSLuqman Aden #include <vmmapi.h>
264ac713daSLuqman Aden
274ac713daSLuqman Aden
284ac713daSLuqman Aden /*
294ac713daSLuqman Aden * Generate name for test VM based on the name of the test suite (and the pid).
304ac713daSLuqman Aden */
314ac713daSLuqman Aden void
name_test_vm(const char * test_suite_name,char * outp)324ac713daSLuqman Aden name_test_vm(const char *test_suite_name, char *outp)
334ac713daSLuqman Aden {
344ac713daSLuqman Aden (void) snprintf(outp, VM_MAX_NAMELEN, "bhyve-test-%s-%d",
354ac713daSLuqman Aden test_suite_name, getpid());
364ac713daSLuqman Aden }
374ac713daSLuqman Aden
384ac713daSLuqman Aden /*
394ac713daSLuqman Aden * Create a test VM. The name of the test suite will be used to derive the name
404ac713daSLuqman Aden * of the instance.
414ac713daSLuqman Aden */
424ac713daSLuqman Aden struct vmctx *
create_test_vm(const char * test_suite_name)434ac713daSLuqman Aden create_test_vm(const char *test_suite_name)
444ac713daSLuqman Aden {
454ac713daSLuqman Aden char name[VM_MAX_NAMELEN];
464ac713daSLuqman Aden int res;
474ac713daSLuqman Aden
484ac713daSLuqman Aden name_test_vm(test_suite_name, name);
494ac713daSLuqman Aden
504ac713daSLuqman Aden res = vm_create(name, 0);
514ac713daSLuqman Aden if (res != 0) {
524ac713daSLuqman Aden return (NULL);
534ac713daSLuqman Aden }
544ac713daSLuqman Aden
554ac713daSLuqman Aden return (vm_open(name));
564ac713daSLuqman Aden }
574ac713daSLuqman Aden
584ac713daSLuqman Aden /*
594ac713daSLuqman Aden * Given a segment ID, length, and name, allocate a memseg in the given VM.
604ac713daSLuqman Aden */
614ac713daSLuqman Aden int
alloc_memseg(struct vmctx * ctx,int segid,size_t len,const char * name)624ac713daSLuqman Aden alloc_memseg(struct vmctx *ctx, int segid, size_t len, const char *name)
634ac713daSLuqman Aden {
644ac713daSLuqman Aden struct vm_memseg memseg = {
654ac713daSLuqman Aden .segid = segid,
664ac713daSLuqman Aden .len = len,
674ac713daSLuqman Aden };
684ac713daSLuqman Aden (void) strlcpy(memseg.name, name, sizeof (memseg.name));
694ac713daSLuqman Aden
704ac713daSLuqman Aden int fd = vm_get_device_fd(ctx);
714ac713daSLuqman Aden
724ac713daSLuqman Aden return (ioctl(fd, VM_ALLOC_MEMSEG, &memseg));
734ac713daSLuqman Aden }
744ac713daSLuqman Aden
754ac713daSLuqman Aden /*
764ac713daSLuqman Aden * Open the vmm_drv_test device.
774ac713daSLuqman Aden */
784ac713daSLuqman Aden int
open_drv_test(void)794ac713daSLuqman Aden open_drv_test(void)
804ac713daSLuqman Aden {
814ac713daSLuqman Aden return (open("/dev/vmm_drv_test", O_RDWR));
824ac713daSLuqman Aden }
834ac713daSLuqman Aden
844ac713daSLuqman Aden
854ac713daSLuqman Aden /*
864ac713daSLuqman Aden * Test if VMM instance exists (and is not being destroyed).
874ac713daSLuqman Aden */
884ac713daSLuqman Aden bool
check_instance_usable(const char * suite_name)894ac713daSLuqman Aden check_instance_usable(const char *suite_name)
904ac713daSLuqman Aden {
914ac713daSLuqman Aden char vm_name[VM_MAX_NAMELEN];
924ac713daSLuqman Aden char vm_path[MAXPATHLEN];
934ac713daSLuqman Aden
944ac713daSLuqman Aden name_test_vm(suite_name, vm_name);
954ac713daSLuqman Aden (void) snprintf(vm_path, sizeof (vm_path), "/dev/vmm/%s", vm_name);
964ac713daSLuqman Aden
974ac713daSLuqman Aden int fd = open(vm_path, O_RDWR, 0);
984ac713daSLuqman Aden if (fd < 0) {
994ac713daSLuqman Aden return (false);
1004ac713daSLuqman Aden }
1014ac713daSLuqman Aden
1024ac713daSLuqman Aden const int destroy_pending = ioctl(fd, VM_DESTROY_PENDING, 0);
1034ac713daSLuqman Aden (void) close(fd);
1044ac713daSLuqman Aden
1054ac713daSLuqman Aden return (destroy_pending == 0);
1064ac713daSLuqman Aden }
1074ac713daSLuqman Aden
1084ac713daSLuqman Aden /*
1094ac713daSLuqman Aden * Does an instance exist in /dev/vmm? (No check for in-progress destroy)
1104ac713daSLuqman Aden */
1114ac713daSLuqman Aden bool
check_instance_exists(const char * suite_name)1124ac713daSLuqman Aden check_instance_exists(const char *suite_name)
1134ac713daSLuqman Aden {
1144ac713daSLuqman Aden char vm_name[VM_MAX_NAMELEN];
1154ac713daSLuqman Aden char vm_path[MAXPATHLEN];
1164ac713daSLuqman Aden
1174ac713daSLuqman Aden name_test_vm(suite_name, vm_name);
1184ac713daSLuqman Aden (void) snprintf(vm_path, sizeof (vm_path), "/dev/vmm/%s", vm_name);
1194ac713daSLuqman Aden
1204ac713daSLuqman Aden return (access(vm_path, F_OK) == 0);
1214ac713daSLuqman Aden }
1224ac713daSLuqman Aden
1234ac713daSLuqman Aden
1244ac713daSLuqman Aden /*
1254ac713daSLuqman Aden * Destroy a VMM instance via the vmmctl device.
1264ac713daSLuqman Aden */
1274ac713daSLuqman Aden int
destroy_instance(const char * suite_name)1284ac713daSLuqman Aden destroy_instance(const char *suite_name)
1294ac713daSLuqman Aden {
1304ac713daSLuqman Aden int ctl_fd = open(VMM_CTL_DEV, O_EXCL | O_RDWR);
1314ac713daSLuqman Aden if (ctl_fd < 0) {
1324ac713daSLuqman Aden return (-1);
1334ac713daSLuqman Aden }
1344ac713daSLuqman Aden
1354ac713daSLuqman Aden struct vm_destroy_req req;
1364ac713daSLuqman Aden name_test_vm(suite_name, req.name);
1374ac713daSLuqman Aden
1384ac713daSLuqman Aden if (ioctl(ctl_fd, VMM_DESTROY_VM, &req) != 0) {
1394ac713daSLuqman Aden /* Preserve the destroy error across the close() */
1404ac713daSLuqman Aden int err = errno;
1414ac713daSLuqman Aden (void) close(ctl_fd);
1424ac713daSLuqman Aden errno = err;
1434ac713daSLuqman Aden return (-1);
1444ac713daSLuqman Aden } else {
1454ac713daSLuqman Aden (void) close(ctl_fd);
1464ac713daSLuqman Aden return (0);
1474ac713daSLuqman Aden }
1484ac713daSLuqman Aden }
149*717646f7SJordan Paige Hendricks
150*717646f7SJordan Paige Hendricks /*
151*717646f7SJordan Paige Hendricks * Returns true if running on AMD
152*717646f7SJordan Paige Hendricks */
153*717646f7SJordan Paige Hendricks bool
cpu_vendor_amd(void)154*717646f7SJordan Paige Hendricks cpu_vendor_amd(void)
155*717646f7SJordan Paige Hendricks {
156*717646f7SJordan Paige Hendricks uint_t regs[4];
157*717646f7SJordan Paige Hendricks char cpu_vendor[13];
158*717646f7SJordan Paige Hendricks
159*717646f7SJordan Paige Hendricks do_cpuid(0, regs);
160*717646f7SJordan Paige Hendricks ((uint_t *)&cpu_vendor)[0] = regs[1];
161*717646f7SJordan Paige Hendricks ((uint_t *)&cpu_vendor)[1] = regs[3];
162*717646f7SJordan Paige Hendricks ((uint_t *)&cpu_vendor)[2] = regs[2];
163*717646f7SJordan Paige Hendricks cpu_vendor[12] = '\0';
164*717646f7SJordan Paige Hendricks
165*717646f7SJordan Paige Hendricks return (strcmp(cpu_vendor, "AuthenticAMD") == 0 ||
166*717646f7SJordan Paige Hendricks strcmp(cpu_vendor, "HygonGenuine") == 0);
167*717646f7SJordan Paige Hendricks }
168