1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25c48b108SAl Viro #include <stdio.h>
35c48b108SAl Viro #include <stdlib.h>
45c48b108SAl Viro #include <signal.h>
55c48b108SAl Viro #include <sys/mman.h>
637185b33SAl Viro #include <longjmp.h>
75c48b108SAl Viro
85c48b108SAl Viro #ifdef __i386__
95c48b108SAl Viro
105c48b108SAl Viro static jmp_buf buf;
115c48b108SAl Viro
segfault(int sig)125c48b108SAl Viro static void segfault(int sig)
135c48b108SAl Viro {
145c48b108SAl Viro longjmp(buf, 1);
155c48b108SAl Viro }
165c48b108SAl Viro
page_ok(unsigned long page)175c48b108SAl Viro static int page_ok(unsigned long page)
185c48b108SAl Viro {
195c48b108SAl Viro unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
205c48b108SAl Viro unsigned long n = ~0UL;
215c48b108SAl Viro void *mapped = NULL;
225c48b108SAl Viro int ok = 0;
235c48b108SAl Viro
245c48b108SAl Viro /*
255c48b108SAl Viro * First see if the page is readable. If it is, it may still
265c48b108SAl Viro * be a VDSO, so we go on to see if it's writable. If not
275c48b108SAl Viro * then try mapping memory there. If that fails, then we're
285c48b108SAl Viro * still in the kernel area. As a sanity check, we'll fail if
295c48b108SAl Viro * the mmap succeeds, but gives us an address different from
305c48b108SAl Viro * what we wanted.
315c48b108SAl Viro */
325c48b108SAl Viro if (setjmp(buf) == 0)
335c48b108SAl Viro n = *address;
345c48b108SAl Viro else {
355c48b108SAl Viro mapped = mmap(address, UM_KERN_PAGE_SIZE,
365c48b108SAl Viro PROT_READ | PROT_WRITE,
375c48b108SAl Viro MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
385c48b108SAl Viro if (mapped == MAP_FAILED)
395c48b108SAl Viro return 0;
405c48b108SAl Viro if (mapped != address)
415c48b108SAl Viro goto out;
425c48b108SAl Viro }
435c48b108SAl Viro
445c48b108SAl Viro /*
455c48b108SAl Viro * Now, is it writeable? If so, then we're in user address
465c48b108SAl Viro * space. If not, then try mprotecting it and try the write
475c48b108SAl Viro * again.
485c48b108SAl Viro */
495c48b108SAl Viro if (setjmp(buf) == 0) {
505c48b108SAl Viro *address = n;
515c48b108SAl Viro ok = 1;
525c48b108SAl Viro goto out;
535c48b108SAl Viro } else if (mprotect(address, UM_KERN_PAGE_SIZE,
545c48b108SAl Viro PROT_READ | PROT_WRITE) != 0)
555c48b108SAl Viro goto out;
565c48b108SAl Viro
575c48b108SAl Viro if (setjmp(buf) == 0) {
585c48b108SAl Viro *address = n;
595c48b108SAl Viro ok = 1;
605c48b108SAl Viro }
615c48b108SAl Viro
625c48b108SAl Viro out:
635c48b108SAl Viro if (mapped != NULL)
645c48b108SAl Viro munmap(mapped, UM_KERN_PAGE_SIZE);
655c48b108SAl Viro return ok;
665c48b108SAl Viro }
675c48b108SAl Viro
os_get_top_address(void)685c48b108SAl Viro unsigned long os_get_top_address(void)
695c48b108SAl Viro {
705c48b108SAl Viro struct sigaction sa, old;
715c48b108SAl Viro unsigned long bottom = 0;
725c48b108SAl Viro /*
735c48b108SAl Viro * A 32-bit UML on a 64-bit host gets confused about the VDSO at
745c48b108SAl Viro * 0xffffe000. It is mapped, is readable, can be reprotected writeable
755c48b108SAl Viro * and written. However, exec discovers later that it can't be
765c48b108SAl Viro * unmapped. So, just set the highest address to be checked to just
775c48b108SAl Viro * below it. This might waste some address space on 4G/4G 32-bit
785c48b108SAl Viro * hosts, but shouldn't hurt otherwise.
795c48b108SAl Viro */
805c48b108SAl Viro unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
815c48b108SAl Viro unsigned long test, original;
825c48b108SAl Viro
835c48b108SAl Viro printf("Locating the bottom of the address space ... ");
845c48b108SAl Viro fflush(stdout);
855c48b108SAl Viro
865c48b108SAl Viro /*
875c48b108SAl Viro * We're going to be longjmping out of the signal handler, so
885c48b108SAl Viro * SA_DEFER needs to be set.
895c48b108SAl Viro */
905c48b108SAl Viro sa.sa_handler = segfault;
915c48b108SAl Viro sigemptyset(&sa.sa_mask);
925c48b108SAl Viro sa.sa_flags = SA_NODEFER;
935c48b108SAl Viro if (sigaction(SIGSEGV, &sa, &old)) {
945c48b108SAl Viro perror("os_get_top_address");
955c48b108SAl Viro exit(1);
965c48b108SAl Viro }
975c48b108SAl Viro
985c48b108SAl Viro /* Manually scan the address space, bottom-up, until we find
995c48b108SAl Viro * the first valid page (or run out of them).
1005c48b108SAl Viro */
1015c48b108SAl Viro for (bottom = 0; bottom < top; bottom++) {
1025c48b108SAl Viro if (page_ok(bottom))
1035c48b108SAl Viro break;
1045c48b108SAl Viro }
1055c48b108SAl Viro
1065c48b108SAl Viro /* If we've got this far, we ran out of pages. */
1075c48b108SAl Viro if (bottom == top) {
1085c48b108SAl Viro fprintf(stderr, "Unable to determine bottom of address "
1095c48b108SAl Viro "space.\n");
1105c48b108SAl Viro exit(1);
1115c48b108SAl Viro }
1125c48b108SAl Viro
113ad32a1f3SColin Ian King printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT);
1145c48b108SAl Viro printf("Locating the top of the address space ... ");
1155c48b108SAl Viro fflush(stdout);
1165c48b108SAl Viro
1175c48b108SAl Viro original = bottom;
1185c48b108SAl Viro
1195c48b108SAl Viro /* This could happen with a 4G/4G split */
1205c48b108SAl Viro if (page_ok(top))
1215c48b108SAl Viro goto out;
1225c48b108SAl Viro
1235c48b108SAl Viro do {
1245c48b108SAl Viro test = bottom + (top - bottom) / 2;
1255c48b108SAl Viro if (page_ok(test))
1265c48b108SAl Viro bottom = test;
1275c48b108SAl Viro else
1285c48b108SAl Viro top = test;
1295c48b108SAl Viro } while (top - bottom > 1);
1305c48b108SAl Viro
1315c48b108SAl Viro out:
1325c48b108SAl Viro /* Restore the old SIGSEGV handling */
1335c48b108SAl Viro if (sigaction(SIGSEGV, &old, NULL)) {
1345c48b108SAl Viro perror("os_get_top_address");
1355c48b108SAl Viro exit(1);
1365c48b108SAl Viro }
1375c48b108SAl Viro top <<= UM_KERN_PAGE_SHIFT;
138ad32a1f3SColin Ian King printf("0x%lx\n", top);
1395c48b108SAl Viro
1405c48b108SAl Viro return top;
1415c48b108SAl Viro }
1425c48b108SAl Viro
1435c48b108SAl Viro #else
1445c48b108SAl Viro
os_get_top_address(void)1455c48b108SAl Viro unsigned long os_get_top_address(void)
1465c48b108SAl Viro {
1475c48b108SAl Viro /* The old value of CONFIG_TOP_ADDR */
148*bfc58e2bSJohannes Berg return 0x7fc0002000;
1495c48b108SAl Viro }
1505c48b108SAl Viro
1515c48b108SAl Viro #endif
152