uml: runtime host VMSPLIT detection
[linux-2.6] / arch / um / os-Linux / sys-i386 / task_size.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <sys/mman.h>
5 #include "longjmp.h"
6 #include "kern_constants.h"
7
8 static jmp_buf buf;
9
10 static void segfault(int sig)
11 {
12         longjmp(buf, 1);
13 }
14
15 static int page_ok(unsigned long page)
16 {
17         unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
18         unsigned long n = ~0UL;
19         void *mapped = NULL;
20         int ok = 0;
21
22         /*
23          * First see if the page is readable.  If it is, it may still
24          * be a VDSO, so we go on to see if it's writable.  If not
25          * then try mapping memory there.  If that fails, then we're
26          * still in the kernel area.  As a sanity check, we'll fail if
27          * the mmap succeeds, but gives us an address different from
28          * what we wanted.
29          */
30         if (setjmp(buf) == 0)
31                 n = *address;
32         else {
33                 mapped = mmap(address, UM_KERN_PAGE_SIZE,
34                               PROT_READ | PROT_WRITE,
35                               MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
36                 if (mapped == MAP_FAILED)
37                         return 0;
38                 if (mapped != address)
39                         goto out;
40         }
41
42         /*
43          * Now, is it writeable?  If so, then we're in user address
44          * space.  If not, then try mprotecting it and try the write
45          * again.
46          */
47         if (setjmp(buf) == 0) {
48                 *address = n;
49                 ok = 1;
50                 goto out;
51         } else if (mprotect(address, UM_KERN_PAGE_SIZE,
52                             PROT_READ | PROT_WRITE) != 0)
53                 goto out;
54
55         if (setjmp(buf) == 0) {
56                 *address = n;
57                 ok = 1;
58         }
59
60  out:
61         if (mapped != NULL)
62                 munmap(mapped, UM_KERN_PAGE_SIZE);
63         return ok;
64 }
65
66 unsigned long os_get_task_size(void)
67 {
68         struct sigaction sa, old;
69         unsigned long bottom = 0;
70         /*
71          * A 32-bit UML on a 64-bit host gets confused about the VDSO at
72          * 0xffffe000.  It is mapped, is readable, can be reprotected writeable
73          * and written.  However, exec discovers later that it can't be
74          * unmapped.  So, just set the highest address to be checked to just
75          * below it.  This might waste some address space on 4G/4G 32-bit
76          * hosts, but shouldn't hurt otherwise.
77          */
78         unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
79         unsigned long test;
80
81         printf("Locating the top of the address space ... ");
82         fflush(stdout);
83
84         /*
85          * We're going to be longjmping out of the signal handler, so
86          * SA_DEFER needs to be set.
87          */
88         sa.sa_handler = segfault;
89         sigemptyset(&sa.sa_mask);
90         sa.sa_flags = SA_NODEFER;
91         sigaction(SIGSEGV, &sa, &old);
92
93         if (!page_ok(bottom)) {
94                 fprintf(stderr, "Address 0x%x no good?\n",
95                         bottom << UM_KERN_PAGE_SHIFT);
96                 exit(1);
97         }
98
99         /* This could happen with a 4G/4G split */
100         if (page_ok(top))
101                 goto out;
102
103         do {
104                 test = bottom + (top - bottom) / 2;
105                 if (page_ok(test))
106                         bottom = test;
107                 else
108                         top = test;
109         } while (top - bottom > 1);
110
111 out:
112         /* Restore the old SIGSEGV handling */
113         sigaction(SIGSEGV, &old, NULL);
114
115         top <<= UM_KERN_PAGE_SHIFT;
116         printf("0x%x\n", top);
117         fflush(stdout);
118
119         return top;
120 }