Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdio.h>
0003 #include <stdlib.h>
0004 #include <signal.h>
0005 #include <sys/mman.h>
0006 #include <longjmp.h>
0007 
0008 #ifdef __i386__
0009 
0010 static jmp_buf buf;
0011 
0012 static void segfault(int sig)
0013 {
0014     longjmp(buf, 1);
0015 }
0016 
0017 static int page_ok(unsigned long page)
0018 {
0019     unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
0020     unsigned long n = ~0UL;
0021     void *mapped = NULL;
0022     int ok = 0;
0023 
0024     /*
0025      * First see if the page is readable.  If it is, it may still
0026      * be a VDSO, so we go on to see if it's writable.  If not
0027      * then try mapping memory there.  If that fails, then we're
0028      * still in the kernel area.  As a sanity check, we'll fail if
0029      * the mmap succeeds, but gives us an address different from
0030      * what we wanted.
0031      */
0032     if (setjmp(buf) == 0)
0033         n = *address;
0034     else {
0035         mapped = mmap(address, UM_KERN_PAGE_SIZE,
0036                   PROT_READ | PROT_WRITE,
0037                   MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0038         if (mapped == MAP_FAILED)
0039             return 0;
0040         if (mapped != address)
0041             goto out;
0042     }
0043 
0044     /*
0045      * Now, is it writeable?  If so, then we're in user address
0046      * space.  If not, then try mprotecting it and try the write
0047      * again.
0048      */
0049     if (setjmp(buf) == 0) {
0050         *address = n;
0051         ok = 1;
0052         goto out;
0053     } else if (mprotect(address, UM_KERN_PAGE_SIZE,
0054                 PROT_READ | PROT_WRITE) != 0)
0055         goto out;
0056 
0057     if (setjmp(buf) == 0) {
0058         *address = n;
0059         ok = 1;
0060     }
0061 
0062  out:
0063     if (mapped != NULL)
0064         munmap(mapped, UM_KERN_PAGE_SIZE);
0065     return ok;
0066 }
0067 
0068 unsigned long os_get_top_address(void)
0069 {
0070     struct sigaction sa, old;
0071     unsigned long bottom = 0;
0072     /*
0073      * A 32-bit UML on a 64-bit host gets confused about the VDSO at
0074      * 0xffffe000.  It is mapped, is readable, can be reprotected writeable
0075      * and written.  However, exec discovers later that it can't be
0076      * unmapped.  So, just set the highest address to be checked to just
0077      * below it.  This might waste some address space on 4G/4G 32-bit
0078      * hosts, but shouldn't hurt otherwise.
0079      */
0080     unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
0081     unsigned long test, original;
0082 
0083     printf("Locating the bottom of the address space ... ");
0084     fflush(stdout);
0085 
0086     /*
0087      * We're going to be longjmping out of the signal handler, so
0088      * SA_DEFER needs to be set.
0089      */
0090     sa.sa_handler = segfault;
0091     sigemptyset(&sa.sa_mask);
0092     sa.sa_flags = SA_NODEFER;
0093     if (sigaction(SIGSEGV, &sa, &old)) {
0094         perror("os_get_top_address");
0095         exit(1);
0096     }
0097 
0098     /* Manually scan the address space, bottom-up, until we find
0099      * the first valid page (or run out of them).
0100      */
0101     for (bottom = 0; bottom < top; bottom++) {
0102         if (page_ok(bottom))
0103             break;
0104     }
0105 
0106     /* If we've got this far, we ran out of pages. */
0107     if (bottom == top) {
0108         fprintf(stderr, "Unable to determine bottom of address "
0109             "space.\n");
0110         exit(1);
0111     }
0112 
0113     printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT);
0114     printf("Locating the top of the address space ... ");
0115     fflush(stdout);
0116 
0117     original = bottom;
0118 
0119     /* This could happen with a 4G/4G split */
0120     if (page_ok(top))
0121         goto out;
0122 
0123     do {
0124         test = bottom + (top - bottom) / 2;
0125         if (page_ok(test))
0126             bottom = test;
0127         else
0128             top = test;
0129     } while (top - bottom > 1);
0130 
0131 out:
0132     /* Restore the old SIGSEGV handling */
0133     if (sigaction(SIGSEGV, &old, NULL)) {
0134         perror("os_get_top_address");
0135         exit(1);
0136     }
0137     top <<= UM_KERN_PAGE_SHIFT;
0138     printf("0x%lx\n", top);
0139 
0140     return top;
0141 }
0142 
0143 #else
0144 
0145 unsigned long os_get_top_address(void)
0146 {
0147     /* The old value of CONFIG_TOP_ADDR */
0148     return 0x7fc0002000;
0149 }
0150 
0151 #endif