aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Spink <tspink@gmail.com>2008-08-05 16:14:06 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-08-20 11:15:37 -0700
commitd448dc1547ff53d06386582bb9db155aec2b10a7 (patch)
treed3631a9b237930d41d97765a887fc42db34cfd3d
parentbbdf9b9a28729ed1dd211368f76baef051aaecae (diff)
uml: deal with inaccessible address space start
commit 40fb16a360d9c6459afee91dc793c1e3374feb94 upstream 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> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--arch/um/include/os.h2
-rw-r--r--arch/um/kernel/um_arch.c2
-rw-r--r--arch/um/os-Linux/sys-i386/task_size.c38
-rw-r--r--arch/um/os-Linux/sys-x86_64/task_size.c2
4 files changed, 32 insertions, 12 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 32c799e3a49..2d0a2a69cf9 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -298,6 +298,6 @@ extern int os_arch_prctl(int pid, int code, unsigned long *addr);
extern int get_pty(void);
/* sys-$ARCH/task_size.c */
-extern unsigned long os_get_task_size(void);
+extern unsigned long os_get_top_address(void);
#endif
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index 9cecb42eb49..d1fe2224dda 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -273,7 +273,7 @@ int __init linux_main(int argc, char **argv)
if (have_root == 0)
add_arg(DEFAULT_COMMAND_LINE);
- host_task_size = os_get_task_size();
+ host_task_size = os_get_top_address();
/*
* TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps
* out
diff --git a/arch/um/os-Linux/sys-i386/task_size.c b/arch/um/os-Linux/sys-i386/task_size.c
index 48d211b3d9a..4d26d5d6431 100644
--- a/arch/um/os-Linux/sys-i386/task_size.c
+++ b/arch/um/os-Linux/sys-i386/task_size.c
@@ -63,7 +63,7 @@ static int page_ok(unsigned long page)
return ok;
}
-unsigned long os_get_task_size(void)
+unsigned long os_get_top_address(void)
{
struct sigaction sa, old;
unsigned long bottom = 0;
@@ -76,9 +76,9 @@ unsigned long os_get_task_size(void)
* hosts, but shouldn't hurt otherwise.
*/
unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
- unsigned long test;
+ unsigned long test, original;
- printf("Locating the top of the address space ... ");
+ printf("Locating the bottom of the address space ... ");
fflush(stdout);
/*
@@ -88,14 +88,32 @@ unsigned long os_get_task_size(void)
sa.sa_handler = segfault;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NODEFER;
- sigaction(SIGSEGV, &sa, &old);
+ if (sigaction(SIGSEGV, &sa, &old)) {
+ perror("os_get_top_address");
+ exit(1);
+ }
- if (!page_ok(bottom)) {
- fprintf(stderr, "Address 0x%x no good?\n",
- bottom << UM_KERN_PAGE_SHIFT);
+ /* 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;
@@ -110,8 +128,10 @@ unsigned long os_get_task_size(void)
out:
/* Restore the old SIGSEGV handling */
- sigaction(SIGSEGV, &old, NULL);
-
+ if (sigaction(SIGSEGV, &old, NULL)) {
+ perror("os_get_top_address");
+ exit(1);
+ }
top <<= UM_KERN_PAGE_SHIFT;
printf("0x%x\n", top);
fflush(stdout);
diff --git a/arch/um/os-Linux/sys-x86_64/task_size.c b/arch/um/os-Linux/sys-x86_64/task_size.c
index fad6f57f8ee..26a0dd1f349 100644
--- a/arch/um/os-Linux/sys-x86_64/task_size.c
+++ b/arch/um/os-Linux/sys-x86_64/task_size.c
@@ -1,4 +1,4 @@
-unsigned long os_get_task_size(unsigned long shift)
+unsigned long os_get_top_address(unsigned long shift)
{
/* The old value of CONFIG_TOP_ADDR */
return 0x7fc0000000;