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