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