xref: /openbmc/linux/arch/x86/um/os-Linux/task_size.c (revision 23c2b932)
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,
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 
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%lx\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%lx\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
151