diff options
Diffstat (limited to 'arch/x86/um/os-Linux')
| -rw-r--r-- | arch/x86/um/os-Linux/Makefile | 13 | ||||
| -rw-r--r-- | arch/x86/um/os-Linux/mcontext.c | 31 | ||||
| -rw-r--r-- | arch/x86/um/os-Linux/prctl.c | 12 | ||||
| -rw-r--r-- | arch/x86/um/os-Linux/registers.c | 113 | ||||
| -rw-r--r-- | arch/x86/um/os-Linux/task_size.c | 150 | ||||
| -rw-r--r-- | arch/x86/um/os-Linux/tls.c | 67 | 
6 files changed, 386 insertions, 0 deletions
diff --git a/arch/x86/um/os-Linux/Makefile b/arch/x86/um/os-Linux/Makefile new file mode 100644 index 00000000000..253bfb8cb70 --- /dev/null +++ b/arch/x86/um/os-Linux/Makefile @@ -0,0 +1,13 @@ +# +# Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) +# Licensed under the GPL +# + +obj-y = registers.o task_size.o mcontext.o + +obj-$(CONFIG_X86_32) += tls.o +obj-$(CONFIG_64BIT) += prctl.o + +USER_OBJS := $(obj-y) + +include arch/um/scripts/Makefile.rules diff --git a/arch/x86/um/os-Linux/mcontext.c b/arch/x86/um/os-Linux/mcontext.c new file mode 100644 index 00000000000..1d33d72c628 --- /dev/null +++ b/arch/x86/um/os-Linux/mcontext.c @@ -0,0 +1,31 @@ +#include <sys/ucontext.h> +#define __FRAME_OFFSETS +#include <asm/ptrace.h> +#include <sysdep/ptrace.h> + +void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc) +{ +#ifdef __i386__ +#define COPY2(X,Y) regs->gp[X] = mc->gregs[REG_##Y] +#define COPY(X) regs->gp[X] = mc->gregs[REG_##X] +#define COPY_SEG(X) regs->gp[X] = mc->gregs[REG_##X] & 0xffff; +#define COPY_SEG_CPL3(X) regs->gp[X] = (mc->gregs[REG_##X] & 0xffff) | 3; +	COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS); +	COPY(EDI); COPY(ESI); COPY(EBP); +	COPY2(UESP, ESP); /* sic */ +	COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX); +	COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS); +#else +#define COPY2(X,Y) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##Y] +#define COPY(X) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##X] +	COPY(R8); COPY(R9); COPY(R10); COPY(R11); +	COPY(R12); COPY(R13); COPY(R14); COPY(R15); +	COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX); +	COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP); +	COPY(RIP); +	COPY2(EFLAGS, EFL); +	COPY2(CS, CSGSFS); +	regs->gp[CS / sizeof(unsigned long)] &= 0xffff; +	regs->gp[CS / sizeof(unsigned long)] |= 3; +#endif +} diff --git a/arch/x86/um/os-Linux/prctl.c b/arch/x86/um/os-Linux/prctl.c new file mode 100644 index 00000000000..96eb2bd2883 --- /dev/null +++ b/arch/x86/um/os-Linux/prctl.c @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com}) + * Licensed under the GPL + */ + +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +int os_arch_prctl(int pid, int code, unsigned long *addr) +{ +        return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code); +} diff --git a/arch/x86/um/os-Linux/registers.c b/arch/x86/um/os-Linux/registers.c new file mode 100644 index 00000000000..41bfe84e11a --- /dev/null +++ b/arch/x86/um/os-Linux/registers.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2004 PathScale, Inc + * Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Licensed under the GPL + */ + +#include <errno.h> +#include <sys/ptrace.h> +#ifdef __i386__ +#include <sys/user.h> +#endif +#include <longjmp.h> +#include <sysdep/ptrace_user.h> + +int save_fp_registers(int pid, unsigned long *fp_regs) +{ +	if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) +		return -errno; +	return 0; +} + +int restore_fp_registers(int pid, unsigned long *fp_regs) +{ +	if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) +		return -errno; +	return 0; +} + +#ifdef __i386__ +int have_fpx_regs = 1; +int save_fpx_registers(int pid, unsigned long *fp_regs) +{ +	if (ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs) < 0) +		return -errno; +	return 0; +} + +int restore_fpx_registers(int pid, unsigned long *fp_regs) +{ +	if (ptrace(PTRACE_SETFPXREGS, pid, 0, fp_regs) < 0) +		return -errno; +	return 0; +} + +int get_fp_registers(int pid, unsigned long *regs) +{ +	if (have_fpx_regs) +		return save_fpx_registers(pid, regs); +	else +		return save_fp_registers(pid, regs); +} + +int put_fp_registers(int pid, unsigned long *regs) +{ +	if (have_fpx_regs) +		return restore_fpx_registers(pid, regs); +	else +		return restore_fp_registers(pid, regs); +} + +void arch_init_registers(int pid) +{ +	struct user_fpxregs_struct fpx_regs; +	int err; + +	err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs); +	if (!err) +		return; + +	if (errno != EIO) +		panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", +		      errno); + +	have_fpx_regs = 0; +} +#else + +int get_fp_registers(int pid, unsigned long *regs) +{ +	return save_fp_registers(pid, regs); +} + +int put_fp_registers(int pid, unsigned long *regs) +{ +	return restore_fp_registers(pid, regs); +} + +#endif + +unsigned long get_thread_reg(int reg, jmp_buf *buf) +{ +	switch (reg) { +#ifdef __i386__ +	case HOST_IP: +		return buf[0]->__eip; +	case HOST_SP: +		return buf[0]->__esp; +	case HOST_BP: +		return buf[0]->__ebp; +#else +	case HOST_IP: +		return buf[0]->__rip; +	case HOST_SP: +		return buf[0]->__rsp; +	case HOST_BP: +		return buf[0]->__rbp; +#endif +	default: +		printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n", +		       reg); +		return 0; +	} +} diff --git a/arch/x86/um/os-Linux/task_size.c b/arch/x86/um/os-Linux/task_size.c new file mode 100644 index 00000000000..8502ad30e61 --- /dev/null +++ b/arch/x86/um/os-Linux/task_size.c @@ -0,0 +1,150 @@ +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/mman.h> +#include <longjmp.h> + +#ifdef __i386__ + +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; +} + +unsigned long os_get_top_address(void) +{ +	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; +	unsigned long test, original; + +	printf("Locating the bottom of the address space ... "); +	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; +	if (sigaction(SIGSEGV, &sa, &old)) { +		perror("os_get_top_address"); +		exit(1); +	} + +	/* 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; + +	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 */ +	if (sigaction(SIGSEGV, &old, NULL)) { +		perror("os_get_top_address"); +		exit(1); +	} +	top <<= UM_KERN_PAGE_SHIFT; +	printf("0x%x\n", top); + +	return top; +} + +#else + +unsigned long os_get_top_address(void) +{ +	/* The old value of CONFIG_TOP_ADDR */ +	return 0x7fc0000000; +} + +#endif diff --git a/arch/x86/um/os-Linux/tls.c b/arch/x86/um/os-Linux/tls.c new file mode 100644 index 00000000000..9d94b3b76c7 --- /dev/null +++ b/arch/x86/um/os-Linux/tls.c @@ -0,0 +1,67 @@ +#include <errno.h> +#include <linux/unistd.h> + +#include <sys/ptrace.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include <sysdep/tls.h> + +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA 25 +#endif + +#ifndef PTRACE_SET_THREAD_AREA +#define PTRACE_SET_THREAD_AREA 26 +#endif + +/* Checks whether host supports TLS, and sets *tls_min according to the value + * valid on the host. + * i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */ +void check_host_supports_tls(int *supports_tls, int *tls_min) +{ +	/* Values for x86 and x86_64.*/ +	int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64}; +	int i; + +	for (i = 0; i < ARRAY_SIZE(val); i++) { +		user_desc_t info; +		info.entry_number = val[i]; + +		if (syscall(__NR_get_thread_area, &info) == 0) { +			*tls_min = val[i]; +			*supports_tls = 1; +			return; +		} else { +			if (errno == EINVAL) +				continue; +			else if (errno == ENOSYS) +				*supports_tls = 0; +				return; +		} +	} + +	*supports_tls = 0; +} + +int os_set_thread_area(user_desc_t *info, int pid) +{ +	int ret; + +	ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number, +		     (unsigned long) info); +	if (ret < 0) +		ret = -errno; +	return ret; +} + +int os_get_thread_area(user_desc_t *info, int pid) +{ +	int ret; + +	ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number, +		     (unsigned long) info); +	if (ret < 0) +		ret = -errno; +	return ret; +}  | 
