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
10static jmp_buf buf;
11
12static void segfault(int sig)
13{
14 longjmp(buf, 1);
15}
16
17static 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
68unsigned 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
131out:
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
145unsigned long os_get_top_address(void)
146{
147 /* The old value of CONFIG_TOP_ADDR */
148 return 0x7fc0002000;
149}
150
151#endif
152

source code of linux/arch/x86/um/os-Linux/task_size.c