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