Linux Audio

Check our new training course

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