| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | #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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | unsigned long os_get_top_address(void) | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | 	unsigned long test, original; | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | 	printf("Locating the bottom of the address space ... "); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | 	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; | 
					
						
							| 
									
										
										
										
											2008-04-28 02:13:57 -07:00
										 |  |  | 	if (sigaction(SIGSEGV, &sa, &old)) { | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | 		perror("os_get_top_address"); | 
					
						
							| 
									
										
										
										
											2008-04-28 02:13:57 -07:00
										 |  |  | 		exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | 	/* 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"); | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | 		exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | 	printf("0x%x\n", bottom << UM_KERN_PAGE_SHIFT); | 
					
						
							|  |  |  | 	printf("Locating the top of the address space ... "); | 
					
						
							|  |  |  | 	fflush(stdout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	original = bottom; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | 	/* 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 */ | 
					
						
							| 
									
										
										
										
											2008-04-28 02:13:57 -07:00
										 |  |  | 	if (sigaction(SIGSEGV, &old, NULL)) { | 
					
						
							| 
									
										
										
										
											2008-06-05 22:46:12 -07:00
										 |  |  | 		perror("os_get_top_address"); | 
					
						
							| 
									
										
										
										
											2008-04-28 02:13:57 -07:00
										 |  |  | 		exit(1); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-02-08 04:22:07 -08:00
										 |  |  | 	top <<= UM_KERN_PAGE_SHIFT; | 
					
						
							|  |  |  | 	printf("0x%x\n", top); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return top; | 
					
						
							|  |  |  | } |