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