task_size.c (3401B)
1// SPDX-License-Identifier: GPL-2.0 2#include <stdio.h> 3#include <stdlib.h> 4#include <signal.h> 5#include <sys/mman.h> 6#include <longjmp.h> 7 8#ifdef __i386__ 9 10static jmp_buf buf; 11 12static void segfault(int sig) 13{ 14 longjmp(buf, 1); 15} 16 17static int page_ok(unsigned long page) 18{ 19 unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT); 20 unsigned long n = ~0UL; 21 void *mapped = NULL; 22 int ok = 0; 23 24 /* 25 * First see if the page is readable. If it is, it may still 26 * be a VDSO, so we go on to see if it's writable. If not 27 * then try mapping memory there. If that fails, then we're 28 * still in the kernel area. As a sanity check, we'll fail if 29 * the mmap succeeds, but gives us an address different from 30 * what we wanted. 31 */ 32 if (setjmp(buf) == 0) 33 n = *address; 34 else { 35 mapped = mmap(address, UM_KERN_PAGE_SIZE, 36 PROT_READ | PROT_WRITE, 37 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 38 if (mapped == MAP_FAILED) 39 return 0; 40 if (mapped != address) 41 goto out; 42 } 43 44 /* 45 * Now, is it writeable? If so, then we're in user address 46 * space. If not, then try mprotecting it and try the write 47 * again. 48 */ 49 if (setjmp(buf) == 0) { 50 *address = n; 51 ok = 1; 52 goto out; 53 } else if (mprotect(address, UM_KERN_PAGE_SIZE, 54 PROT_READ | PROT_WRITE) != 0) 55 goto out; 56 57 if (setjmp(buf) == 0) { 58 *address = n; 59 ok = 1; 60 } 61 62 out: 63 if (mapped != NULL) 64 munmap(mapped, UM_KERN_PAGE_SIZE); 65 return ok; 66} 67 68unsigned long os_get_top_address(void) 69{ 70 struct sigaction sa, old; 71 unsigned long bottom = 0; 72 /* 73 * A 32-bit UML on a 64-bit host gets confused about the VDSO at 74 * 0xffffe000. It is mapped, is readable, can be reprotected writeable 75 * and written. However, exec discovers later that it can't be 76 * unmapped. So, just set the highest address to be checked to just 77 * below it. This might waste some address space on 4G/4G 32-bit 78 * hosts, but shouldn't hurt otherwise. 79 */ 80 unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT; 81 unsigned long test, original; 82 83 printf("Locating the bottom of the address space ... "); 84 fflush(stdout); 85 86 /* 87 * We're going to be longjmping out of the signal handler, so 88 * SA_DEFER needs to be set. 89 */ 90 sa.sa_handler = segfault; 91 sigemptyset(&sa.sa_mask); 92 sa.sa_flags = SA_NODEFER; 93 if (sigaction(SIGSEGV, &sa, &old)) { 94 perror("os_get_top_address"); 95 exit(1); 96 } 97 98 /* Manually scan the address space, bottom-up, until we find 99 * the first valid page (or run out of them). 100 */ 101 for (bottom = 0; bottom < top; bottom++) { 102 if (page_ok(bottom)) 103 break; 104 } 105 106 /* If we've got this far, we ran out of pages. */ 107 if (bottom == top) { 108 fprintf(stderr, "Unable to determine bottom of address " 109 "space.\n"); 110 exit(1); 111 } 112 113 printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT); 114 printf("Locating the top of the address space ... "); 115 fflush(stdout); 116 117 original = bottom; 118 119 /* This could happen with a 4G/4G split */ 120 if (page_ok(top)) 121 goto out; 122 123 do { 124 test = bottom + (top - bottom) / 2; 125 if (page_ok(test)) 126 bottom = test; 127 else 128 top = test; 129 } while (top - bottom > 1); 130 131out: 132 /* Restore the old SIGSEGV handling */ 133 if (sigaction(SIGSEGV, &old, NULL)) { 134 perror("os_get_top_address"); 135 exit(1); 136 } 137 top <<= UM_KERN_PAGE_SHIFT; 138 printf("0x%lx\n", top); 139 140 return top; 141} 142 143#else 144 145unsigned long os_get_top_address(void) 146{ 147 /* The old value of CONFIG_TOP_ADDR */ 148 return 0x7fc0002000; 149} 150 151#endif