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 | |
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 | |
68 | unsigned 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(page: 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(page: top)) |
121 | goto out; |
122 | |
123 | do { |
124 | test = bottom + (top - bottom) / 2; |
125 | if (page_ok(page: test)) |
126 | bottom = test; |
127 | else |
128 | top = test; |
129 | } while (top - bottom > 1); |
130 | |
131 | out: |
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 | |
145 | unsigned long os_get_top_address(void) |
146 | { |
147 | /* The old value of CONFIG_TOP_ADDR */ |
148 | return 0x7fc0002000; |
149 | } |
150 | |
151 | #endif |
152 | |