Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
task_size.c
Go to the documentation of this file.
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 
9 static jmp_buf buf;
10 
11 static void segfault(int sig)
12 {
13  longjmp(buf, 1);
14 }
15 
16 static 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,
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 
67 unsigned 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 
130 out:
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 
144 unsigned long os_get_top_address(void)
145 {
146  /* The old value of CONFIG_TOP_ADDR */
147  return 0x7fc0000000;
148 }
149 
150 #endif