Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1#include <stdio.h>
  2#include <stdlib.h>
  3#include <signal.h>
  4#include <sys/mman.h>
  5#include "longjmp.h"
  6#include "kern_constants.h"
  7
  8static jmp_buf buf;
  9
 10static void segfault(int sig)
 11{
 12	longjmp(buf, 1);
 13}
 14
 15static int page_ok(unsigned long page)
 16{
 17	unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT);
 18	unsigned long n = ~0UL;
 19	void *mapped = NULL;
 20	int ok = 0;
 21
 22	/*
 23	 * First see if the page is readable.  If it is, it may still
 24	 * be a VDSO, so we go on to see if it's writable.  If not
 25	 * then try mapping memory there.  If that fails, then we're
 26	 * still in the kernel area.  As a sanity check, we'll fail if
 27	 * the mmap succeeds, but gives us an address different from
 28	 * what we wanted.
 29	 */
 30	if (setjmp(buf) == 0)
 31		n = *address;
 32	else {
 33		mapped = mmap(address, UM_KERN_PAGE_SIZE,
 34			      PROT_READ | PROT_WRITE,
 35			      MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 36		if (mapped == MAP_FAILED)
 37			return 0;
 38		if (mapped != address)
 39			goto out;
 40	}
 41
 42	/*
 43	 * Now, is it writeable?  If so, then we're in user address
 44	 * space.  If not, then try mprotecting it and try the write
 45	 * again.
 46	 */
 47	if (setjmp(buf) == 0) {
 48		*address = n;
 49		ok = 1;
 50		goto out;
 51	} else if (mprotect(address, UM_KERN_PAGE_SIZE,
 52			    PROT_READ | PROT_WRITE) != 0)
 53		goto out;
 54
 55	if (setjmp(buf) == 0) {
 56		*address = n;
 57		ok = 1;
 58	}
 59
 60 out:
 61	if (mapped != NULL)
 62		munmap(mapped, UM_KERN_PAGE_SIZE);
 63	return ok;
 64}
 65
 66unsigned long os_get_top_address(void)
 67{
 68	struct sigaction sa, old;
 69	unsigned long bottom = 0;
 70	/*
 71	 * A 32-bit UML on a 64-bit host gets confused about the VDSO at
 72	 * 0xffffe000.  It is mapped, is readable, can be reprotected writeable
 73	 * and written.  However, exec discovers later that it can't be
 74	 * unmapped.  So, just set the highest address to be checked to just
 75	 * below it.  This might waste some address space on 4G/4G 32-bit
 76	 * hosts, but shouldn't hurt otherwise.
 77	 */
 78	unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT;
 79	unsigned long test, original;
 80
 81	printf("Locating the bottom of the address space ... ");
 82	fflush(stdout);
 83
 84	/*
 85	 * We're going to be longjmping out of the signal handler, so
 86	 * SA_DEFER needs to be set.
 87	 */
 88	sa.sa_handler = segfault;
 89	sigemptyset(&sa.sa_mask);
 90	sa.sa_flags = SA_NODEFER;
 91	if (sigaction(SIGSEGV, &sa, &old)) {
 92		perror("os_get_top_address");
 93		exit(1);
 94	}
 95
 96	/* Manually scan the address space, bottom-up, until we find
 97	 * the first valid page (or run out of them).
 98	 */
 99	for (bottom = 0; bottom < top; bottom++) {
100		if (page_ok(bottom))
101			break;
102	}
103
104	/* If we've got this far, we ran out of pages. */
105	if (bottom == top) {
106		fprintf(stderr, "Unable to determine bottom of address "
107			"space.\n");
108		exit(1);
109	}
110
111	printf("0x%x\n", bottom << UM_KERN_PAGE_SHIFT);
112	printf("Locating the top of the address space ... ");
113	fflush(stdout);
114
115	original = bottom;
116
117	/* This could happen with a 4G/4G split */
118	if (page_ok(top))
119		goto out;
120
121	do {
122		test = bottom + (top - bottom) / 2;
123		if (page_ok(test))
124			bottom = test;
125		else
126			top = test;
127	} while (top - bottom > 1);
128
129out:
130	/* Restore the old SIGSEGV handling */
131	if (sigaction(SIGSEGV, &old, NULL)) {
132		perror("os_get_top_address");
133		exit(1);
134	}
135	top <<= UM_KERN_PAGE_SHIFT;
136	printf("0x%x\n", top);
137
138	return top;
139}