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