![Tom Spink](/assets/img/avatar_default.png)
This patch makes os_get_task_size locate the bottom of the address space, as well as the top. This is for systems which put a lower limit on mmap addresses. It works by manually scanning pages from zero onwards until a valid page is found. Because the bottom of the address space may not be zero, it's not sufficient to assume the top of the address space is the size of the address space. The size is the difference between the top address and bottom address. [jdike@addtoit.com: changed the name to reflect that this function is supposed to return the top of the process address space, not its size and changed the return value to reflect that. Also some minor formatting changes] Signed-off-by: Tom Spink <tspink@gmail.com> Signed-off-by: Jeff Dike <jdike@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
140 lines
3.2 KiB
C
140 lines
3.2 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <sys/mman.h>
|
|
#include "longjmp.h"
|
|
#include "kern_constants.h"
|
|
|
|
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%x\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%x\n", top);
|
|
|
|
return top;
|
|
}
|