]>
Commit | Line | Data |
---|---|---|
536788fe JD |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
3 | #include <signal.h> | |
4 | #include <sys/mman.h> | |
37185b33 | 5 | #include <longjmp.h> |
536788fe | 6 | |
51d34749 AV |
7 | #ifdef __i386__ |
8 | ||
536788fe JD |
9 | static jmp_buf buf; |
10 | ||
11 | static void segfault(int sig) | |
12 | { | |
13 | longjmp(buf, 1); | |
14 | } | |
15 | ||
16 | static 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 | ||
40fb16a3 | 67 | unsigned long os_get_top_address(void) |
536788fe JD |
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; | |
40fb16a3 | 80 | unsigned long test, original; |
536788fe | 81 | |
40fb16a3 | 82 | printf("Locating the bottom of the address space ... "); |
536788fe JD |
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; | |
4415d8a5 | 92 | if (sigaction(SIGSEGV, &sa, &old)) { |
40fb16a3 | 93 | perror("os_get_top_address"); |
4415d8a5 WC |
94 | exit(1); |
95 | } | |
536788fe | 96 | |
40fb16a3 TS |
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"); | |
536788fe JD |
109 | exit(1); |
110 | } | |
111 | ||
ad32a1f3 | 112 | printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT); |
40fb16a3 TS |
113 | printf("Locating the top of the address space ... "); |
114 | fflush(stdout); | |
115 | ||
116 | original = bottom; | |
117 | ||
536788fe JD |
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 | ||
130 | out: | |
131 | /* Restore the old SIGSEGV handling */ | |
4415d8a5 | 132 | if (sigaction(SIGSEGV, &old, NULL)) { |
40fb16a3 | 133 | perror("os_get_top_address"); |
4415d8a5 WC |
134 | exit(1); |
135 | } | |
536788fe | 136 | top <<= UM_KERN_PAGE_SHIFT; |
ad32a1f3 | 137 | printf("0x%lx\n", top); |
536788fe JD |
138 | |
139 | return top; | |
140 | } | |
51d34749 AV |
141 | |
142 | #else | |
143 | ||
144 | unsigned long os_get_top_address(void) | |
145 | { | |
146 | /* The old value of CONFIG_TOP_ADDR */ | |
147 | return 0x7fc0000000; | |
148 | } | |
149 | ||
150 | #endif |