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
151