337 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 addresses17 * 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 26struct 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 35static struct testcase *testcases;36static struct testcase *hugetlb_testcases;37static int sz_testcases, sz_hugetlb_testcases;38static unsigned long switch_hint;39 40/* Initialize testcases inside a function to compute parameters at runtime */41void 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#endif57 58 struct testcase t[] = {59 {60 /*61 * If stack is moved, we could possibly allocate62 * 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 hint73 * addr is never at requested address or above it, which is74 * 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, should86 * 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 260static 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 catch284 * bugs in page fault handling285 */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 */298static 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#endif309 310static 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#else319 return 0;320#endif321}322 323int 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