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 <fcntl.h>
20 #include <libgen.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <assert.h>
24 
25 #include <sys/vmm.h>
26 #include <sys/vmm_dev.h>
27 #include <sys/vmm_drv_test.h>
28 #include <vmmapi.h>
29 
30 #include "common.h"
31 
32 bool
33 test_for_instance(const char *suite_name)
34 {
35 	char vm_name[VM_MAX_NAMELEN];
36 	char vm_path[MAXPATHLEN];
37 
38 	name_test_vm(suite_name, vm_name);
39 	(void) snprintf(vm_path, sizeof (vm_path), "/dev/vmm/%s", vm_name);
40 
41 	struct stat buf;
42 	return (stat(vm_path, &buf) == 0);
43 }
44 
45 int
46 destroy_instance(const char *suite_name)
47 {
48 	int ctl_fd = open(VMM_CTL_DEV, O_EXCL | O_RDWR);
49 	if (ctl_fd < 0) {
50 		return (-1);
51 	}
52 
53 	struct vm_destroy_req req;
54 	name_test_vm(suite_name, req.name);
55 
56 	if (ioctl(ctl_fd, VMM_DESTROY_VM, &req) != 0) {
57 		/* Preserve the destroy error across the close() */
58 		int err = errno;
59 		(void) close(ctl_fd);
60 		errno = err;
61 		return (-1);
62 	} else {
63 		(void) close(ctl_fd);
64 		return (0);
65 	}
66 }
67 
68 int
69 main(int argc, char *argv[])
70 {
71 	const char *suite_name = basename(argv[0]);
72 	struct vmctx *ctx;
73 
74 	ctx = create_test_vm(suite_name);
75 	if (ctx == NULL) {
76 		perror("could open test VM");
77 		return (EXIT_FAILURE);
78 	}
79 
80 	/*
81 	 * It would be odd if we had the freshly created VM instance, but it did
82 	 * not appear to exist.
83 	 */
84 	assert(test_for_instance(suite_name));
85 
86 	/* Make sure that auto-destruct is off */
87 	if (ioctl(vm_get_device_fd(ctx), VM_SET_AUTODESTRUCT, 0) != 0) {
88 		perror("could not disable auto-destruct");
89 		return (EXIT_FAILURE);
90 	}
91 
92 	vm_close(ctx);
93 	if (!test_for_instance(suite_name)) {
94 		perror("instance missing after close");
95 		return (EXIT_FAILURE);
96 	}
97 	ctx = NULL;
98 
99 	if (destroy_instance(suite_name) != 0) {
100 		perror("could not clean up instance");
101 		return (EXIT_FAILURE);
102 	}
103 
104 	/* Now repeat that process, but enable auto-destruct */
105 	ctx = create_test_vm(suite_name);
106 	if (ctx == NULL) {
107 		perror("could open test VM");
108 		return (EXIT_FAILURE);
109 	}
110 	if (ioctl(vm_get_device_fd(ctx), VM_SET_AUTODESTRUCT, 1) != 0) {
111 		perror("could not enable auto-destruct");
112 		return (EXIT_FAILURE);
113 	}
114 	vm_close(ctx);
115 	ctx = NULL;
116 	/* At this point, the instance should be gone */
117 	if (test_for_instance(suite_name)) {
118 		(void) fprintf(stderr,
119 		    "instance did not auto-destruct as expected");
120 		return (EXIT_FAILURE);
121 	}
122 
123 	/*
124 	 * Repeat the test again, but establish a vmm_drv hold first.
125 	 * The instance should auto-destruct when the hold is released.
126 	 */
127 	ctx = create_test_vm(suite_name);
128 	if (ctx == NULL) {
129 		perror("could open test VM");
130 		return (EXIT_FAILURE);
131 	}
132 	if (ioctl(vm_get_device_fd(ctx), VM_SET_AUTODESTRUCT, 1) != 0) {
133 		perror("could not enable auto-destruct");
134 		return (EXIT_FAILURE);
135 	}
136 	int vdtfd = open_drv_test();
137 	if (vdtfd < 0) {
138 		perror("could open drv_test device");
139 		return (EXIT_FAILURE);
140 	}
141 	if (ioctl(vdtfd, VDT_IOC_HOLD, vm_get_device_fd(ctx)) != 0) {
142 		perror("could not hold VM from vmm_drv device");
143 		return (EXIT_FAILURE);
144 	}
145 	vm_close(ctx);
146 	ctx = NULL;
147 	if (!test_for_instance(suite_name)) {
148 		(void) fprintf(stderr,
149 		    "instance auto-destructed despite existing vmm_drv hold");
150 		return (EXIT_FAILURE);
151 	}
152 	if (ioctl(vdtfd, VDT_IOC_RELE, 0) != 0) {
153 		perror("could not release VM from vmm_drv device");
154 		return (EXIT_FAILURE);
155 	}
156 	if (test_for_instance(suite_name)) {
157 		(void) fprintf(stderr,
158 		    "instance did not auto-destructed after vmm_drv release");
159 		return (EXIT_FAILURE);
160 	}
161 
162 	(void) printf("%s\tPASS\n", suite_name);
163 	return (EXIT_SUCCESS);
164 }
165