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