1 /* 2 * 3 * Authors: Kirill A. Shutemov <kirill.shutemov (at) linux.intel.com> 4 * Authors: Aneesh Kumar K.V <aneesh.kumar (at) linux.vnet.ibm.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2, as 8 * published by the Free Software Foundation. 9 10 * This program is distributed in the hope that it would be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 * 14 */ 15 16 #include <stdio.h> 17 #include <sys/mman.h> 18 #include <string.h> 19 20 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 21 22 #ifdef __powerpc64__ 23 #define PAGE_SIZE (64 << 10) 24 /* 25 * This will work with 16M and 2M hugepage size 26 */ 27 #define HUGETLB_SIZE (16 << 20) 28 #else 29 #define PAGE_SIZE (4 << 10) 30 #define HUGETLB_SIZE (2 << 20) 31 #endif 32 33 /* 34 * >= 128TB is the hint addr value we used to select 35 * large address space. 36 */ 37 #define ADDR_SWITCH_HINT (1UL << 47) 38 #define LOW_ADDR ((void *) (1UL << 30)) 39 #define HIGH_ADDR ((void *) (1UL << 48)) 40 41 struct testcase { 42 void *addr; 43 unsigned long size; 44 unsigned long flags; 45 const char *msg; 46 unsigned int low_addr_required:1; 47 unsigned int keep_mapped:1; 48 }; 49 50 static struct testcase testcases[] = { 51 { 52 /* 53 * If stack is moved, we could possibly allocate 54 * this at the requested address. 55 */ 56 .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)), 57 .size = PAGE_SIZE, 58 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 59 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)", 60 .low_addr_required = 1, 61 }, 62 { 63 /* 64 * We should never allocate at the requested address or above it 65 * The len cross the 128TB boundary. Without MAP_FIXED 66 * we will always search in the lower address space. 67 */ 68 .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)), 69 .size = 2 * PAGE_SIZE, 70 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 71 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, (2 * PAGE_SIZE))", 72 .low_addr_required = 1, 73 }, 74 { 75 /* 76 * Exact mapping at 128TB, the area is free we should get that 77 * even without MAP_FIXED. 78 */ 79 .addr = ((void *)(ADDR_SWITCH_HINT)), 80 .size = PAGE_SIZE, 81 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 82 .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)", 83 .keep_mapped = 1, 84 }, 85 { 86 .addr = (void *)(ADDR_SWITCH_HINT), 87 .size = 2 * PAGE_SIZE, 88 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 89 .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)", 90 }, 91 { 92 .addr = NULL, 93 .size = 2 * PAGE_SIZE, 94 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 95 .msg = "mmap(NULL)", 96 .low_addr_required = 1, 97 }, 98 { 99 .addr = LOW_ADDR, 100 .size = 2 * PAGE_SIZE, 101 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 102 .msg = "mmap(LOW_ADDR)", 103 .low_addr_required = 1, 104 }, 105 { 106 .addr = HIGH_ADDR, 107 .size = 2 * PAGE_SIZE, 108 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 109 .msg = "mmap(HIGH_ADDR)", 110 .keep_mapped = 1, 111 }, 112 { 113 .addr = HIGH_ADDR, 114 .size = 2 * PAGE_SIZE, 115 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 116 .msg = "mmap(HIGH_ADDR) again", 117 .keep_mapped = 1, 118 }, 119 { 120 .addr = HIGH_ADDR, 121 .size = 2 * PAGE_SIZE, 122 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 123 .msg = "mmap(HIGH_ADDR, MAP_FIXED)", 124 }, 125 { 126 .addr = (void *) -1, 127 .size = 2 * PAGE_SIZE, 128 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 129 .msg = "mmap(-1)", 130 .keep_mapped = 1, 131 }, 132 { 133 .addr = (void *) -1, 134 .size = 2 * PAGE_SIZE, 135 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 136 .msg = "mmap(-1) again", 137 }, 138 { 139 .addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)), 140 .size = PAGE_SIZE, 141 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 142 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)", 143 .low_addr_required = 1, 144 }, 145 { 146 .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE), 147 .size = 2 * PAGE_SIZE, 148 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 149 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2 * PAGE_SIZE)", 150 .low_addr_required = 1, 151 .keep_mapped = 1, 152 }, 153 { 154 .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE / 2), 155 .size = 2 * PAGE_SIZE, 156 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 157 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE/2 , 2 * PAGE_SIZE)", 158 .low_addr_required = 1, 159 .keep_mapped = 1, 160 }, 161 { 162 .addr = ((void *)(ADDR_SWITCH_HINT)), 163 .size = PAGE_SIZE, 164 .flags = MAP_PRIVATE | MAP_ANONYMOUS, 165 .msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)", 166 }, 167 { 168 .addr = (void *)(ADDR_SWITCH_HINT), 169 .size = 2 * PAGE_SIZE, 170 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 171 .msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)", 172 }, 173 }; 174 175 static struct testcase hugetlb_testcases[] = { 176 { 177 .addr = NULL, 178 .size = HUGETLB_SIZE, 179 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 180 .msg = "mmap(NULL, MAP_HUGETLB)", 181 .low_addr_required = 1, 182 }, 183 { 184 .addr = LOW_ADDR, 185 .size = HUGETLB_SIZE, 186 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 187 .msg = "mmap(LOW_ADDR, MAP_HUGETLB)", 188 .low_addr_required = 1, 189 }, 190 { 191 .addr = HIGH_ADDR, 192 .size = HUGETLB_SIZE, 193 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 194 .msg = "mmap(HIGH_ADDR, MAP_HUGETLB)", 195 .keep_mapped = 1, 196 }, 197 { 198 .addr = HIGH_ADDR, 199 .size = HUGETLB_SIZE, 200 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 201 .msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again", 202 .keep_mapped = 1, 203 }, 204 { 205 .addr = HIGH_ADDR, 206 .size = HUGETLB_SIZE, 207 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 208 .msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)", 209 }, 210 { 211 .addr = (void *) -1, 212 .size = HUGETLB_SIZE, 213 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 214 .msg = "mmap(-1, MAP_HUGETLB)", 215 .keep_mapped = 1, 216 }, 217 { 218 .addr = (void *) -1, 219 .size = HUGETLB_SIZE, 220 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 221 .msg = "mmap(-1, MAP_HUGETLB) again", 222 }, 223 { 224 .addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE), 225 .size = 2 * HUGETLB_SIZE, 226 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, 227 .msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2*HUGETLB_SIZE, MAP_HUGETLB)", 228 .low_addr_required = 1, 229 .keep_mapped = 1, 230 }, 231 { 232 .addr = (void *)(ADDR_SWITCH_HINT), 233 .size = 2 * HUGETLB_SIZE, 234 .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 235 .msg = "mmap(ADDR_SWITCH_HINT , 2*HUGETLB_SIZE, MAP_FIXED | MAP_HUGETLB)", 236 }, 237 }; 238 239 static int run_test(struct testcase *test, int count) 240 { 241 void *p; 242 int i, ret = 0; 243 244 for (i = 0; i < count; i++) { 245 struct testcase *t = test + i; 246 247 p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0); 248 249 printf("%s: %p - ", t->msg, p); 250 251 if (p == MAP_FAILED) { 252 printf("FAILED\n"); 253 ret = 1; 254 continue; 255 } 256 257 if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) { 258 printf("FAILED\n"); 259 ret = 1; 260 } else { 261 /* 262 * Do a dereference of the address returned so that we catch 263 * bugs in page fault handling 264 */ 265 memset(p, 0, t->size); 266 printf("OK\n"); 267 } 268 if (!t->keep_mapped) 269 munmap(p, t->size); 270 } 271 272 return ret; 273 } 274 275 static int supported_arch(void) 276 { 277 #if defined(__powerpc64__) 278 return 1; 279 #elif defined(__x86_64__) 280 return 1; 281 #else 282 return 0; 283 #endif 284 } 285 286 int main(int argc, char **argv) 287 { 288 int ret; 289 290 if (!supported_arch()) 291 return 0; 292 293 ret = run_test(testcases, ARRAY_SIZE(testcases)); 294 if (argc == 2 && !strcmp(argv[1], "--run-hugetlb")) 295 ret = run_test(hugetlb_testcases, ARRAY_SIZE(hugetlb_testcases)); 296 return ret; 297 } 298