mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.3 KiB
151 lines
3.3 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <signal.h> |
|
#include <sys/mman.h> |
|
#include <longjmp.h> |
|
|
|
#ifdef __i386__ |
|
|
|
static jmp_buf buf; |
|
|
|
static void segfault(int sig) |
|
{ |
|
longjmp(buf, 1); |
|
} |
|
|
|
static int page_ok(unsigned long page) |
|
{ |
|
unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT); |
|
unsigned long n = ~0UL; |
|
void *mapped = NULL; |
|
int ok = 0; |
|
|
|
/* |
|
* First see if the page is readable. If it is, it may still |
|
* be a VDSO, so we go on to see if it's writable. If not |
|
* then try mapping memory there. If that fails, then we're |
|
* still in the kernel area. As a sanity check, we'll fail if |
|
* the mmap succeeds, but gives us an address different from |
|
* what we wanted. |
|
*/ |
|
if (setjmp(buf) == 0) |
|
n = *address; |
|
else { |
|
mapped = mmap(address, UM_KERN_PAGE_SIZE, |
|
PROT_READ | PROT_WRITE, |
|
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
|
if (mapped == MAP_FAILED) |
|
return 0; |
|
if (mapped != address) |
|
goto out; |
|
} |
|
|
|
/* |
|
* Now, is it writeable? If so, then we're in user address |
|
* space. If not, then try mprotecting it and try the write |
|
* again. |
|
*/ |
|
if (setjmp(buf) == 0) { |
|
*address = n; |
|
ok = 1; |
|
goto out; |
|
} else if (mprotect(address, UM_KERN_PAGE_SIZE, |
|
PROT_READ | PROT_WRITE) != 0) |
|
goto out; |
|
|
|
if (setjmp(buf) == 0) { |
|
*address = n; |
|
ok = 1; |
|
} |
|
|
|
out: |
|
if (mapped != NULL) |
|
munmap(mapped, UM_KERN_PAGE_SIZE); |
|
return ok; |
|
} |
|
|
|
unsigned long os_get_top_address(void) |
|
{ |
|
struct sigaction sa, old; |
|
unsigned long bottom = 0; |
|
/* |
|
* A 32-bit UML on a 64-bit host gets confused about the VDSO at |
|
* 0xffffe000. It is mapped, is readable, can be reprotected writeable |
|
* and written. However, exec discovers later that it can't be |
|
* unmapped. So, just set the highest address to be checked to just |
|
* below it. This might waste some address space on 4G/4G 32-bit |
|
* hosts, but shouldn't hurt otherwise. |
|
*/ |
|
unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT; |
|
unsigned long test, original; |
|
|
|
printf("Locating the bottom of the address space ... "); |
|
fflush(stdout); |
|
|
|
/* |
|
* We're going to be longjmping out of the signal handler, so |
|
* SA_DEFER needs to be set. |
|
*/ |
|
sa.sa_handler = segfault; |
|
sigemptyset(&sa.sa_mask); |
|
sa.sa_flags = SA_NODEFER; |
|
if (sigaction(SIGSEGV, &sa, &old)) { |
|
perror("os_get_top_address"); |
|
exit(1); |
|
} |
|
|
|
/* Manually scan the address space, bottom-up, until we find |
|
* the first valid page (or run out of them). |
|
*/ |
|
for (bottom = 0; bottom < top; bottom++) { |
|
if (page_ok(bottom)) |
|
break; |
|
} |
|
|
|
/* If we've got this far, we ran out of pages. */ |
|
if (bottom == top) { |
|
fprintf(stderr, "Unable to determine bottom of address " |
|
"space.\n"); |
|
exit(1); |
|
} |
|
|
|
printf("0x%lx\n", bottom << UM_KERN_PAGE_SHIFT); |
|
printf("Locating the top of the address space ... "); |
|
fflush(stdout); |
|
|
|
original = bottom; |
|
|
|
/* This could happen with a 4G/4G split */ |
|
if (page_ok(top)) |
|
goto out; |
|
|
|
do { |
|
test = bottom + (top - bottom) / 2; |
|
if (page_ok(test)) |
|
bottom = test; |
|
else |
|
top = test; |
|
} while (top - bottom > 1); |
|
|
|
out: |
|
/* Restore the old SIGSEGV handling */ |
|
if (sigaction(SIGSEGV, &old, NULL)) { |
|
perror("os_get_top_address"); |
|
exit(1); |
|
} |
|
top <<= UM_KERN_PAGE_SHIFT; |
|
printf("0x%lx\n", top); |
|
|
|
return top; |
|
} |
|
|
|
#else |
|
|
|
unsigned long os_get_top_address(void) |
|
{ |
|
/* The old value of CONFIG_TOP_ADDR */ |
|
return 0x7fc0002000; |
|
} |
|
|
|
#endif
|
|
|