aboutsummaryrefslogtreecommitdiff
path: root/arch/um/os-Linux
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/os-Linux')
-rw-r--r--arch/um/os-Linux/Makefile14
-rw-r--r--arch/um/os-Linux/aio.c414
-rw-r--r--arch/um/os-Linux/elf_aux.c3
-rw-r--r--arch/um/os-Linux/process.c58
-rw-r--r--arch/um/os-Linux/start_up.c359
-rw-r--r--arch/um/os-Linux/tt.c113
6 files changed, 954 insertions, 7 deletions
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile
index 4ddf540284c..7a1662419c0 100644
--- a/arch/um/os-Linux/Makefile
+++ b/arch/um/os-Linux/Makefile
@@ -3,11 +3,19 @@
# Licensed under the GPL
#
-obj-y = elf_aux.o file.o process.o signal.o time.o tty.o user_syms.o drivers/ \
- sys-$(SUBARCH)/
+obj-y = aio.o elf_aux.o file.o process.o signal.o start_up.o time.o tt.o \
+ tty.o user_syms.o drivers/ sys-$(SUBARCH)/
-USER_OBJS := elf_aux.o file.o process.o signal.o time.o tty.o
+USER_OBJS := aio.o elf_aux.o file.o process.o signal.o start_up.o time.o tt.o \
+ tty.o
+
+elf_aux.o: $(ARCH_DIR)/kernel-offsets.h
+CFLAGS_elf_aux.o += -I$(objtree)/arch/um
CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH)
+HAVE_AIO_ABI := $(shell [ -r /usr/include/linux/aio_abi.h ] && \
+ echo -DHAVE_AIO_ABI )
+CFLAGS_aio.o += $(HAVE_AIO_ABI)
+
include arch/um/scripts/Makefile.rules
diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c
new file mode 100644
index 00000000000..b04897cd995
--- /dev/null
+++ b/arch/um/os-Linux/aio.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include "os.h"
+#include "helper.h"
+#include "aio.h"
+#include "init.h"
+#include "user.h"
+#include "mode.h"
+
+static int aio_req_fd_r = -1;
+static int aio_req_fd_w = -1;
+
+static int update_aio(struct aio_context *aio, int res)
+{
+ if(res < 0)
+ aio->len = res;
+ else if((res == 0) && (aio->type == AIO_READ)){
+ /* This is the EOF case - we have hit the end of the file
+ * and it ends in a partial block, so we fill the end of
+ * the block with zeros and claim success.
+ */
+ memset(aio->data, 0, aio->len);
+ aio->len = 0;
+ }
+ else if(res > 0){
+ aio->len -= res;
+ aio->data += res;
+ aio->offset += res;
+ return aio->len;
+ }
+
+ return 0;
+}
+
+#if defined(HAVE_AIO_ABI)
+#include <linux/aio_abi.h>
+
+/* If we have the headers, we are going to build with AIO enabled.
+ * If we don't have aio in libc, we define the necessary stubs here.
+ */
+
+#if !defined(HAVE_AIO_LIBC)
+
+static long io_setup(int n, aio_context_t *ctxp)
+{
+ return syscall(__NR_io_setup, n, ctxp);
+}
+
+static long io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp)
+{
+ return syscall(__NR_io_submit, ctx, nr, iocbpp);
+}
+
+static long io_getevents(aio_context_t ctx_id, long min_nr, long nr,
+ struct io_event *events, struct timespec *timeout)
+{
+ return syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, timeout);
+}
+
+#endif
+
+/* The AIO_MMAP cases force the mmapped page into memory here
+ * rather than in whatever place first touches the data. I used
+ * to do this by touching the page, but that's delicate because
+ * gcc is prone to optimizing that away. So, what's done here
+ * is we read from the descriptor from which the page was
+ * mapped. The caller is required to pass an offset which is
+ * inside the page that was mapped. Thus, when the read
+ * returns, we know that the page is in the page cache, and
+ * that it now backs the mmapped area.
+ */
+
+static int do_aio(aio_context_t ctx, struct aio_context *aio)
+{
+ struct iocb iocb, *iocbp = &iocb;
+ char c;
+ int err;
+
+ iocb = ((struct iocb) { .aio_data = (unsigned long) aio,
+ .aio_reqprio = 0,
+ .aio_fildes = aio->fd,
+ .aio_buf = (unsigned long) aio->data,
+ .aio_nbytes = aio->len,
+ .aio_offset = aio->offset,
+ .aio_reserved1 = 0,
+ .aio_reserved2 = 0,
+ .aio_reserved3 = 0 });
+
+ switch(aio->type){
+ case AIO_READ:
+ iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ break;
+ case AIO_WRITE:
+ iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
+ break;
+ case AIO_MMAP:
+ iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ iocb.aio_buf = (unsigned long) &c;
+ iocb.aio_nbytes = sizeof(c);
+ break;
+ default:
+ printk("Bogus op in do_aio - %d\n", aio->type);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = io_submit(ctx, 1, &iocbp);
+ if(err > 0)
+ err = 0;
+
+ out:
+ return err;
+}
+
+static aio_context_t ctx = 0;
+
+static int aio_thread(void *arg)
+{
+ struct aio_thread_reply reply;
+ struct aio_context *aio;
+ struct io_event event;
+ int err, n;
+
+ signal(SIGWINCH, SIG_IGN);
+
+ while(1){
+ n = io_getevents(ctx, 1, 1, &event, NULL);
+ if(n < 0){
+ if(errno == EINTR)
+ continue;
+ printk("aio_thread - io_getevents failed, "
+ "errno = %d\n", errno);
+ }
+ else {
+ aio = (struct aio_context *) event.data;
+ if(update_aio(aio, event.res)){
+ do_aio(ctx, aio);
+ continue;
+ }
+
+ reply = ((struct aio_thread_reply)
+ { .data = aio,
+ .err = aio->len });
+ err = os_write_file(aio->reply_fd, &reply,
+ sizeof(reply));
+ if(err != sizeof(reply))
+ printk("aio_thread - write failed, "
+ "fd = %d, err = %d\n", aio->reply_fd,
+ -err);
+ }
+ }
+ return 0;
+}
+
+#endif
+
+static int do_not_aio(struct aio_context *aio)
+{
+ char c;
+ int err;
+
+ switch(aio->type){
+ case AIO_READ:
+ err = os_seek_file(aio->fd, aio->offset);
+ if(err)
+ goto out;
+
+ err = os_read_file(aio->fd, aio->data, aio->len);
+ break;
+ case AIO_WRITE:
+ err = os_seek_file(aio->fd, aio->offset);
+ if(err)
+ goto out;
+
+ err = os_write_file(aio->fd, aio->data, aio->len);
+ break;
+ case AIO_MMAP:
+ err = os_seek_file(aio->fd, aio->offset);
+ if(err)
+ goto out;
+
+ err = os_read_file(aio->fd, &c, sizeof(c));
+ break;
+ default:
+ printk("do_not_aio - bad request type : %d\n", aio->type);
+ err = -EINVAL;
+ break;
+ }
+
+ out:
+ return err;
+}
+
+static int not_aio_thread(void *arg)
+{
+ struct aio_context *aio;
+ struct aio_thread_reply reply;
+ int err;
+
+ signal(SIGWINCH, SIG_IGN);
+ while(1){
+ err = os_read_file(aio_req_fd_r, &aio, sizeof(aio));
+ if(err != sizeof(aio)){
+ if(err < 0)
+ printk("not_aio_thread - read failed, "
+ "fd = %d, err = %d\n", aio_req_fd_r,
+ -err);
+ else {
+ printk("not_aio_thread - short read, fd = %d, "
+ "length = %d\n", aio_req_fd_r, err);
+ }
+ continue;
+ }
+ again:
+ err = do_not_aio(aio);
+
+ if(update_aio(aio, err))
+ goto again;
+
+ reply = ((struct aio_thread_reply) { .data = aio,
+ .err = aio->len });
+ err = os_write_file(aio->reply_fd, &reply, sizeof(reply));
+ if(err != sizeof(reply))
+ printk("not_aio_thread - write failed, fd = %d, "
+ "err = %d\n", aio_req_fd_r, -err);
+ }
+}
+
+static int submit_aio_24(struct aio_context *aio)
+{
+ int err;
+
+ err = os_write_file(aio_req_fd_w, &aio, sizeof(aio));
+ if(err == sizeof(aio))
+ err = 0;
+
+ return err;
+}
+
+static int aio_pid = -1;
+static int (*submit_proc)(struct aio_context *aio);
+
+static int init_aio_24(void)
+{
+ unsigned long stack;
+ int fds[2], err;
+
+ err = os_pipe(fds, 1, 1);
+ if(err)
+ goto out;
+
+ aio_req_fd_w = fds[0];
+ aio_req_fd_r = fds[1];
+ err = run_helper_thread(not_aio_thread, NULL,
+ CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
+ if(err < 0)
+ goto out_close_pipe;
+
+ aio_pid = err;
+ goto out;
+
+ out_close_pipe:
+ os_close_file(fds[0]);
+ os_close_file(fds[1]);
+ aio_req_fd_w = -1;
+ aio_req_fd_r = -1;
+ out:
+#ifndef HAVE_AIO_ABI
+ printk("/usr/include/linux/aio_abi.h not present during build\n");
+#endif
+ printk("2.6 host AIO support not used - falling back to I/O "
+ "thread\n");
+
+ submit_proc = submit_aio_24;
+
+ return 0;
+}
+
+#ifdef HAVE_AIO_ABI
+#define DEFAULT_24_AIO 0
+static int submit_aio_26(struct aio_context *aio)
+{
+ struct aio_thread_reply reply;
+ int err;
+
+ err = do_aio(ctx, aio);
+ if(err){
+ reply = ((struct aio_thread_reply) { .data = aio,
+ .err = err });
+ err = os_write_file(aio->reply_fd, &reply, sizeof(reply));
+ if(err != sizeof(reply))
+ printk("submit_aio_26 - write failed, "
+ "fd = %d, err = %d\n", aio->reply_fd, -err);
+ else err = 0;
+ }
+
+ return err;
+}
+
+static int init_aio_26(void)
+{
+ unsigned long stack;
+ int err;
+
+ if(io_setup(256, &ctx)){
+ printk("aio_thread failed to initialize context, err = %d\n",
+ errno);
+ return -errno;
+ }
+
+ err = run_helper_thread(aio_thread, NULL,
+ CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
+ if(err < 0)
+ return -errno;
+
+ aio_pid = err;
+
+ printk("Using 2.6 host AIO\n");
+
+ submit_proc = submit_aio_26;
+
+ return 0;
+}
+
+#else
+#define DEFAULT_24_AIO 1
+static int submit_aio_26(struct aio_context *aio)
+{
+ return -ENOSYS;
+}
+
+static int init_aio_26(void)
+{
+ submit_proc = submit_aio_26;
+ return -ENOSYS;
+}
+#endif
+
+static int aio_24 = DEFAULT_24_AIO;
+
+static int __init set_aio_24(char *name, int *add)
+{
+ aio_24 = 1;
+ return 0;
+}
+
+__uml_setup("aio=2.4", set_aio_24,
+"aio=2.4\n"
+" This is used to force UML to use 2.4-style AIO even when 2.6 AIO is\n"
+" available. 2.4 AIO is a single thread that handles one request at a\n"
+" time, synchronously. 2.6 AIO is a thread which uses the 2.6 AIO \n"
+" interface to handle an arbitrary number of pending requests. 2.6 AIO \n"
+" is not available in tt mode, on 2.4 hosts, or when UML is built with\n"
+" /usr/include/linux/aio_abi.h not available. Many distributions don't\n"
+" include aio_abi.h, so you will need to copy it from a kernel tree to\n"
+" your /usr/include/linux in order to build an AIO-capable UML\n\n"
+);
+
+static int init_aio(void)
+{
+ int err;
+
+ CHOOSE_MODE(({
+ if(!aio_24){
+ printk("Disabling 2.6 AIO in tt mode\n");
+ aio_24 = 1;
+ } }), (void) 0);
+
+ if(!aio_24){
+ err = init_aio_26();
+ if(err && (errno == ENOSYS)){
+ printk("2.6 AIO not supported on the host - "
+ "reverting to 2.4 AIO\n");
+ aio_24 = 1;
+ }
+ else return err;
+ }
+
+ if(aio_24)
+ return init_aio_24();
+
+ return 0;
+}
+
+/* The reason for the __initcall/__uml_exitcall asymmetry is that init_aio
+ * needs to be called when the kernel is running because it calls run_helper,
+ * which needs get_free_page. exit_aio is a __uml_exitcall because the generic
+ * kernel does not run __exitcalls on shutdown, and can't because many of them
+ * break when called outside of module unloading.
+ */
+__initcall(init_aio);
+
+static void exit_aio(void)
+{
+ if(aio_pid != -1)
+ os_kill_process(aio_pid, 1);
+}
+
+__uml_exitcall(exit_aio);
+
+int submit_aio(struct aio_context *aio)
+{
+ return (*submit_proc)(aio);
+}
diff --git a/arch/um/os-Linux/elf_aux.c b/arch/um/os-Linux/elf_aux.c
index 4cca3e9c23f..1399520a858 100644
--- a/arch/um/os-Linux/elf_aux.c
+++ b/arch/um/os-Linux/elf_aux.c
@@ -12,8 +12,9 @@
#include "init.h"
#include "elf_user.h"
#include "mem_user.h"
+#include <kernel-offsets.h>
-#if ELF_CLASS == ELFCLASS32
+#if HOST_ELF_CLASS == ELFCLASS32
typedef Elf32_auxv_t elf_auxv_t;
#else
typedef Elf64_auxv_t elf_auxv_t;
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c
index 1e126bfd31a..d32413e4b4c 100644
--- a/arch/um/os-Linux/process.c
+++ b/arch/um/os-Linux/process.c
@@ -3,10 +3,10 @@
* Licensed under the GPL
*/
-#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
+#include <setjmp.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
@@ -14,6 +14,10 @@
#include "os.h"
#include "user.h"
#include "user_util.h"
+#include "signal_user.h"
+#include "process.h"
+#include "irq_user.h"
+#include "kern_util.h"
#define ARBITRARY_ADDR -1
#define FAILURE_PID -1
@@ -114,8 +118,10 @@ void os_usr1_process(int pid)
kill(pid, SIGUSR1);
}
-/*Don't use the glibc version, which caches the result in TLS. It misses some
- * syscalls, and also breaks with clone(), which does not unshare the TLS.*/
+/* Don't use the glibc version, which caches the result in TLS. It misses some
+ * syscalls, and also breaks with clone(), which does not unshare the TLS.
+ */
+
inline _syscall0(pid_t, getpid)
int os_getpid(void)
@@ -164,6 +170,52 @@ int os_unmap_memory(void *addr, int len)
return(0);
}
+void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int))
+{
+ int flags = 0, pages;
+
+ if(sig_stack != NULL){
+ pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER);
+ set_sigstack(sig_stack, pages * page_size());
+ flags = SA_ONSTACK;
+ }
+ if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1);
+}
+
+void init_new_thread_signals(int altstack)
+{
+ int flags = altstack ? SA_ONSTACK : 0;
+
+ set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags,
+ SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
+ set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags,
+ SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
+ set_handler(SIGFPE, (__sighandler_t) sig_handler, flags,
+ SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
+ set_handler(SIGILL, (__sighandler_t) sig_handler, flags,
+ SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
+ set_handler(SIGBUS, (__sighandler_t) sig_handler, flags,
+ SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
+ set_handler(SIGUSR2, (__sighandler_t) sig_handler,
+ flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
+ signal(SIGHUP, SIG_IGN);
+
+ init_irq_signals(altstack);
+}
+
+int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr)
+{
+ sigjmp_buf buf;
+ int n;
+
+ *jmp_ptr = &buf;
+ n = sigsetjmp(buf, 1);
+ if(n != 0)
+ return(n);
+ (*fn)(arg);
+ return(0);
+}
+
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
new file mode 100644
index 00000000000..040cc1472bc
--- /dev/null
+++ b/arch/um/os-Linux/start_up.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sched.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <asm/unistd.h>
+#include <asm/page.h>
+#include "user_util.h"
+#include "kern_util.h"
+#include "user.h"
+#include "signal_kern.h"
+#include "signal_user.h"
+#include "sysdep/ptrace.h"
+#include "sysdep/sigcontext.h"
+#include "irq_user.h"
+#include "ptrace_user.h"
+#include "time_user.h"
+#include "init.h"
+#include "os.h"
+#include "uml-config.h"
+#include "choose-mode.h"
+#include "mode.h"
+#include "tempfile.h"
+#ifdef UML_CONFIG_MODE_SKAS
+#include "skas.h"
+#include "skas_ptrace.h"
+#include "registers.h"
+#endif
+
+static int ptrace_child(void *arg)
+{
+ int ret;
+ int pid = os_getpid(), ppid = getppid();
+ int sc_result;
+
+ if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
+ perror("ptrace");
+ os_kill_process(pid, 0);
+ }
+ os_stop_process(pid);
+
+ /*This syscall will be intercepted by the parent. Don't call more than
+ * once, please.*/
+ sc_result = os_getpid();
+
+ if (sc_result == pid)
+ ret = 1; /*Nothing modified by the parent, we are running
+ normally.*/
+ else if (sc_result == ppid)
+ ret = 0; /*Expected in check_ptrace and check_sysemu when they
+ succeed in modifying the stack frame*/
+ else
+ ret = 2; /*Serious trouble! This could be caused by a bug in
+ host 2.6 SKAS3/2.6 patch before release -V6, together
+ with a bug in the UML code itself.*/
+ _exit(ret);
+}
+
+static int start_ptraced_child(void **stack_out)
+{
+ void *stack;
+ unsigned long sp;
+ int pid, n, status;
+
+ stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if(stack == MAP_FAILED)
+ panic("check_ptrace : mmap failed, errno = %d", errno);
+ sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
+ pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL);
+ if(pid < 0)
+ panic("start_ptraced_child : clone failed, errno = %d", errno);
+ CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+ if(n < 0)
+ panic("check_ptrace : clone failed, errno = %d", errno);
+ if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
+ panic("check_ptrace : expected SIGSTOP, got status = %d",
+ status);
+
+ *stack_out = stack;
+ return(pid);
+}
+
+/* When testing for SYSEMU support, if it is one of the broken versions, we
+ * must just avoid using sysemu, not panic, but only if SYSEMU features are
+ * broken.
+ * So only for SYSEMU features we test mustpanic, while normal host features
+ * must work anyway!
+ */
+static int stop_ptraced_child(int pid, void *stack, int exitcode,
+ int mustpanic)
+{
+ int status, n, ret = 0;
+
+ if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
+ panic("check_ptrace : ptrace failed, errno = %d", errno);
+ CATCH_EINTR(n = waitpid(pid, &status, 0));
+ if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
+ int exit_with = WEXITSTATUS(status);
+ if (exit_with == 2)
+ printk("check_ptrace : child exited with status 2. "
+ "Serious trouble happening! Try updating your "
+ "host skas patch!\nDisabling SYSEMU support.");
+ printk("check_ptrace : child exited with exitcode %d, while "
+ "expecting %d; status 0x%x", exit_with,
+ exitcode, status);
+ if (mustpanic)
+ panic("\n");
+ else
+ printk("\n");
+ ret = -1;
+ }
+
+ if(munmap(stack, PAGE_SIZE) < 0)
+ panic("check_ptrace : munmap failed, errno = %d", errno);
+ return ret;
+}
+
+int ptrace_faultinfo = 1;
+int proc_mm = 1;
+
+static int __init skas0_cmd_param(char *str, int* add)
+{
+ ptrace_faultinfo = proc_mm = 0;
+ return 0;
+}
+
+__uml_setup("skas0", skas0_cmd_param,
+ "skas0\n"
+ " Disables SKAS3 usage, so that SKAS0 is used, unless \n"
+ " you specify mode=tt.\n\n");
+
+static int force_sysemu_disabled = 0;
+
+static int __init nosysemu_cmd_param(char *str, int* add)
+{
+ force_sysemu_disabled = 1;
+ return 0;
+}
+
+__uml_setup("nosysemu", nosysemu_cmd_param,
+"nosysemu\n"
+" Turns off syscall emulation patch for ptrace (SYSEMU) on.\n"
+" SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
+" behaviour of ptrace() and helps reducing host context switch rate.\n"
+" To make it working, you need a kernel patch for your host, too.\n"
+" See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
+" information.\n\n");
+
+static void __init check_sysemu(void)
+{
+ void *stack;
+ int pid, n, status, count=0;
+
+ printk("Checking syscall emulation patch for ptrace...");
+ sysemu_supported = 0;
+ pid = start_ptraced_child(&stack);
+
+ if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
+ goto fail;
+
+ CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+ if (n < 0)
+ panic("check_sysemu : wait failed, errno = %d", errno);
+ if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
+ panic("check_sysemu : expected SIGTRAP, "
+ "got status = %d", status);
+
+ n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
+ os_getpid());
+ if(n < 0)
+ panic("check_sysemu : failed to modify system "
+ "call return, errno = %d", errno);
+
+ if (stop_ptraced_child(pid, stack, 0, 0) < 0)
+ goto fail_stopped;
+
+ sysemu_supported = 1;
+ printk("OK\n");
+ set_using_sysemu(!force_sysemu_disabled);
+
+ printk("Checking advanced syscall emulation patch for ptrace...");
+ pid = start_ptraced_child(&stack);
+
+ if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
+ (void *) PTRACE_O_TRACESYSGOOD) < 0)
+ panic("check_ptrace: PTRACE_OLDSETOPTIONS failed, errno = %d",
+ errno);
+
+ while(1){
+ count++;
+ if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
+ goto fail;
+ CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+ if(n < 0)
+ panic("check_ptrace : wait failed, errno = %d", errno);
+ if(WIFSTOPPED(status) && (WSTOPSIG(status) == (SIGTRAP|0x80))){
+ if (!count)
+ panic("check_ptrace : SYSEMU_SINGLESTEP "
+ "doesn't singlestep");
+ n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
+ os_getpid());
+ if(n < 0)
+ panic("check_sysemu : failed to modify system "
+ "call return, errno = %d", errno);
+ break;
+ }
+ else if(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
+ count++;
+ else
+ panic("check_ptrace : expected SIGTRAP or "
+ "(SIGTRAP|0x80), got status = %d", status);
+ }
+ if (stop_ptraced_child(pid, stack, 0, 0) < 0)
+ goto fail_stopped;
+
+ sysemu_supported = 2;
+ printk("OK\n");
+
+ if ( !force_sysemu_disabled )
+ set_using_sysemu(sysemu_supported);
+ return;
+
+fail:
+ stop_ptraced_child(pid, stack, 1, 0);
+fail_stopped:
+ printk("missing\n");
+}
+
+static void __init check_ptrace(void)
+{
+ void *stack;
+ int pid, syscall, n, status;
+
+ printk("Checking that ptrace can change system call numbers...");
+ pid = start_ptraced_child(&stack);
+
+ if(ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
+ panic("check_ptrace: PTRACE_OLDSETOPTIONS failed, errno = %d", errno);
+
+ while(1){
+ if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
+ panic("check_ptrace : ptrace failed, errno = %d",
+ errno);
+ CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+ if(n < 0)
+ panic("check_ptrace : wait failed, errno = %d", errno);
+ if(!WIFSTOPPED(status) || (WSTOPSIG(status) != (SIGTRAP|0x80)))
+ panic("check_ptrace : expected (SIGTRAP|0x80), "
+ "got status = %d", status);
+
+ syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
+ 0);
+ if(syscall == __NR_getpid){
+ n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
+ __NR_getppid);
+ if(n < 0)
+ panic("check_ptrace : failed to modify system "
+ "call, errno = %d", errno);
+ break;
+ }
+ }
+ stop_ptraced_child(pid, stack, 0, 1);
+ printk("OK\n");
+ check_sysemu();
+}
+
+void os_early_checks(void)
+{
+ check_ptrace();
+}
+
+static int __init noprocmm_cmd_param(char *str, int* add)
+{
+ proc_mm = 0;
+ return 0;
+}
+
+__uml_setup("noprocmm", noprocmm_cmd_param,
+"noprocmm\n"
+" Turns off usage of /proc/mm, even if host supports it.\n"
+" To support /proc/mm, the host needs to be patched using\n"
+" the current skas3 patch.\n\n");
+
+static int __init noptracefaultinfo_cmd_param(char *str, int* add)
+{
+ ptrace_faultinfo = 0;
+ return 0;
+}
+
+__uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param,
+"noptracefaultinfo\n"
+" Turns off usage of PTRACE_FAULTINFO, even if host supports\n"
+" it. To support PTRACE_FAULTINFO, the host needs to be patched\n"
+" using the current skas3 patch.\n\n");
+
+#ifdef UML_CONFIG_MODE_SKAS
+static inline void check_skas3_ptrace_support(void)
+{
+ struct ptrace_faultinfo fi;
+ void *stack;
+ int pid, n;
+
+ printf("Checking for the skas3 patch in the host...");
+ pid = start_ptraced_child(&stack);
+
+ n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi);
+ if (n < 0) {
+ ptrace_faultinfo = 0;
+ if(errno == EIO)
+ printf("not found\n");
+ else
+ perror("not found");
+ }
+ else {
+ if (!ptrace_faultinfo)
+ printf("found but disabled on command line\n");
+ else
+ printf("found\n");
+ }
+
+ init_registers(pid);
+ stop_ptraced_child(pid, stack, 1, 1);
+}
+
+int can_do_skas(void)
+{
+ printf("Checking for /proc/mm...");
+ if (os_access("/proc/mm", OS_ACC_W_OK) < 0) {
+ proc_mm = 0;
+ printf("not found\n");
+ }
+ else {
+ if (!proc_mm)
+ printf("found but disabled on command line\n");
+ else
+ printf("found\n");
+ }
+
+ check_skas3_ptrace_support();
+ return 1;
+}
+#else
+int can_do_skas(void)
+{
+ return(0);
+}
+#endif
diff --git a/arch/um/os-Linux/tt.c b/arch/um/os-Linux/tt.c
new file mode 100644
index 00000000000..5b047ab8416
--- /dev/null
+++ b/arch/um/os-Linux/tt.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sched.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <sys/time.h>
+#include <sys/ptrace.h>
+#include <linux/ptrace.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <asm/ptrace.h>
+#include <asm/unistd.h>
+#include <asm/page.h>
+#include "user_util.h"
+#include "kern_util.h"
+#include "user.h"
+#include "signal_kern.h"
+#include "signal_user.h"
+#include "sysdep/ptrace.h"
+#include "sysdep/sigcontext.h"
+#include "irq_user.h"
+#include "ptrace_user.h"
+#include "time_user.h"
+#include "init.h"
+#include "os.h"
+#include "uml-config.h"
+#include "choose-mode.h"
+#include "mode.h"
+#include "tempfile.h"
+
+/*
+ *-------------------------
+ * only for tt mode (will be deleted in future...)
+ *-------------------------
+ */
+
+struct tramp {
+ int (*tramp)(void *);
+ void *tramp_data;
+ unsigned long temp_stack;
+ int flags;
+ int pid;
+};
+
+/* See above for why sigkill is here */
+
+int sigkill = SIGKILL;
+
+int outer_tramp(void *arg)
+{
+ struct tramp *t;
+ int sig = sigkill;
+
+ t = arg;
+ t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2,
+ t->flags, t->tramp_data);
+ if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL);
+ kill(os_getpid(), sig);
+ _exit(0);
+}
+
+int start_fork_tramp(void *thread_arg, unsigned long temp_stack,
+ int clone_flags, int (*tramp)(void *))
+{
+ struct tramp arg;
+ unsigned long sp;
+ int new_pid, status, err;
+
+ /* The trampoline will run on the temporary stack */
+ sp = stack_sp(temp_stack);
+
+ clone_flags |= CLONE_FILES | SIGCHLD;
+
+ arg.tramp = tramp;
+ arg.tramp_data = thread_arg;
+ arg.temp_stack = temp_stack;
+ arg.flags = clone_flags;
+
+ /* Start the process and wait for it to kill itself */
+ new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg);
+ if(new_pid < 0)
+ return(new_pid);
+
+ CATCH_EINTR(err = waitpid(new_pid, &status, 0));
+ if(err < 0)
+ panic("Waiting for outer trampoline failed - errno = %d",
+ errno);
+
+ if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL))
+ panic("outer trampoline didn't exit with SIGKILL, "
+ "status = %d", status);
+
+ return(arg.pid);
+}
+
+void forward_pending_sigio(int target)
+{
+ sigset_t sigs;
+
+ if(sigpending(&sigs))
+ panic("forward_pending_sigio : sigpending failed");
+ if(sigismember(&sigs, SIGIO))
+ kill(target, SIGIO);
+}
+