1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * 4 * Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> 5 * Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 6 */ 7 8 #include <stdio.h> 9 #include <sys/mman.h> 10 #include <string.h> 11 12 #include "vm_util.h" 13 #include "../kselftest.h" 14 15 /* 16 * The hint addr value is used to allocate addresses 17 * beyond the high address switch boundary. 18 */ 19 20 #define ADDR_MARK_128TB (1UL << 47) 21 #define ADDR_MARK_256TB (1UL << 48) 22 23 #define HIGH_ADDR_128TB (1UL << 48) 24 #define HIGH_ADDR_256TB (1UL << 49) 25 26 struct testcase { 27 void *addr; 28 unsigned long size; 29 unsigned long flags; 30 const char *msg; 31 unsigned int low_addr_required:1; 32 unsigned int keep_mapped:1; 33 }; 34 35 static struct testcase *testcases; 36 static struct testcase *hugetlb_testcases; 37 static int sz_testcases, sz_hugetlb_testcases; 38 static unsigned long switch_hint; 39 40 /* Initialize testcases inside a function to compute parameters at runtime */ 41 void testcases_init(void) 42 { 43 unsigned long pagesize = getpagesize(); 44 unsigned long hugepagesize = default_huge_page_size(); 45 unsigned long low_addr = (1UL << 30); 46 unsigned long addr_switch_hint = ADDR_MARK_128TB; 47 unsigned long high_addr = HIGH_ADDR_128TB; 48 49 #ifdef __aarch64__ 50 51 /* Post LPA2, the lower userspace VA on a 16K pagesize is 47 bits. */ 52 if (pagesize != (16UL << 10)) { 53 addr_switch_hint = ADDR_MARK_256TB; 54 high_addr = HIGH_ADDR_256TB; 55 } 56 #endif 57 58 struct testcase t[] = { 59 { 60 /* 61 * If stack is moved, we could possibly allocate 62 * this at the requested address. 63 */ 64 .addr = ((void *)(addr_switch_hint - pagesize)), 65 .size = pagesize, 66 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 67 .msg = "mmap(addr_switch_hint - pagesize, pagesize)", 68 .low_addr_required = 1, 69 }, 70 { 71 /* 72 * Unless MAP_FIXED is specified, allocation based on hint 73 * addr is never at requested address or above it, which is 74 * beyond high address switch boundary in this case. Instead, 75 * a suitable allocation is found in lower address space. 76 */ 77 .addr = ((void *)(addr_switch_hint - pagesize)), 78 .size = 2 * pagesize, 79 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 80 .msg = "mmap(addr_switch_hint - pagesize, (2 * pagesize))", 81 .low_addr_required = 1, 82 }, 83 { 84 /* 85 * Exact mapping at high address switch boundary, should 86 * be obtained even without MAP_FIXED as area is free. 87 */ 88 .addr = ((void *)(addr_switch_hint)), 89 .size = pagesize, 90 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 91 .msg = "mmap(addr_switch_hint, pagesize)", 92 .keep_mapped = 1, 93 }, 94 { 95 .addr = (void *)(addr_switch_hint), 96 .size = 2 * pagesize, 97 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 98 .msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)", 99 }, 100 { 101 .addr = NULL, 102 .size = 2 * pagesize, 103 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 104 .msg = "mmap(NULL)", 105 .low_addr_required = 1, 106 }, 107 { 108 .addr = (void *)low_addr, 109 .size = 2 * pagesize, 110 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 111 .msg = "mmap(low_addr)", 112 .low_addr_required = 1, 113 }, 114 { 115 .addr = (void *)high_addr, 116 .size = 2 * pagesize, 117 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 118 .msg = "mmap(high_addr)", 119 .keep_mapped = 1, 120 }, 121 { 122 .addr = (void *)high_addr, 123 .size = 2 * pagesize, 124 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 125 .msg = "mmap(high_addr) again", 126 .keep_mapped = 1, 127 }, 128 { 129 .addr = (void *)high_addr, 130 .size = 2 * pagesize, 131 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 132 .msg = "mmap(high_addr, MAP_FIXED)", 133 }, 134 { 135 .addr = (void *) -1, 136 .size = 2 * pagesize, 137 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 138 .msg = "mmap(-1)", 139 .keep_mapped = 1, 140 }, 141 { 142 .addr = (void *) -1, 143 .size = 2 * pagesize, 144 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 145 .msg = "mmap(-1) again", 146 }, 147 { 148 .addr = ((void *)(addr_switch_hint - pagesize)), 149 .size = pagesize, 150 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 151 .msg = "mmap(addr_switch_hint - pagesize, pagesize)", 152 .low_addr_required = 1, 153 }, 154 { 155 .addr = (void *)(addr_switch_hint - pagesize), 156 .size = 2 * pagesize, 157 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 158 .msg = "mmap(addr_switch_hint - pagesize, 2 * pagesize)", 159 .low_addr_required = 1, 160 .keep_mapped = 1, 161 }, 162 { 163 .addr = (void *)(addr_switch_hint - pagesize / 2), 164 .size = 2 * pagesize, 165 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 166 .msg = "mmap(addr_switch_hint - pagesize/2 , 2 * pagesize)", 167 .low_addr_required = 1, 168 .keep_mapped = 1, 169 }, 170 { 171 .addr = ((void *)(addr_switch_hint)), 172 .size = pagesize, 173 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 174 .msg = "mmap(addr_switch_hint, pagesize)", 175 }, 176 { 177 .addr = (void *)(addr_switch_hint), 178 .size = 2 * pagesize, 179 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 180 .msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)", 181 }, 182 }; 183 184 struct testcase ht[] = { 185 { 186 .addr = NULL, 187 .size = hugepagesize, 188 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 189 .msg = "mmap(NULL, MAP_HUGETLB)", 190 .low_addr_required = 1, 191 }, 192 { 193 .addr = (void *)low_addr, 194 .size = hugepagesize, 195 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 196 .msg = "mmap(low_addr, MAP_HUGETLB)", 197 .low_addr_required = 1, 198 }, 199 { 200 .addr = (void *)high_addr, 201 .size = hugepagesize, 202 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 203 .msg = "mmap(high_addr, MAP_HUGETLB)", 204 .keep_mapped = 1, 205 }, 206 { 207 .addr = (void *)high_addr, 208 .size = hugepagesize, 209 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 210 .msg = "mmap(high_addr, MAP_HUGETLB) again", 211 .keep_mapped = 1, 212 }, 213 { 214 .addr = (void *)high_addr, 215 .size = hugepagesize, 216 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 217 .msg = "mmap(high_addr, MAP_FIXED | MAP_HUGETLB)", 218 }, 219 { 220 .addr = (void *) -1, 221 .size = hugepagesize, 222 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 223 .msg = "mmap(-1, MAP_HUGETLB)", 224 .keep_mapped = 1, 225 }, 226 { 227 .addr = (void *) -1, 228 .size = hugepagesize, 229 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 230 .msg = "mmap(-1, MAP_HUGETLB) again", 231 }, 232 { 233 .addr = (void *)(addr_switch_hint - pagesize), 234 .size = 2 * hugepagesize, 235 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 236 .msg = "mmap(addr_switch_hint - pagesize, 2*hugepagesize, MAP_HUGETLB)", 237 .low_addr_required = 1, 238 .keep_mapped = 1, 239 }, 240 { 241 .addr = (void *)(addr_switch_hint), 242 .size = 2 * hugepagesize, 243 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 244 .msg = "mmap(addr_switch_hint , 2*hugepagesize, MAP_FIXED | MAP_HUGETLB)", 245 }, 246 }; 247 248 testcases = malloc(sizeof(t)); 249 hugetlb_testcases = malloc(sizeof(ht)); 250 251 /* Copy into global arrays */ 252 memcpy(testcases, t, sizeof(t)); 253 memcpy(hugetlb_testcases, ht, sizeof(ht)); 254 255 sz_testcases = ARRAY_SIZE(t); 256 sz_hugetlb_testcases = ARRAY_SIZE(ht); 257 switch_hint = addr_switch_hint; 258 } 259 260 static int run_test(struct testcase *test, int count) 261 { 262 void *p; 263 int i, ret = KSFT_PASS; 264 265 for (i = 0; i < count; i++) { 266 struct testcase *t = test + i; 267 268 p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0); 269 270 printf("%s: %p - ", t->msg, p); 271 272 if (p == MAP_FAILED) { 273 printf("FAILED\n"); 274 ret = KSFT_FAIL; 275 continue; 276 } 277 278 if (t->low_addr_required && p >= (void *)(switch_hint)) { 279 printf("FAILED\n"); 280 ret = KSFT_FAIL; 281 } else { 282 /* 283 * Do a dereference of the address returned so that we catch 284 * bugs in page fault handling 285 */ 286 memset(p, 0, t->size); 287 printf("OK\n"); 288 } 289 if (!t->keep_mapped) 290 munmap(p, t->size); 291 } 292 293 return ret; 294 } 295 296 #ifdef __aarch64__ 297 /* Check if userspace VA > 48 bits */ 298 static int high_address_present(void) 299 { 300 void *ptr = mmap((void *)(1UL << 50), 1, PROT_READ | PROT_WRITE, 301 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); 302 if (ptr == MAP_FAILED) 303 return 0; 304 305 munmap(ptr, 1); 306 return 1; 307 } 308 #endif 309 310 static int supported_arch(void) 311 { 312 #if defined(__powerpc64__) 313 return 1; 314 #elif defined(__x86_64__) 315 return 1; 316 #elif defined(__aarch64__) 317 return high_address_present(); 318 #else 319 return 0; 320 #endif 321 } 322 323 int main(int argc, char **argv) 324 { 325 int ret; 326 327 if (!supported_arch()) 328 return KSFT_SKIP; 329 330 testcases_init(); 331 332 ret = run_test(testcases, sz_testcases); 333 if (argc == 2 && !strcmp(argv[1], "--run-hugetlb")) 334 ret = run_test(hugetlb_testcases, sz_hugetlb_testcases); 335 return ret; 336 } 337