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 <stdlib.h>
19 #include <fcntl.h>
20 #include <libgen.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <err.h>
24 #include <assert.h>
25 #include <sys/sysmacros.h>
26 #include <stdbool.h>
27 
28 #include <sys/vmm.h>
29 #include <sys/vmm_dev.h>
30 #include <sys/vmm_data.h>
31 #include <vmmapi.h>
32 
33 #include "common.h"
34 
35 bool
36 check_paused(struct vmctx *ctx)
37 {
38 	struct vdi_field_entry_v1 entry = {
39 		.vfe_ident = VAI_VM_IS_PAUSED,
40 	};
41 	struct vm_data_xfer xfer = {
42 		.vdx_vcpuid = -1,
43 		.vdx_class = VDC_VMM_ARCH,
44 		.vdx_version = 1,
45 		.vdx_len = sizeof (entry),
46 		.vdx_data = &entry,
47 		.vdx_flags = VDX_FLAG_READ_COPYIN,
48 	};
49 
50 	const int vmfd = vm_get_device_fd(ctx);
51 	if (ioctl(vmfd, VM_DATA_READ, &xfer) != 0) {
52 		err(EXIT_FAILURE, "error reading pause state");
53 	}
54 
55 	return (entry.vfe_value != 0);
56 }
57 
58 int
59 main(int argc, char *argv[])
60 {
61 	const char *suite_name = basename(argv[0]);
62 	struct vmctx *ctx;
63 
64 	ctx = create_test_vm(suite_name);
65 	if (ctx == NULL) {
66 		errx(EXIT_FAILURE, "could not open test VM");
67 	}
68 
69 	if (vm_activate_cpu(ctx, 0) != 0) {
70 		err(EXIT_FAILURE, "could not activate vcpu0");
71 	}
72 
73 	const int vmfd = vm_get_device_fd(ctx);
74 	int error;
75 
76 	/* Instance should not be paused after initial creation */
77 	if (check_paused(ctx)) {
78 		errx(EXIT_FAILURE, "VM unexpectedly in paused state");
79 	}
80 
81 	if (ioctl(vmfd, VM_PAUSE, 0) != 0) {
82 		err(EXIT_FAILURE, "VM_PAUSE failed");
83 	}
84 
85 	/* Now we should observe the instance as paused */
86 	if (!check_paused(ctx)) {
87 		errx(EXIT_FAILURE, "VM no in expected paused state");
88 	}
89 
90 	/* Pausing an already-paused instanced should result in EALREADY */
91 	if (ioctl(vmfd, VM_PAUSE, 0) == 0) {
92 		errx(EXIT_FAILURE, "VM_PAUSE should have failed");
93 	}
94 	error = errno;
95 	if (error != EALREADY) {
96 		errx(EXIT_FAILURE, "VM_PAUSE unexpected errno: %d != %d",
97 		    EALREADY, error);
98 	}
99 
100 	/* A VM_RUN attempted now should fail with EBUSY */
101 	struct vm_entry ventry = { .cmd = 0, };
102 	struct vm_exit vexit = { 0 };
103 	if (vm_run(ctx, 0, &ventry, &vexit) == 0) {
104 		errx(EXIT_FAILURE, "VM_RUN should have failed");
105 	}
106 	error = errno;
107 	if (error != EBUSY) {
108 		errx(EXIT_FAILURE, "VM_RUN unexpected errno: %d != %d",
109 		    EBUSY, error);
110 	}
111 
112 	if (ioctl(vmfd, VM_RESUME, 0) != 0) {
113 		err(EXIT_FAILURE, "VM_RESUME failed");
114 	}
115 
116 	/* Now we should observe the instance as no longer paused */
117 	if (check_paused(ctx)) {
118 		errx(EXIT_FAILURE, "VM unexpectedly in paused state");
119 	}
120 
121 	/* Resuming an already-running instanced should result in EALREADY */
122 	if (ioctl(vmfd, VM_RESUME, 0) == 0) {
123 		errx(EXIT_FAILURE, "VM_RESUME should have failed");
124 	}
125 	error = errno;
126 	if (error != EALREADY) {
127 		errx(EXIT_FAILURE, "VM_RESUME unexpected errno: %d != %d",
128 		    EALREADY, error);
129 	}
130 
131 	vm_destroy(ctx);
132 	(void) printf("%s\tPASS\n", suite_name);
133 	return (EXIT_SUCCESS);
134 }
135