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 2022 Oxide Computer Company
14  */
15 
16 
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <stropts.h>
20 #include <strings.h>
21 #include <signal.h>
22 #include <setjmp.h>
23 #include <libgen.h>
24 
25 #include <sys/vmm.h>
26 #include <sys/vmm_dev.h>
27 #include <sys/mman.h>
28 #include <vmmapi.h>
29 
30 #include "common.h"
31 
32 /* Half of a leaf page table is 256 pages */
33 #define	LOWER_SZ	(256 * 4096)
34 #define	UPPER_SZ	LOWER_SZ
35 #define	TOTAL_SZ	(LOWER_SZ + UPPER_SZ)
36 
37 #define	LOWER_OFF	0
38 #define	UPPER_OFF	LOWER_SZ
39 
40 enum test_memsegs {
41 	MSEG_LOW = 0,
42 	MSEG_HIGH = 1,
43 };
44 
45 static sigjmp_buf segv_env;
46 
47 void
sigsegv_handler(int sig)48 sigsegv_handler(int sig)
49 {
50 	siglongjmp(segv_env, 1);
51 }
52 
53 
54 int
main(int argc,char * argv[])55 main(int argc, char *argv[])
56 {
57 	struct vmctx *ctx;
58 	int res, fd;
59 	void *guest_mem;
60 	const char *suite_name = basename(argv[0]);
61 
62 	ctx = create_test_vm(suite_name);
63 	if (ctx == NULL) {
64 		perror("could open test VM");
65 		return (1);
66 	}
67 	fd = vm_get_device_fd(ctx);
68 
69 	res = alloc_memseg(ctx, MSEG_LOW, LOWER_SZ, "mseg_low");
70 	if (res != 0) {
71 		perror("could not alloc low memseg");
72 		goto bail;
73 	}
74 	res = alloc_memseg(ctx, MSEG_HIGH, UPPER_SZ, "mseg_high");
75 	if (res != 0) {
76 		perror("could not alloc high memseg");
77 		goto bail;
78 	}
79 
80 
81 	res = vm_mmap_memseg(ctx, LOWER_OFF, MSEG_LOW, 0, LOWER_SZ, PROT_ALL);
82 	if (res != 0) {
83 		perror("could not map low memseg");
84 		goto bail;
85 	}
86 	res = vm_mmap_memseg(ctx, UPPER_OFF, MSEG_HIGH, 0, UPPER_SZ, PROT_ALL);
87 	if (res != 0) {
88 		perror("could not map high memseg");
89 		goto bail;
90 	}
91 
92 	guest_mem = mmap(NULL, TOTAL_SZ, PROT_READ | PROT_WRITE, MAP_SHARED,
93 	    fd, 0);
94 	if (guest_mem == MAP_FAILED) {
95 		perror("could not mmap guest memory");
96 		goto bail;
97 	}
98 
99 	/* Fill memory with 0xff */
100 	for (uintptr_t gpa = 0; gpa < TOTAL_SZ; gpa++) {
101 		uint8_t *ptr = guest_mem + gpa;
102 		*ptr = 0xff;
103 	}
104 
105 	/* Unmap the lower memseg */
106 	res = vm_munmap_memseg(ctx, LOWER_OFF, LOWER_SZ);
107 	if (guest_mem == NULL) {
108 		perror("could not unmap lower memseg");
109 		goto bail;
110 	}
111 
112 	/* Confirm upper contents are still correct/accessible */
113 	for (uintptr_t gpa = UPPER_OFF; gpa < UPPER_OFF + UPPER_SZ; gpa++) {
114 		uint8_t *ptr = guest_mem + gpa;
115 		if (*ptr != 0xff) {
116 			(void) printf("invalid mem contents at GPA %lx: %x\n",
117 			    gpa, *ptr);
118 			goto bail;
119 		}
120 		*ptr = 0xee;
121 	}
122 
123 	/*
124 	 * Attempt to access the lower contents, which should result in an
125 	 * expected (and thus handled) SIGSEGV.
126 	 */
127 	struct sigaction sa = {
128 		.sa_handler = sigsegv_handler,
129 	};
130 	struct sigaction old_sa;
131 	res = sigaction(SIGSEGV, &sa, &old_sa);
132 	if (res != 0) {
133 		perror("could not prep signal handling for bad access");
134 		goto bail;
135 	}
136 
137 	if (sigsetjmp(segv_env, 1) == 0) {
138 		volatile uint8_t *ptr = guest_mem;
139 
140 		/*
141 		 * This access to the guest space should fail, since the memseg
142 		 * covering the lower part of the VM space has been unmapped.
143 		 */
144 		uint8_t tmp = *ptr;
145 
146 		(void) printf("access to %p (%x) should have failed\n", tmp);
147 		goto bail;
148 	}
149 
150 	/*
151 	 * Unmap and remap the space so any cached entries are dropped for the
152 	 * portion we expect is still accessible.
153 	 */
154 	res = munmap(guest_mem, TOTAL_SZ);
155 	if (res != 0) {
156 		perror("could not unmap lower memseg");
157 		goto bail;
158 	}
159 	guest_mem = mmap(NULL, TOTAL_SZ, PROT_READ | PROT_WRITE, MAP_SHARED,
160 	    fd, 0);
161 	if (guest_mem == MAP_FAILED) {
162 		perror("could not re-mmap guest memory");
163 		goto bail;
164 	}
165 
166 	/* Check the upper portion for accessibility. */
167 	if (sigsetjmp(segv_env, 1) == 0) {
168 		volatile uint8_t *ptr = guest_mem + UPPER_OFF;
169 
170 		uint8_t tmp = *ptr;
171 		if (tmp != 0xee) {
172 			(void) printf("unexpected value at %p (%x)\n", ptr,
173 			    tmp);
174 			goto bail;
175 		}
176 
177 		res = sigaction(SIGSEGV, &old_sa, NULL);
178 		if (res != 0) {
179 			perror("could not restore SIGSEGV handler");
180 			goto bail;
181 		}
182 	} else {
183 		(void) printf("unexpected fault in upper mapping\n");
184 		goto bail;
185 	}
186 
187 
188 	/* Unmap the upper memseg */
189 	res = vm_munmap_memseg(ctx, UPPER_OFF, UPPER_SZ);
190 	if (guest_mem == NULL) {
191 		perror("could not unmap upper memseg");
192 		goto bail;
193 	}
194 
195 	/* mission accomplished */
196 	(void) printf("%s\tPASS\n", suite_name);
197 	vm_destroy(ctx);
198 	return (0);
199 
200 bail:
201 	vm_destroy(ctx);
202 	return (1);
203 }
204