Linux Audio

Check our new training course

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