Line data Source code
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(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(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 */
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 5 : unsigned long os_get_top_address(void)
146 : {
147 : /* The old value of CONFIG_TOP_ADDR */
148 5 : return 0x7fc0002000;
149 : }
150 :
151 : #endif
|