GNU Linux-libre 4.14.332-gnu1
[releases.git] / arch / x86 / um / os-Linux / task_size.c
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 unsigned long os_get_top_address(void)
146 {
147         /* The old value of CONFIG_TOP_ADDR */
148         return 0x7fc0000000;
149 }
150
151 #endif