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