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