xref: /linux/arch/x86/um/os-Linux/task_size.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
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