xref: /illumos-gate/usr/src/test/bhyve-tests/tests/vmm/pause_resume.c (revision c093b3ec6d35e1fe023174ed7f6ca6b90690d526)
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 	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