aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/os-Linux/sys-x86/task_size.c
diff options
context:
space:
mode:
authorAl Viro <viro@ftp.linux.org.uk>2011-08-18 15:03:49 -0400
committerRichard Weinberger <richard@nod.at>2011-11-02 09:14:53 -0400
commit51d34749051a6369093e5067ef67c17f17694921 (patch)
tree765e499c73e2797cc5d525f1b4cf590f17aa4132 /arch/um/os-Linux/sys-x86/task_size.c
parent7eb122555c8583e1601b7a620c5a88c1e06c3eac (diff)
um: merge arch/um/os-Linux/sys-{i386,x86_64}
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/os-Linux/sys-x86/task_size.c')
-rw-r--r--arch/um/os-Linux/sys-x86/task_size.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/arch/um/os-Linux/sys-x86/task_size.c b/arch/um/os-Linux/sys-x86/task_size.c
new file mode 100644
index 000000000000..efb16c5c9bcf
--- /dev/null
+++ b/arch/um/os-Linux/sys-x86/task_size.c
@@ -0,0 +1,150 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <signal.h>
4#include <sys/mman.h>
5#include "longjmp.h"
6
7#ifdef __i386__
8
9static jmp_buf buf;
10
11static void segfault(int sig)
12{
13 longjmp(buf, 1);
14}
15
16static int page_ok(unsigned long page)
17{
18 unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
19 unsigned long n = ~0UL;
20 void *mapped = NULL;
21 int ok = 0;
22
23 /*
24 * First see if the page is readable. If it is, it may still
25 * be a VDSO, so we go on to see if it's writable. If not
26 * then try mapping memory there. If that fails, then we're
27 * still in the kernel area. As a sanity check, we'll fail if
28 * the mmap succeeds, but gives us an address different from
29 * what we wanted.
30 */
31 if (setjmp(buf) == 0)
32 n = *address;
33 else {
34 mapped = mmap(address, UM_KERN_PAGE_SIZE,
35 PROT_READ | PROT_WRITE,
36 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
37 if (mapped == MAP_FAILED)
38 return 0;
39 if (mapped != address)
40 goto out;
41 }
42
43 /*
44 * Now, is it writeable? If so, then we're in user address
45 * space. If not, then try mprotecting it and try the write
46 * again.
47 */
48 if (setjmp(buf) == 0) {
49 *address = n;
50 ok = 1;
51 goto out;
52 } else if (mprotect(address, UM_KERN_PAGE_SIZE,
53 PROT_READ | PROT_WRITE) != 0)
54 goto out;
55
56 if (setjmp(buf) == 0) {
57 *address = n;
58 ok = 1;
59 }
60
61 out:
62 if (mapped != NULL)
63 munmap(mapped, UM_KERN_PAGE_SIZE);
64 return ok;
65}
66
67unsigned long os_get_top_address(void)
68{
69 struct sigaction sa, old;
70 unsigned long bottom = 0;
71 /*
72 * A 32-bit UML on a 64-bit host gets confused about the VDSO at
73 * 0xffffe000. It is mapped, is readable, can be reprotected writeable
74 * and written. However, exec discovers later that it can't be
75 * unmapped. So, just set the highest address to be checked to just
76 * below it. This might waste some address space on 4G/4G 32-bit
77 * hosts, but shouldn't hurt otherwise.
78 */
79 unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
80 unsigned long test, original;
81
82 printf("Locating the bottom of the address space ... ");
83 fflush(stdout);
84
85 /*
86 * We're going to be longjmping out of the signal handler, so
87 * SA_DEFER needs to be set.
88 */
89 sa.sa_handler = segfault;
90 sigemptyset(&sa.sa_mask);
91 sa.sa_flags = SA_NODEFER;
92 if (sigaction(SIGSEGV, &sa, &old)) {
93 perror("os_get_top_address");
94 exit(1);
95 }
96
97 /* Manually scan the address space, bottom-up, until we find
98 * the first valid page (or run out of them).
99 */
100 for (bottom = 0; bottom < top; bottom++) {
101 if (page_ok(bottom))
102 break;
103 }
104
105 /* If we've got this far, we ran out of pages. */
106 if (bottom == top) {
107 fprintf(stderr, "Unable to determine bottom of address "
108 "space.\n");
109 exit(1);
110 }
111
112 printf("0x%x\n", bottom << UM_KERN_PAGE_SHIFT);
113 printf("Locating the top of the address space ... ");
114 fflush(stdout);
115
116 original = bottom;
117
118 /* This could happen with a 4G/4G split */
119 if (page_ok(top))
120 goto out;
121
122 do {
123 test = bottom + (top - bottom) / 2;
124 if (page_ok(test))
125 bottom = test;
126 else
127 top = test;
128 } while (top - bottom > 1);
129
130out:
131 /* Restore the old SIGSEGV handling */
132 if (sigaction(SIGSEGV, &old, NULL)) {
133 perror("os_get_top_address");
134 exit(1);
135 }
136 top <<= UM_KERN_PAGE_SHIFT;
137 printf("0x%x\n", top);
138
139 return top;
140}
141
142#else
143
144unsigned long os_get_top_address(void)
145{
146 /* The old value of CONFIG_TOP_ADDR */
147 return 0x7fc0000000;
148}
149
150#endif