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