1*b9b43e84SPatrick Mooney /*
2*b9b43e84SPatrick Mooney * This file and its contents are supplied under the terms of the
3*b9b43e84SPatrick Mooney * Common Development and Distribution License ("CDDL"), version 1.0.
4*b9b43e84SPatrick Mooney * You may only use this file in accordance with the terms of version
5*b9b43e84SPatrick Mooney * 1.0 of the CDDL.
6*b9b43e84SPatrick Mooney *
7*b9b43e84SPatrick Mooney * A full copy of the text of the CDDL should have accompanied this
8*b9b43e84SPatrick Mooney * source. A copy of the CDDL is also available via the Internet at
9*b9b43e84SPatrick Mooney * http://www.illumos.org/license/CDDL.
10*b9b43e84SPatrick Mooney */
11*b9b43e84SPatrick Mooney
12*b9b43e84SPatrick Mooney /*
13*b9b43e84SPatrick Mooney * Copyright 2024 Oxide Computer Company
14*b9b43e84SPatrick Mooney */
15*b9b43e84SPatrick Mooney
16*b9b43e84SPatrick Mooney #include <stdio.h>
17*b9b43e84SPatrick Mooney #include <unistd.h>
18*b9b43e84SPatrick Mooney #include <stdlib.h>
19*b9b43e84SPatrick Mooney #include <fcntl.h>
20*b9b43e84SPatrick Mooney #include <libgen.h>
21*b9b43e84SPatrick Mooney #include <sys/stat.h>
22*b9b43e84SPatrick Mooney #include <errno.h>
23*b9b43e84SPatrick Mooney #include <err.h>
24*b9b43e84SPatrick Mooney #include <assert.h>
25*b9b43e84SPatrick Mooney #include <sys/sysmacros.h>
26*b9b43e84SPatrick Mooney #include <stdbool.h>
27*b9b43e84SPatrick Mooney
28*b9b43e84SPatrick Mooney #include <sys/vmm.h>
29*b9b43e84SPatrick Mooney #include <sys/vmm_dev.h>
30*b9b43e84SPatrick Mooney #include <sys/vmm_data.h>
31*b9b43e84SPatrick Mooney #include <vmmapi.h>
32*b9b43e84SPatrick Mooney
33*b9b43e84SPatrick Mooney #include "common.h"
34*b9b43e84SPatrick Mooney
35*b9b43e84SPatrick Mooney #define PAGESZ 4096
36*b9b43e84SPatrick Mooney #define TEST_PAGE_COUNT 256
37*b9b43e84SPatrick Mooney #define TEST_MEM_SZ (PAGESZ * 256)
38*b9b43e84SPatrick Mooney
39*b9b43e84SPatrick Mooney static struct vmctx *
check_vmm_capability(const char * tname)40*b9b43e84SPatrick Mooney check_vmm_capability(const char *tname)
41*b9b43e84SPatrick Mooney {
42*b9b43e84SPatrick Mooney char vmname[VM_MAX_NAMELEN];
43*b9b43e84SPatrick Mooney
44*b9b43e84SPatrick Mooney name_test_vm(tname, vmname);
45*b9b43e84SPatrick Mooney int res = vm_create(vmname, VCF_TRACK_DIRTY);
46*b9b43e84SPatrick Mooney
47*b9b43e84SPatrick Mooney if (res != 0) {
48*b9b43e84SPatrick Mooney if (errno == ENOTSUP) {
49*b9b43e84SPatrick Mooney (void) fprintf(stderr,
50*b9b43e84SPatrick Mooney "VMM lacks dirty page tracking capability");
51*b9b43e84SPatrick Mooney (void) printf("%s\tSKIP\n", tname);
52*b9b43e84SPatrick Mooney exit(EXIT_SUCCESS);
53*b9b43e84SPatrick Mooney }
54*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "could not create VM");
55*b9b43e84SPatrick Mooney }
56*b9b43e84SPatrick Mooney struct vmctx *ctx = vm_open(vmname);
57*b9b43e84SPatrick Mooney if (ctx == NULL) {
58*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "could not open test VM");
59*b9b43e84SPatrick Mooney }
60*b9b43e84SPatrick Mooney
61*b9b43e84SPatrick Mooney return (ctx);
62*b9b43e84SPatrick Mooney }
63*b9b43e84SPatrick Mooney
64*b9b43e84SPatrick Mooney static void
expect_errno(int expected)65*b9b43e84SPatrick Mooney expect_errno(int expected)
66*b9b43e84SPatrick Mooney {
67*b9b43e84SPatrick Mooney if (errno != expected) {
68*b9b43e84SPatrick Mooney errx(EXIT_FAILURE, "unexpected errno %d != %d",
69*b9b43e84SPatrick Mooney errno, expected);
70*b9b43e84SPatrick Mooney }
71*b9b43e84SPatrick Mooney }
72*b9b43e84SPatrick Mooney
73*b9b43e84SPatrick Mooney static uint8_t
popc8(uint8_t val)74*b9b43e84SPatrick Mooney popc8(uint8_t val)
75*b9b43e84SPatrick Mooney {
76*b9b43e84SPatrick Mooney uint8_t cnt;
77*b9b43e84SPatrick Mooney
78*b9b43e84SPatrick Mooney for (cnt = 0; val != 0; val &= (val - 1)) {
79*b9b43e84SPatrick Mooney cnt++;
80*b9b43e84SPatrick Mooney }
81*b9b43e84SPatrick Mooney return (cnt);
82*b9b43e84SPatrick Mooney }
83*b9b43e84SPatrick Mooney
84*b9b43e84SPatrick Mooney static uint_t
legacy_clear_dirty(struct vmctx * ctx)85*b9b43e84SPatrick Mooney legacy_clear_dirty(struct vmctx *ctx)
86*b9b43e84SPatrick Mooney {
87*b9b43e84SPatrick Mooney uint8_t bitmap[TEST_PAGE_COUNT / 8] = { 0 };
88*b9b43e84SPatrick Mooney struct vmm_dirty_tracker req = {
89*b9b43e84SPatrick Mooney .vdt_start_gpa = 0,
90*b9b43e84SPatrick Mooney .vdt_len = TEST_MEM_SZ,
91*b9b43e84SPatrick Mooney .vdt_pfns = bitmap,
92*b9b43e84SPatrick Mooney };
93*b9b43e84SPatrick Mooney
94*b9b43e84SPatrick Mooney if (ioctl(vm_get_device_fd(ctx), VM_TRACK_DIRTY_PAGES, &req) != 0) {
95*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "VM_TRACK_DIRTY_PAGES failed");
96*b9b43e84SPatrick Mooney }
97*b9b43e84SPatrick Mooney
98*b9b43e84SPatrick Mooney uint_t bits_set = 0;
99*b9b43e84SPatrick Mooney for (uint_t i = 0; i < (TEST_PAGE_COUNT / 8); i++) {
100*b9b43e84SPatrick Mooney bits_set += popc8(bitmap[i]);
101*b9b43e84SPatrick Mooney }
102*b9b43e84SPatrick Mooney return (bits_set);
103*b9b43e84SPatrick Mooney }
104*b9b43e84SPatrick Mooney
105*b9b43e84SPatrick Mooney static void
do_npt_op(int vmfd,struct vm_npt_operation * vno)106*b9b43e84SPatrick Mooney do_npt_op(int vmfd, struct vm_npt_operation *vno)
107*b9b43e84SPatrick Mooney {
108*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, vno) != 0) {
109*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "VM_NPT_OPERATION failed");
110*b9b43e84SPatrick Mooney }
111*b9b43e84SPatrick Mooney }
112*b9b43e84SPatrick Mooney
113*b9b43e84SPatrick Mooney static void
test_legacy(struct vmctx * ctx)114*b9b43e84SPatrick Mooney test_legacy(struct vmctx *ctx)
115*b9b43e84SPatrick Mooney {
116*b9b43e84SPatrick Mooney const int vmfd = vm_get_device_fd(ctx);
117*b9b43e84SPatrick Mooney uint8_t *datap = vm_map_gpa(ctx, 0, PAGESZ);
118*b9b43e84SPatrick Mooney
119*b9b43e84SPatrick Mooney /* dirty the first page */
120*b9b43e84SPatrick Mooney *datap = 0xff;
121*b9b43e84SPatrick Mooney
122*b9b43e84SPatrick Mooney uint8_t bitmap[TEST_PAGE_COUNT / 8] = { 0 };
123*b9b43e84SPatrick Mooney struct vmm_dirty_tracker req = {
124*b9b43e84SPatrick Mooney .vdt_start_gpa = 0,
125*b9b43e84SPatrick Mooney .vdt_len = TEST_MEM_SZ,
126*b9b43e84SPatrick Mooney .vdt_pfns = bitmap,
127*b9b43e84SPatrick Mooney };
128*b9b43e84SPatrick Mooney
129*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_TRACK_DIRTY_PAGES, &req) != 0) {
130*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "VM_TRACK_DIRTY_PAGES failed");
131*b9b43e84SPatrick Mooney }
132*b9b43e84SPatrick Mooney
133*b9b43e84SPatrick Mooney if (bitmap[0] != 1) {
134*b9b43e84SPatrick Mooney errx(EXIT_FAILURE, "first page not marked dirty");
135*b9b43e84SPatrick Mooney }
136*b9b43e84SPatrick Mooney for (uint_t i = 1; i < (TEST_PAGE_COUNT / 8); i++) {
137*b9b43e84SPatrick Mooney if (bitmap[i] != 0) {
138*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
139*b9b43e84SPatrick Mooney "unexpected non-zero entry: bitmap[%u] = %x\n",
140*b9b43e84SPatrick Mooney i, bitmap[i]);
141*b9b43e84SPatrick Mooney }
142*b9b43e84SPatrick Mooney }
143*b9b43e84SPatrick Mooney }
144*b9b43e84SPatrick Mooney
145*b9b43e84SPatrick Mooney static void
test_toggle_tracking(struct vmctx * ctx)146*b9b43e84SPatrick Mooney test_toggle_tracking(struct vmctx *ctx)
147*b9b43e84SPatrick Mooney {
148*b9b43e84SPatrick Mooney const int vmfd = vm_get_device_fd(ctx);
149*b9b43e84SPatrick Mooney struct vm_npt_operation vno = {
150*b9b43e84SPatrick Mooney .vno_operation = VNO_OP_GET_TRACK_DIRTY,
151*b9b43e84SPatrick Mooney .vno_gpa = 0,
152*b9b43e84SPatrick Mooney .vno_len = 0,
153*b9b43e84SPatrick Mooney };
154*b9b43e84SPatrick Mooney
155*b9b43e84SPatrick Mooney /*
156*b9b43e84SPatrick Mooney * Since the VM was created with VCF_TRACK_DIRTY set, dirty tracking
157*b9b43e84SPatrick Mooney * should already be active.
158*b9b43e84SPatrick Mooney */
159*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) != 1) {
160*b9b43e84SPatrick Mooney errx(EXIT_FAILURE, "expected dirty tracking to be active");
161*b9b43e84SPatrick Mooney }
162*b9b43e84SPatrick Mooney
163*b9b43e84SPatrick Mooney vno.vno_operation = VNO_OP_DIS_TRACK_DIRTY;
164*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) != 0) {
165*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "VM_NPT_OPERATION failed");
166*b9b43e84SPatrick Mooney }
167*b9b43e84SPatrick Mooney
168*b9b43e84SPatrick Mooney vno.vno_operation = VNO_OP_GET_TRACK_DIRTY;
169*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) != 0) {
170*b9b43e84SPatrick Mooney errx(EXIT_FAILURE, "expected dirty tracking to be inactive");
171*b9b43e84SPatrick Mooney }
172*b9b43e84SPatrick Mooney
173*b9b43e84SPatrick Mooney vno.vno_operation = VNO_OP_EN_TRACK_DIRTY;
174*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) != 0) {
175*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "VM_NPT_OPERATION failed");
176*b9b43e84SPatrick Mooney }
177*b9b43e84SPatrick Mooney
178*b9b43e84SPatrick Mooney vno.vno_operation = VNO_OP_GET_TRACK_DIRTY;
179*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) != 1) {
180*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
181*b9b43e84SPatrick Mooney "expected dirty tracking to be active again");
182*b9b43e84SPatrick Mooney }
183*b9b43e84SPatrick Mooney }
184*b9b43e84SPatrick Mooney
185*b9b43e84SPatrick Mooney static void
test_inval_args(struct vmctx * ctx)186*b9b43e84SPatrick Mooney test_inval_args(struct vmctx *ctx)
187*b9b43e84SPatrick Mooney {
188*b9b43e84SPatrick Mooney const int vmfd = vm_get_device_fd(ctx);
189*b9b43e84SPatrick Mooney struct vm_npt_operation vno = { 0 };
190*b9b43e84SPatrick Mooney
191*b9b43e84SPatrick Mooney /* invalid vno_operation */
192*b9b43e84SPatrick Mooney vno.vno_operation = ~0;
193*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) == 0) {
194*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "unexpected VM_NPT_OPERATION success");
195*b9b43e84SPatrick Mooney }
196*b9b43e84SPatrick Mooney expect_errno(EINVAL);
197*b9b43e84SPatrick Mooney
198*b9b43e84SPatrick Mooney /* valid operation, but gpa which is not page-aligned */
199*b9b43e84SPatrick Mooney vno.vno_operation = VNO_OP_GET_DIRTY | VNO_FLAG_BITMAP_IN;
200*b9b43e84SPatrick Mooney vno.vno_gpa = 0x100;
201*b9b43e84SPatrick Mooney vno.vno_len = PAGESZ;
202*b9b43e84SPatrick Mooney
203*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) == 0) {
204*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "unexpected VM_NPT_OPERATION success");
205*b9b43e84SPatrick Mooney }
206*b9b43e84SPatrick Mooney expect_errno(EINVAL);
207*b9b43e84SPatrick Mooney
208*b9b43e84SPatrick Mooney /* gpa is page-aligned, but len isn't */
209*b9b43e84SPatrick Mooney vno.vno_gpa = 0;
210*b9b43e84SPatrick Mooney vno.vno_len = PAGESZ + 0x100;
211*b9b43e84SPatrick Mooney
212*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) == 0) {
213*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "unexpected VM_NPT_OPERATION success");
214*b9b43e84SPatrick Mooney }
215*b9b43e84SPatrick Mooney expect_errno(EINVAL);
216*b9b43e84SPatrick Mooney
217*b9b43e84SPatrick Mooney /* overflowing region */
218*b9b43e84SPatrick Mooney vno.vno_gpa = 0xffffffffffffe000;
219*b9b43e84SPatrick Mooney vno.vno_len = 512 * PAGESZ;
220*b9b43e84SPatrick Mooney
221*b9b43e84SPatrick Mooney if (ioctl(vmfd, VM_NPT_OPERATION, &vno) == 0) {
222*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "unexpected VM_NPT_OPERATION success");
223*b9b43e84SPatrick Mooney }
224*b9b43e84SPatrick Mooney expect_errno(EOVERFLOW);
225*b9b43e84SPatrick Mooney }
226*b9b43e84SPatrick Mooney
227*b9b43e84SPatrick Mooney static void
test_op_get_dirty(struct vmctx * ctx)228*b9b43e84SPatrick Mooney test_op_get_dirty(struct vmctx *ctx)
229*b9b43e84SPatrick Mooney {
230*b9b43e84SPatrick Mooney const int vmfd = vm_get_device_fd(ctx);
231*b9b43e84SPatrick Mooney uint8_t *datap = vm_map_gpa(ctx, 0, TEST_MEM_SZ);
232*b9b43e84SPatrick Mooney
233*b9b43e84SPatrick Mooney /* Use legacy mechanism to ensure dirty bits are clear to start */
234*b9b43e84SPatrick Mooney (void) legacy_clear_dirty(ctx);
235*b9b43e84SPatrick Mooney
236*b9b43e84SPatrick Mooney /* Dirty the first page out of every 8 */
237*b9b43e84SPatrick Mooney for (uint_t i = 0; i < TEST_MEM_SZ; i += (PAGESZ * 8)) {
238*b9b43e84SPatrick Mooney datap[i] = 0xff;
239*b9b43e84SPatrick Mooney }
240*b9b43e84SPatrick Mooney
241*b9b43e84SPatrick Mooney uint8_t bits[TEST_PAGE_COUNT / 8] = { 0 };
242*b9b43e84SPatrick Mooney struct vm_npt_operation vno = {
243*b9b43e84SPatrick Mooney .vno_gpa = 0,
244*b9b43e84SPatrick Mooney .vno_len = TEST_MEM_SZ,
245*b9b43e84SPatrick Mooney .vno_operation = VNO_OP_GET_DIRTY | VNO_FLAG_BITMAP_OUT,
246*b9b43e84SPatrick Mooney .vno_bitmap = bits,
247*b9b43e84SPatrick Mooney };
248*b9b43e84SPatrick Mooney do_npt_op(vmfd, &vno);
249*b9b43e84SPatrick Mooney
250*b9b43e84SPatrick Mooney for (uint_t i = 0; i < TEST_PAGE_COUNT / 8; i++) {
251*b9b43e84SPatrick Mooney if (bits[i] != 0x01) {
252*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
253*b9b43e84SPatrick Mooney "unexpected dirty bits %02x at base gpa %08x",
254*b9b43e84SPatrick Mooney bits[i], i * PAGESZ * 8);
255*b9b43e84SPatrick Mooney }
256*b9b43e84SPatrick Mooney }
257*b9b43e84SPatrick Mooney
258*b9b43e84SPatrick Mooney /* Clear those bits again */
259*b9b43e84SPatrick Mooney (void) legacy_clear_dirty(ctx);
260*b9b43e84SPatrick Mooney
261*b9b43e84SPatrick Mooney /* And check that they are zeroed now */
262*b9b43e84SPatrick Mooney do_npt_op(vmfd, &vno);
263*b9b43e84SPatrick Mooney for (uint_t i = 0; i < TEST_PAGE_COUNT / 8; i++) {
264*b9b43e84SPatrick Mooney if (bits[i] != 0) {
265*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
266*b9b43e84SPatrick Mooney "unexpected dirty bits %02x at base gpa %08x",
267*b9b43e84SPatrick Mooney bits[i], i * PAGESZ * 8);
268*b9b43e84SPatrick Mooney }
269*b9b43e84SPatrick Mooney }
270*b9b43e84SPatrick Mooney }
271*b9b43e84SPatrick Mooney
272*b9b43e84SPatrick Mooney static void
test_op_set_dirty(struct vmctx * ctx)273*b9b43e84SPatrick Mooney test_op_set_dirty(struct vmctx *ctx)
274*b9b43e84SPatrick Mooney {
275*b9b43e84SPatrick Mooney const int vmfd = vm_get_device_fd(ctx);
276*b9b43e84SPatrick Mooney
277*b9b43e84SPatrick Mooney /* Use legacy mechanism to ensure dirty bits are clear to start */
278*b9b43e84SPatrick Mooney (void) legacy_clear_dirty(ctx);
279*b9b43e84SPatrick Mooney
280*b9b43e84SPatrick Mooney /* Mark first 17 pages as dirty */
281*b9b43e84SPatrick Mooney uint8_t bits[TEST_PAGE_COUNT / 8] = { 0xff, 0xff, 0x80 };
282*b9b43e84SPatrick Mooney struct vm_npt_operation vno = {
283*b9b43e84SPatrick Mooney .vno_gpa = 0,
284*b9b43e84SPatrick Mooney .vno_len = TEST_MEM_SZ,
285*b9b43e84SPatrick Mooney .vno_operation = VNO_OP_SET_DIRTY | VNO_FLAG_BITMAP_IN,
286*b9b43e84SPatrick Mooney .vno_bitmap = bits,
287*b9b43e84SPatrick Mooney };
288*b9b43e84SPatrick Mooney do_npt_op(vmfd, &vno);
289*b9b43e84SPatrick Mooney
290*b9b43e84SPatrick Mooney uint_t legacy_dirty = legacy_clear_dirty(ctx);
291*b9b43e84SPatrick Mooney if (legacy_dirty != 17) {
292*b9b43e84SPatrick Mooney errx(EXIT_FAILURE, "unexpected dirty count after OP_SET_DIRTY");
293*b9b43e84SPatrick Mooney }
294*b9b43e84SPatrick Mooney }
295*b9b43e84SPatrick Mooney
296*b9b43e84SPatrick Mooney #define BMAP_IDX(gpa) ((gpa) / (PAGESZ * 8))
297*b9b43e84SPatrick Mooney #define BMAP_BIT(gpa) (((gpa) / PAGESZ) % 8)
298*b9b43e84SPatrick Mooney
299*b9b43e84SPatrick Mooney static void
test_op_reset_dirty(struct vmctx * ctx)300*b9b43e84SPatrick Mooney test_op_reset_dirty(struct vmctx *ctx)
301*b9b43e84SPatrick Mooney {
302*b9b43e84SPatrick Mooney const int vmfd = vm_get_device_fd(ctx);
303*b9b43e84SPatrick Mooney uint8_t *datap = vm_map_gpa(ctx, 0, TEST_MEM_SZ);
304*b9b43e84SPatrick Mooney
305*b9b43e84SPatrick Mooney /* Use legacy mechanism to ensure dirty bits are clear to start */
306*b9b43e84SPatrick Mooney (void) legacy_clear_dirty(ctx);
307*b9b43e84SPatrick Mooney
308*b9b43e84SPatrick Mooney /* Dirty the front half of memory */
309*b9b43e84SPatrick Mooney for (uintptr_t gpa = 0; gpa < (TEST_MEM_SZ / 2); gpa += PAGESZ) {
310*b9b43e84SPatrick Mooney datap[gpa] = 0xff;
311*b9b43e84SPatrick Mooney }
312*b9b43e84SPatrick Mooney
313*b9b43e84SPatrick Mooney uint8_t bits[TEST_PAGE_COUNT / 8] = { 0 };
314*b9b43e84SPatrick Mooney /* Mark bitmap for every other page, starting at 0 */
315*b9b43e84SPatrick Mooney for (uintptr_t gpa = 0; gpa < TEST_MEM_SZ; gpa += (2 * PAGESZ)) {
316*b9b43e84SPatrick Mooney bits[BMAP_IDX(gpa)] |= (1 << BMAP_BIT(gpa));
317*b9b43e84SPatrick Mooney }
318*b9b43e84SPatrick Mooney
319*b9b43e84SPatrick Mooney struct vm_npt_operation vno = {
320*b9b43e84SPatrick Mooney .vno_gpa = 0,
321*b9b43e84SPatrick Mooney .vno_len = TEST_MEM_SZ,
322*b9b43e84SPatrick Mooney .vno_operation = VNO_OP_RESET_DIRTY |
323*b9b43e84SPatrick Mooney VNO_FLAG_BITMAP_IN | VNO_FLAG_BITMAP_OUT,
324*b9b43e84SPatrick Mooney .vno_bitmap = bits,
325*b9b43e84SPatrick Mooney };
326*b9b43e84SPatrick Mooney do_npt_op(vmfd, &vno);
327*b9b43e84SPatrick Mooney
328*b9b43e84SPatrick Mooney /* Check that pages marked dirty were reported back as such */
329*b9b43e84SPatrick Mooney for (uintptr_t gpa = 0; gpa < TEST_MEM_SZ; gpa += PAGESZ) {
330*b9b43e84SPatrick Mooney const bool is_even_page = (BMAP_BIT(gpa) % 2) == 0;
331*b9b43e84SPatrick Mooney const bool is_dirty =
332*b9b43e84SPatrick Mooney (bits[BMAP_IDX(gpa)] & (1 << BMAP_BIT(gpa))) != 0;
333*b9b43e84SPatrick Mooney
334*b9b43e84SPatrick Mooney /* Even pages in the first half should be set */
335*b9b43e84SPatrick Mooney if (is_even_page && gpa < (TEST_MEM_SZ / 2) && !is_dirty) {
336*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
337*b9b43e84SPatrick Mooney "missing dirty bit set at gpa %08lx", gpa);
338*b9b43e84SPatrick Mooney }
339*b9b43e84SPatrick Mooney
340*b9b43e84SPatrick Mooney /* Odd pages and even pages in second half should be unset */
341*b9b43e84SPatrick Mooney if (is_dirty && (!is_even_page || gpa >= (TEST_MEM_SZ / 2))) {
342*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
343*b9b43e84SPatrick Mooney "unexpected dirty bit set at gpa %08lx", gpa);
344*b9b43e84SPatrick Mooney }
345*b9b43e84SPatrick Mooney }
346*b9b43e84SPatrick Mooney
347*b9b43e84SPatrick Mooney /*
348*b9b43e84SPatrick Mooney * With half of the pages dirtied at first, and then half of those reset
349*b9b43e84SPatrick Mooney * from dirty in the NPT operation, we expect 1/4 to be remaining.
350*b9b43e84SPatrick Mooney */
351*b9b43e84SPatrick Mooney uint_t remaining_dirty = legacy_clear_dirty(ctx);
352*b9b43e84SPatrick Mooney if (remaining_dirty != (TEST_PAGE_COUNT / 4)) {
353*b9b43e84SPatrick Mooney errx(EXIT_FAILURE,
354*b9b43e84SPatrick Mooney "expected %u pages remaining dirty, found %u",
355*b9b43e84SPatrick Mooney TEST_PAGE_COUNT / 2, remaining_dirty);
356*b9b43e84SPatrick Mooney }
357*b9b43e84SPatrick Mooney }
358*b9b43e84SPatrick Mooney
359*b9b43e84SPatrick Mooney int
main(int argc,char * argv[])360*b9b43e84SPatrick Mooney main(int argc, char *argv[])
361*b9b43e84SPatrick Mooney {
362*b9b43e84SPatrick Mooney const char *suite_name = basename(argv[0]);
363*b9b43e84SPatrick Mooney struct vmctx *ctx;
364*b9b43e84SPatrick Mooney
365*b9b43e84SPatrick Mooney ctx = check_vmm_capability(suite_name);
366*b9b43e84SPatrick Mooney
367*b9b43e84SPatrick Mooney if (vm_setup_memory(ctx, TEST_MEM_SZ, VM_MMAP_ALL) != 0) {
368*b9b43e84SPatrick Mooney err(EXIT_FAILURE, "could not setup VM memory");
369*b9b43e84SPatrick Mooney }
370*b9b43e84SPatrick Mooney
371*b9b43e84SPatrick Mooney /* Test "legacy" VM_TRACK_DIRTY_PAGES mechanism first */
372*b9b43e84SPatrick Mooney test_legacy(ctx);
373*b9b43e84SPatrick Mooney
374*b9b43e84SPatrick Mooney /* Confirm that dirty tracking can be queried and toggled on/off */
375*b9b43e84SPatrick Mooney test_toggle_tracking(ctx);
376*b9b43e84SPatrick Mooney
377*b9b43e84SPatrick Mooney /* Check some invalid argument conditions */
378*b9b43e84SPatrick Mooney test_inval_args(ctx);
379*b9b43e84SPatrick Mooney
380*b9b43e84SPatrick Mooney /* Can dirty bits be queried with VNO_OP_GET_DIRTY */
381*b9b43e84SPatrick Mooney test_op_get_dirty(ctx);
382*b9b43e84SPatrick Mooney
383*b9b43e84SPatrick Mooney /* Can dirty bits be set with VNO_OP_SET_DIRTY */
384*b9b43e84SPatrick Mooney test_op_set_dirty(ctx);
385*b9b43e84SPatrick Mooney
386*b9b43e84SPatrick Mooney /*
387*b9b43e84SPatrick Mooney * Can dirty bits be reset (simultaneously queried and cleared )
388*b9b43e84SPatrick Mooney * with VNO_OP_RESET_DIRTY
389*b9b43e84SPatrick Mooney */
390*b9b43e84SPatrick Mooney test_op_reset_dirty(ctx);
391*b9b43e84SPatrick Mooney
392*b9b43e84SPatrick Mooney vm_destroy(ctx);
393*b9b43e84SPatrick Mooney (void) printf("%s\tPASS\n", suite_name);
394*b9b43e84SPatrick Mooney return (EXIT_SUCCESS);
395*b9b43e84SPatrick Mooney }
396