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 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