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
check_paused(struct vmctx * ctx)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
main(int argc,char * argv[])59 main(int argc, char *argv[])
60 {
61 const char *suite_name = basename(argv[0]);
62 struct vmctx *ctx;
63 struct vcpu *vcpu;
64
65 ctx = create_test_vm(suite_name);
66 if (ctx == NULL) {
67 errx(EXIT_FAILURE, "could not open test VM");
68 }
69
70 if ((vcpu = vm_vcpu_open(ctx, 0)) == NULL) {
71 err(EXIT_FAILURE, "Could not open vcpu0");
72 }
73
74 if (vm_activate_cpu(vcpu) != 0) {
75 err(EXIT_FAILURE, "could not activate vcpu0");
76 }
77
78 const int vmfd = vm_get_device_fd(ctx);
79 int error;
80
81 /* Instance should not be paused after initial creation */
82 if (check_paused(ctx)) {
83 errx(EXIT_FAILURE, "VM unexpectedly in paused state");
84 }
85
86 if (ioctl(vmfd, VM_PAUSE, 0) != 0) {
87 err(EXIT_FAILURE, "VM_PAUSE failed");
88 }
89
90 /* Now we should observe the instance as paused */
91 if (!check_paused(ctx)) {
92 errx(EXIT_FAILURE, "VM no in expected paused state");
93 }
94
95 /* Pausing an already-paused instanced should result in EALREADY */
96 if (ioctl(vmfd, VM_PAUSE, 0) == 0) {
97 errx(EXIT_FAILURE, "VM_PAUSE should have failed");
98 }
99 error = errno;
100 if (error != EALREADY) {
101 errx(EXIT_FAILURE, "VM_PAUSE unexpected errno: %d != %d",
102 EALREADY, error);
103 }
104
105 /* A VM_RUN attempted now should fail with EBUSY */
106 struct vm_entry ventry = { .cmd = 0, };
107 struct vm_exit vexit = { 0 };
108 if (vm_run(vcpu, &ventry, &vexit) == 0) {
109 errx(EXIT_FAILURE, "VM_RUN should have failed");
110 }
111 error = errno;
112 if (error != EBUSY) {
113 errx(EXIT_FAILURE, "VM_RUN unexpected errno: %d != %d",
114 EBUSY, error);
115 }
116
117 if (ioctl(vmfd, VM_RESUME, 0) != 0) {
118 err(EXIT_FAILURE, "VM_RESUME failed");
119 }
120
121 /* Now we should observe the instance as no longer paused */
122 if (check_paused(ctx)) {
123 errx(EXIT_FAILURE, "VM unexpectedly in paused state");
124 }
125
126 /* Resuming an already-running instanced should result in EALREADY */
127 if (ioctl(vmfd, VM_RESUME, 0) == 0) {
128 errx(EXIT_FAILURE, "VM_RESUME should have failed");
129 }
130 error = errno;
131 if (error != EALREADY) {
132 errx(EXIT_FAILURE, "VM_RESUME unexpected errno: %d != %d",
133 EALREADY, error);
134 }
135
136 vm_vcpu_close(vcpu);
137 vm_destroy(ctx);
138 (void) printf("%s\tPASS\n", suite_name);
139 return (EXIT_SUCCESS);
140 }
141