Path: blob/master/arch/um/os-Linux/sys-i386/task_size.c
10821 views
#include <stdio.h>1#include <stdlib.h>2#include <signal.h>3#include <sys/mman.h>4#include "longjmp.h"5#include "kern_constants.h"67static jmp_buf buf;89static void segfault(int sig)10{11longjmp(buf, 1);12}1314static int page_ok(unsigned long page)15{16unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);17unsigned long n = ~0UL;18void *mapped = NULL;19int ok = 0;2021/*22* First see if the page is readable. If it is, it may still23* be a VDSO, so we go on to see if it's writable. If not24* then try mapping memory there. If that fails, then we're25* still in the kernel area. As a sanity check, we'll fail if26* the mmap succeeds, but gives us an address different from27* what we wanted.28*/29if (setjmp(buf) == 0)30n = *address;31else {32mapped = mmap(address, UM_KERN_PAGE_SIZE,33PROT_READ | PROT_WRITE,34MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);35if (mapped == MAP_FAILED)36return 0;37if (mapped != address)38goto out;39}4041/*42* Now, is it writeable? If so, then we're in user address43* space. If not, then try mprotecting it and try the write44* again.45*/46if (setjmp(buf) == 0) {47*address = n;48ok = 1;49goto out;50} else if (mprotect(address, UM_KERN_PAGE_SIZE,51PROT_READ | PROT_WRITE) != 0)52goto out;5354if (setjmp(buf) == 0) {55*address = n;56ok = 1;57}5859out:60if (mapped != NULL)61munmap(mapped, UM_KERN_PAGE_SIZE);62return ok;63}6465unsigned long os_get_top_address(void)66{67struct sigaction sa, old;68unsigned long bottom = 0;69/*70* A 32-bit UML on a 64-bit host gets confused about the VDSO at71* 0xffffe000. It is mapped, is readable, can be reprotected writeable72* and written. However, exec discovers later that it can't be73* unmapped. So, just set the highest address to be checked to just74* below it. This might waste some address space on 4G/4G 32-bit75* hosts, but shouldn't hurt otherwise.76*/77unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;78unsigned long test, original;7980printf("Locating the bottom of the address space ... ");81fflush(stdout);8283/*84* We're going to be longjmping out of the signal handler, so85* SA_DEFER needs to be set.86*/87sa.sa_handler = segfault;88sigemptyset(&sa.sa_mask);89sa.sa_flags = SA_NODEFER;90if (sigaction(SIGSEGV, &sa, &old)) {91perror("os_get_top_address");92exit(1);93}9495/* Manually scan the address space, bottom-up, until we find96* the first valid page (or run out of them).97*/98for (bottom = 0; bottom < top; bottom++) {99if (page_ok(bottom))100break;101}102103/* If we've got this far, we ran out of pages. */104if (bottom == top) {105fprintf(stderr, "Unable to determine bottom of address "106"space.\n");107exit(1);108}109110printf("0x%x\n", bottom << UM_KERN_PAGE_SHIFT);111printf("Locating the top of the address space ... ");112fflush(stdout);113114original = bottom;115116/* This could happen with a 4G/4G split */117if (page_ok(top))118goto out;119120do {121test = bottom + (top - bottom) / 2;122if (page_ok(test))123bottom = test;124else125top = test;126} while (top - bottom > 1);127128out:129/* Restore the old SIGSEGV handling */130if (sigaction(SIGSEGV, &old, NULL)) {131perror("os_get_top_address");132exit(1);133}134top <<= UM_KERN_PAGE_SHIFT;135printf("0x%x\n", top);136137return top;138}139140141