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