• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
segfault(int sig)10 static void segfault(int sig)
11 {
12 	longjmp(buf, 1);
13 }
14 
page_ok(unsigned long page)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 
os_get_top_address(void)66 unsigned long os_get_top_address(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, original;
80 
81 	printf("Locating the bottom 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 	if (sigaction(SIGSEGV, &sa, &old)) {
92 		perror("os_get_top_address");
93 		exit(1);
94 	}
95 
96 	/* Manually scan the address space, bottom-up, until we find
97 	 * the first valid page (or run out of them).
98 	 */
99 	for (bottom = 0; bottom < top; bottom++) {
100 		if (page_ok(bottom))
101 			break;
102 	}
103 
104 	/* If we've got this far, we ran out of pages. */
105 	if (bottom == top) {
106 		fprintf(stderr, "Unable to determine bottom of address "
107 			"space.\n");
108 		exit(1);
109 	}
110 
111 	printf("0x%x\n", bottom << UM_KERN_PAGE_SHIFT);
112 	printf("Locating the top of the address space ... ");
113 	fflush(stdout);
114 
115 	original = bottom;
116 
117 	/* This could happen with a 4G/4G split */
118 	if (page_ok(top))
119 		goto out;
120 
121 	do {
122 		test = bottom + (top - bottom) / 2;
123 		if (page_ok(test))
124 			bottom = test;
125 		else
126 			top = test;
127 	} while (top - bottom > 1);
128 
129 out:
130 	/* Restore the old SIGSEGV handling */
131 	if (sigaction(SIGSEGV, &old, NULL)) {
132 		perror("os_get_top_address");
133 		exit(1);
134 	}
135 	top <<= UM_KERN_PAGE_SHIFT;
136 	printf("0x%x\n", top);
137 
138 	return top;
139 }
140