v4.19.13 snapshot.
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile
new file mode 100644
index 0000000..ada473b
--- /dev/null
+++ b/arch/um/os-Linux/Makefile
@@ -0,0 +1,23 @@
+# 
+# Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+# Licensed under the GPL
+#
+
+# Don't instrument UML-specific code
+KCOV_INSTRUMENT                := n
+
+obj-y = aio.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
+	registers.o sigio.o signal.o start_up.o time.o tty.o \
+	umid.o user_syms.o util.o drivers/ skas/
+
+obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o
+
+USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
+	main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
+	tty.o umid.o util.o
+
+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 0000000..014eb35
--- /dev/null
+++ b/arch/um/os-Linux/aio.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <unistd.h>
+#include <sched.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <asm/unistd.h>
+#include <aio.h>
+#include <init.h>
+#include <kern_util.h>
+#include <os.h>
+
+struct aio_thread_req {
+	enum aio_type type;
+	int io_fd;
+	unsigned long long offset;
+	char *buf;
+	int len;
+	struct aio_context *aio;
+};
+
+#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, enum aio_type type, int fd, char *buf,
+		  int len, unsigned long long offset, struct aio_context *aio)
+{
+	struct iocb *iocbp = & ((struct iocb) {
+				    .aio_data       = (unsigned long) aio,
+				    .aio_fildes     = fd,
+				    .aio_buf        = (unsigned long) buf,
+				    .aio_nbytes     = len,
+				    .aio_offset     = offset
+			     });
+	char c;
+
+	switch (type) {
+	case AIO_READ:
+		iocbp->aio_lio_opcode = IOCB_CMD_PREAD;
+		break;
+	case AIO_WRITE:
+		iocbp->aio_lio_opcode = IOCB_CMD_PWRITE;
+		break;
+	case AIO_MMAP:
+		iocbp->aio_lio_opcode = IOCB_CMD_PREAD;
+		iocbp->aio_buf = (unsigned long) &c;
+		iocbp->aio_nbytes = sizeof(c);
+		break;
+	default:
+		printk(UM_KERN_ERR "Bogus op in do_aio - %d\n", type);
+		return -EINVAL;
+	}
+
+	return (io_submit(ctx, 1, &iocbp) > 0) ? 0 : -errno;
+}
+
+/* Initialized in an initcall and unchanged thereafter */
+static aio_context_t ctx = 0;
+
+static int aio_thread(void *arg)
+{
+	struct aio_thread_reply reply;
+	struct io_event event;
+	int err, n, reply_fd;
+
+	os_fix_helper_signals();
+	while (1) {
+		n = io_getevents(ctx, 1, 1, &event, NULL);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			printk(UM_KERN_ERR "aio_thread - io_getevents failed, "
+			       "errno = %d\n", errno);
+		}
+		else {
+			reply = ((struct aio_thread_reply)
+				{ .data = (void *) (long) event.data,
+						.err	= event.res });
+			reply_fd = ((struct aio_context *) reply.data)->reply_fd;
+			err = write(reply_fd, &reply, sizeof(reply));
+			if (err != sizeof(reply))
+				printk(UM_KERN_ERR "aio_thread - write failed, "
+				       "fd = %d, err = %d\n", reply_fd, errno);
+		}
+	}
+	return 0;
+}
+
+#endif
+
+static int do_not_aio(struct aio_thread_req *req)
+{
+	char c;
+	unsigned long long actual;
+	int n;
+
+	actual = lseek64(req->io_fd, req->offset, SEEK_SET);
+	if (actual != req->offset)
+		return -errno;
+
+	switch (req->type) {
+	case AIO_READ:
+		n = read(req->io_fd, req->buf, req->len);
+		break;
+	case AIO_WRITE:
+		n = write(req->io_fd, req->buf, req->len);
+		break;
+	case AIO_MMAP:
+		n = read(req->io_fd, &c, sizeof(c));
+		break;
+	default:
+		printk(UM_KERN_ERR "do_not_aio - bad request type : %d\n",
+		       req->type);
+		return -EINVAL;
+	}
+
+	if (n < 0)
+		return -errno;
+	return 0;
+}
+
+/* These are initialized in initcalls and not changed */
+static int aio_req_fd_r = -1;
+static int aio_req_fd_w = -1;
+static int aio_pid = -1;
+static unsigned long aio_stack;
+
+static int not_aio_thread(void *arg)
+{
+	struct aio_thread_req req;
+	struct aio_thread_reply reply;
+	int err;
+
+	os_fix_helper_signals();
+	while (1) {
+		err = read(aio_req_fd_r, &req, sizeof(req));
+		if (err != sizeof(req)) {
+			if (err < 0)
+				printk(UM_KERN_ERR "not_aio_thread - "
+				       "read failed, fd = %d, err = %d\n",
+				       aio_req_fd_r,
+				       errno);
+			else {
+				printk(UM_KERN_ERR "not_aio_thread - short "
+				       "read, fd = %d, length = %d\n",
+				       aio_req_fd_r, err);
+			}
+			continue;
+		}
+		err = do_not_aio(&req);
+		reply = ((struct aio_thread_reply) { .data 	= req.aio,
+						     .err	= err });
+		err = write(req.aio->reply_fd, &reply, sizeof(reply));
+		if (err != sizeof(reply))
+			printk(UM_KERN_ERR "not_aio_thread - write failed, "
+			       "fd = %d, err = %d\n", req.aio->reply_fd, errno);
+	}
+
+	return 0;
+}
+
+static int init_aio_24(void)
+{
+	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 = os_set_fd_block(aio_req_fd_w, 0);
+	if (err)
+		goto out_close_pipe;
+
+	err = run_helper_thread(not_aio_thread, NULL,
+				CLONE_FILES | CLONE_VM, &aio_stack);
+	if (err < 0)
+		goto out_close_pipe;
+
+	aio_pid = err;
+	goto out;
+
+out_close_pipe:
+	close(fds[0]);
+	close(fds[1]);
+	aio_req_fd_w = -1;
+	aio_req_fd_r = -1;
+out:
+#ifndef HAVE_AIO_ABI
+	printk(UM_KERN_INFO "/usr/include/linux/aio_abi.h not present during "
+	       "build\n");
+#endif
+	printk(UM_KERN_INFO "2.6 host AIO support not used - falling back to "
+	       "I/O thread\n");
+	return 0;
+}
+
+#ifdef HAVE_AIO_ABI
+#define DEFAULT_24_AIO 0
+static int init_aio_26(void)
+{
+	int err;
+
+	if (io_setup(256, &ctx)) {
+		err = -errno;
+		printk(UM_KERN_ERR "aio_thread failed to initialize context, "
+		       "err = %d\n", errno);
+		return err;
+	}
+
+	err = run_helper_thread(aio_thread, NULL,
+				CLONE_FILES | CLONE_VM, &aio_stack);
+	if (err < 0)
+		return err;
+
+	aio_pid = err;
+
+	printk(UM_KERN_INFO "Using 2.6 host AIO\n");
+	return 0;
+}
+
+static int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len,
+			 unsigned long long offset, struct aio_context *aio)
+{
+	struct aio_thread_reply reply;
+	int err;
+
+	err = do_aio(ctx, type, io_fd, buf, len, offset, aio);
+	if (err) {
+		reply = ((struct aio_thread_reply) { .data = aio,
+					 .err  = err });
+		err = write(aio->reply_fd, &reply, sizeof(reply));
+		if (err != sizeof(reply)) {
+			err = -errno;
+			printk(UM_KERN_ERR "submit_aio_26 - write failed, "
+			       "fd = %d, err = %d\n", aio->reply_fd, -err);
+		}
+		else err = 0;
+	}
+
+	return err;
+}
+
+#else
+#define DEFAULT_24_AIO 1
+static int init_aio_26(void)
+{
+	return -ENOSYS;
+}
+
+static int submit_aio_26(enum aio_type type, int io_fd, char *buf, int len,
+			 unsigned long long offset, struct aio_context *aio)
+{
+	return -ENOSYS;
+}
+#endif
+
+/* Initialized in an initcall and unchanged thereafter */
+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;
+
+	if (!aio_24) {
+		err = init_aio_26();
+		if (err && (errno == ENOSYS)) {
+			printk(UM_KERN_INFO "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);
+		free_stack(aio_stack, 0);
+	}
+}
+
+__uml_exitcall(exit_aio);
+
+static int submit_aio_24(enum aio_type type, int io_fd, char *buf, int len,
+			 unsigned long long offset, struct aio_context *aio)
+{
+	struct aio_thread_req req = { .type 		= type,
+				      .io_fd		= io_fd,
+				      .offset		= offset,
+				      .buf		= buf,
+				      .len		= len,
+				      .aio		= aio,
+	};
+	int err;
+
+	err = write(aio_req_fd_w, &req, sizeof(req));
+	if (err == sizeof(req))
+		err = 0;
+	else err = -errno;
+
+	return err;
+}
+
+int submit_aio(enum aio_type type, int io_fd, char *buf, int len,
+	       unsigned long long offset, int reply_fd,
+	       struct aio_context *aio)
+{
+	aio->reply_fd = reply_fd;
+	if (aio_24)
+		return submit_aio_24(type, io_fd, buf, len, offset, aio);
+	else
+		return submit_aio_26(type, io_fd, buf, len, offset, aio);
+}
diff --git a/arch/um/os-Linux/drivers/Makefile b/arch/um/os-Linux/drivers/Makefile
new file mode 100644
index 0000000..6c546dc
--- /dev/null
+++ b/arch/um/os-Linux/drivers/Makefile
@@ -0,0 +1,13 @@
+# 
+# Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+ethertap-objs := ethertap_kern.o ethertap_user.o
+tuntap-objs := tuntap_kern.o tuntap_user.o
+
+obj-y = 
+obj-$(CONFIG_UML_NET_ETHERTAP) += ethertap.o
+obj-$(CONFIG_UML_NET_TUNTAP) += tuntap.o
+
+include arch/um/scripts/Makefile.rules
diff --git a/arch/um/os-Linux/drivers/etap.h b/arch/um/os-Linux/drivers/etap.h
new file mode 100644
index 0000000..54183a6
--- /dev/null
+++ b/arch/um/os-Linux/drivers/etap.h
@@ -0,0 +1,21 @@
+/* 
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __DRIVERS_ETAP_H
+#define __DRIVERS_ETAP_H
+
+#include <net_user.h>
+
+struct ethertap_data {
+	char *dev_name;
+	char *gate_addr;
+	int data_fd;
+	int control_fd;
+	void *dev;
+};
+
+extern const struct net_user_info ethertap_user_info;
+
+#endif
diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c
new file mode 100644
index 0000000..f424600
--- /dev/null
+++ b/arch/um/os-Linux/drivers/ethertap_kern.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
+ * James Leu (jleu@mindspring.net).
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Copyright (C) 2001 by various other people who didn't put their name here.
+ * Licensed under the GPL.
+ */
+
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "etap.h"
+#include <net_kern.h>
+
+struct ethertap_init {
+	char *dev_name;
+	char *gate_addr;
+};
+
+static void etap_init(struct net_device *dev, void *data)
+{
+	struct uml_net_private *pri;
+	struct ethertap_data *epri;
+	struct ethertap_init *init = data;
+
+	pri = netdev_priv(dev);
+	epri = (struct ethertap_data *) pri->user;
+	epri->dev_name = init->dev_name;
+	epri->gate_addr = init->gate_addr;
+	epri->data_fd = -1;
+	epri->control_fd = -1;
+	epri->dev = dev;
+
+	printk(KERN_INFO "ethertap backend - %s", epri->dev_name);
+	if (epri->gate_addr != NULL)
+		printk(KERN_CONT ", IP = %s", epri->gate_addr);
+	printk(KERN_CONT "\n");
+}
+
+static int etap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
+{
+	int len;
+
+	len = net_recvfrom(fd, skb_mac_header(skb),
+			   skb->dev->mtu + 2 + ETH_HEADER_ETHERTAP);
+	if (len <= 0)
+		return(len);
+
+	skb_pull(skb, 2);
+	len -= 2;
+	return len;
+}
+
+static int etap_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
+{
+	skb_push(skb, 2);
+	return net_send(fd, skb->data, skb->len);
+}
+
+const struct net_kern_info ethertap_kern_info = {
+	.init			= etap_init,
+	.protocol		= eth_protocol,
+	.read			= etap_read,
+	.write 			= etap_write,
+};
+
+int ethertap_setup(char *str, char **mac_out, void *data)
+{
+	struct ethertap_init *init = data;
+
+	*init = ((struct ethertap_init)
+		{ .dev_name 	= NULL,
+		  .gate_addr 	= NULL });
+	if (tap_setup_common(str, "ethertap", &init->dev_name, mac_out,
+			    &init->gate_addr))
+		return 0;
+	if (init->dev_name == NULL) {
+		printk(KERN_ERR "ethertap_setup : Missing tap device name\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct transport ethertap_transport = {
+	.list 		= LIST_HEAD_INIT(ethertap_transport.list),
+	.name 		= "ethertap",
+	.setup  	= ethertap_setup,
+	.user 		= &ethertap_user_info,
+	.kern 		= &ethertap_kern_info,
+	.private_size 	= sizeof(struct ethertap_data),
+	.setup_size 	= sizeof(struct ethertap_init),
+};
+
+static int register_ethertap(void)
+{
+	register_transport(&ethertap_transport);
+	return 0;
+}
+
+late_initcall(register_ethertap);
diff --git a/arch/um/os-Linux/drivers/ethertap_user.c b/arch/um/os-Linux/drivers/ethertap_user.c
new file mode 100644
index 0000000..6d49182
--- /dev/null
+++ b/arch/um/os-Linux/drivers/ethertap_user.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
+ * James Leu (jleu@mindspring.net).
+ * Copyright (C) 2001 by various other people who didn't put their name here.
+ * Licensed under the GPL.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include "etap.h"
+#include <os.h>
+#include <net_user.h>
+#include <um_malloc.h>
+
+#define MAX_PACKET ETH_MAX_PACKET
+
+static int etap_user_init(void *data, void *dev)
+{
+	struct ethertap_data *pri = data;
+
+	pri->dev = dev;
+	return 0;
+}
+
+struct addr_change {
+	enum { ADD_ADDR, DEL_ADDR } what;
+	unsigned char addr[4];
+	unsigned char netmask[4];
+};
+
+static void etap_change(int op, unsigned char *addr, unsigned char *netmask,
+			int fd)
+{
+	struct addr_change change;
+	char *output;
+	int n;
+
+	change.what = op;
+	memcpy(change.addr, addr, sizeof(change.addr));
+	memcpy(change.netmask, netmask, sizeof(change.netmask));
+	CATCH_EINTR(n = write(fd, &change, sizeof(change)));
+	if (n != sizeof(change)) {
+		printk(UM_KERN_ERR "etap_change - request failed, err = %d\n",
+		       errno);
+		return;
+	}
+
+	output = uml_kmalloc(UM_KERN_PAGE_SIZE, UM_GFP_KERNEL);
+	if (output == NULL)
+		printk(UM_KERN_ERR "etap_change : Failed to allocate output "
+		       "buffer\n");
+	read_output(fd, output, UM_KERN_PAGE_SIZE);
+	if (output != NULL) {
+		printk("%s", output);
+		kfree(output);
+	}
+}
+
+static void etap_open_addr(unsigned char *addr, unsigned char *netmask,
+			   void *arg)
+{
+	etap_change(ADD_ADDR, addr, netmask, *((int *) arg));
+}
+
+static void etap_close_addr(unsigned char *addr, unsigned char *netmask,
+			    void *arg)
+{
+	etap_change(DEL_ADDR, addr, netmask, *((int *) arg));
+}
+
+struct etap_pre_exec_data {
+	int control_remote;
+	int control_me;
+	int data_me;
+};
+
+static void etap_pre_exec(void *arg)
+{
+	struct etap_pre_exec_data *data = arg;
+
+	dup2(data->control_remote, 1);
+	close(data->data_me);
+	close(data->control_me);
+}
+
+static int etap_tramp(char *dev, char *gate, int control_me,
+		      int control_remote, int data_me, int data_remote)
+{
+	struct etap_pre_exec_data pe_data;
+	int pid, err, n;
+	char version_buf[sizeof("nnnnn\0")];
+	char data_fd_buf[sizeof("nnnnnn\0")];
+	char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")];
+	char *setup_args[] = { "uml_net", version_buf, "ethertap", dev,
+			       data_fd_buf, gate_buf, NULL };
+	char *nosetup_args[] = { "uml_net", version_buf, "ethertap",
+				 dev, data_fd_buf, NULL };
+	char **args, c;
+
+	sprintf(data_fd_buf, "%d", data_remote);
+	sprintf(version_buf, "%d", UML_NET_VERSION);
+	if (gate != NULL) {
+		strncpy(gate_buf, gate, 15);
+		args = setup_args;
+	}
+	else args = nosetup_args;
+
+	err = 0;
+	pe_data.control_remote = control_remote;
+	pe_data.control_me = control_me;
+	pe_data.data_me = data_me;
+	pid = run_helper(etap_pre_exec, &pe_data, args);
+
+	if (pid < 0)
+		err = pid;
+	close(data_remote);
+	close(control_remote);
+	CATCH_EINTR(n = read(control_me, &c, sizeof(c)));
+	if (n != sizeof(c)) {
+		err = -errno;
+		printk(UM_KERN_ERR "etap_tramp : read of status failed, "
+		       "err = %d\n", -err);
+		return err;
+	}
+	if (c != 1) {
+		printk(UM_KERN_ERR "etap_tramp : uml_net failed\n");
+		err = helper_wait(pid);
+	}
+	return err;
+}
+
+static int etap_open(void *data)
+{
+	struct ethertap_data *pri = data;
+	char *output;
+	int data_fds[2], control_fds[2], err, output_len;
+
+	err = tap_open_common(pri->dev, pri->gate_addr);
+	if (err)
+		return err;
+
+	err = socketpair(AF_UNIX, SOCK_DGRAM, 0, data_fds);
+	if (err) {
+		err = -errno;
+		printk(UM_KERN_ERR "etap_open - data socketpair failed - "
+		       "err = %d\n", errno);
+		return err;
+	}
+
+	err = socketpair(AF_UNIX, SOCK_STREAM, 0, control_fds);
+	if (err) {
+		err = -errno;
+		printk(UM_KERN_ERR "etap_open - control socketpair failed - "
+		       "err = %d\n", errno);
+		goto out_close_data;
+	}
+
+	err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0],
+			 control_fds[1], data_fds[0], data_fds[1]);
+	output_len = UM_KERN_PAGE_SIZE;
+	output = uml_kmalloc(output_len, UM_GFP_KERNEL);
+	read_output(control_fds[0], output, output_len);
+
+	if (output == NULL)
+		printk(UM_KERN_ERR "etap_open : failed to allocate output "
+		       "buffer\n");
+	else {
+		printk("%s", output);
+		kfree(output);
+	}
+
+	if (err < 0) {
+		printk(UM_KERN_ERR "etap_tramp failed - err = %d\n", -err);
+		goto out_close_control;
+	}
+
+	pri->data_fd = data_fds[0];
+	pri->control_fd = control_fds[0];
+	iter_addresses(pri->dev, etap_open_addr, &pri->control_fd);
+	return data_fds[0];
+
+out_close_control:
+	close(control_fds[0]);
+	close(control_fds[1]);
+out_close_data:
+	close(data_fds[0]);
+	close(data_fds[1]);
+	return err;
+}
+
+static void etap_close(int fd, void *data)
+{
+	struct ethertap_data *pri = data;
+
+	iter_addresses(pri->dev, etap_close_addr, &pri->control_fd);
+	close(fd);
+
+	if (shutdown(pri->data_fd, SHUT_RDWR) < 0)
+		printk(UM_KERN_ERR "etap_close - shutdown data socket failed, "
+		       "errno = %d\n", errno);
+
+	if (shutdown(pri->control_fd, SHUT_RDWR) < 0)
+		printk(UM_KERN_ERR "etap_close - shutdown control socket "
+		       "failed, errno = %d\n", errno);
+
+	close(pri->data_fd);
+	pri->data_fd = -1;
+	close(pri->control_fd);
+	pri->control_fd = -1;
+}
+
+static void etap_add_addr(unsigned char *addr, unsigned char *netmask,
+			  void *data)
+{
+	struct ethertap_data *pri = data;
+
+	tap_check_ips(pri->gate_addr, addr);
+	if (pri->control_fd == -1)
+		return;
+	etap_open_addr(addr, netmask, &pri->control_fd);
+}
+
+static void etap_del_addr(unsigned char *addr, unsigned char *netmask,
+			  void *data)
+{
+	struct ethertap_data *pri = data;
+
+	if (pri->control_fd == -1)
+		return;
+
+	etap_close_addr(addr, netmask, &pri->control_fd);
+}
+
+const struct net_user_info ethertap_user_info = {
+	.init		= etap_user_init,
+	.open		= etap_open,
+	.close	 	= etap_close,
+	.remove	 	= NULL,
+	.add_address	= etap_add_addr,
+	.delete_address = etap_del_addr,
+	.mtu		= ETH_MAX_PACKET,
+	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_ETHERTAP,
+};
diff --git a/arch/um/os-Linux/drivers/tuntap.h b/arch/um/os-Linux/drivers/tuntap.h
new file mode 100644
index 0000000..7367354
--- /dev/null
+++ b/arch/um/os-Linux/drivers/tuntap.h
@@ -0,0 +1,21 @@
+/* 
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_TUNTAP_H
+#define __UM_TUNTAP_H
+
+#include <net_user.h>
+
+struct tuntap_data {
+	char *dev_name;
+	int fixed_config;
+	char *gate_addr;
+	int fd;
+	void *dev;
+};
+
+extern const struct net_user_info tuntap_user_info;
+
+#endif
diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c
new file mode 100644
index 0000000..d9d56e5
--- /dev/null
+++ b/arch/um/os-Linux/drivers/tuntap_kern.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <asm/errno.h>
+#include <net_kern.h>
+#include "tuntap.h"
+
+struct tuntap_init {
+	char *dev_name;
+	char *gate_addr;
+};
+
+static void tuntap_init(struct net_device *dev, void *data)
+{
+	struct uml_net_private *pri;
+	struct tuntap_data *tpri;
+	struct tuntap_init *init = data;
+
+	pri = netdev_priv(dev);
+	tpri = (struct tuntap_data *) pri->user;
+	tpri->dev_name = init->dev_name;
+	tpri->fixed_config = (init->dev_name != NULL);
+	tpri->gate_addr = init->gate_addr;
+	tpri->fd = -1;
+	tpri->dev = dev;
+
+	printk(KERN_INFO "TUN/TAP backend - ");
+	if (tpri->gate_addr != NULL)
+		printk(KERN_CONT "IP = %s", tpri->gate_addr);
+	printk(KERN_CONT "\n");
+}
+
+static int tuntap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
+{
+	return net_read(fd, skb_mac_header(skb),
+			skb->dev->mtu + ETH_HEADER_OTHER);
+}
+
+static int tuntap_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
+{
+	return net_write(fd, skb->data, skb->len);
+}
+
+const struct net_kern_info tuntap_kern_info = {
+	.init			= tuntap_init,
+	.protocol		= eth_protocol,
+	.read			= tuntap_read,
+	.write 			= tuntap_write,
+};
+
+int tuntap_setup(char *str, char **mac_out, void *data)
+{
+	struct tuntap_init *init = data;
+
+	*init = ((struct tuntap_init)
+		{ .dev_name 	= NULL,
+		  .gate_addr 	= NULL });
+	if (tap_setup_common(str, "tuntap", &init->dev_name, mac_out,
+			    &init->gate_addr))
+		return 0;
+
+	return 1;
+}
+
+static struct transport tuntap_transport = {
+	.list 		= LIST_HEAD_INIT(tuntap_transport.list),
+	.name 		= "tuntap",
+	.setup  	= tuntap_setup,
+	.user 		= &tuntap_user_info,
+	.kern 		= &tuntap_kern_info,
+	.private_size 	= sizeof(struct tuntap_data),
+	.setup_size 	= sizeof(struct tuntap_init),
+};
+
+static int register_tuntap(void)
+{
+	register_transport(&tuntap_transport);
+	return 0;
+}
+
+late_initcall(register_tuntap);
diff --git a/arch/um/os-Linux/drivers/tuntap_user.c b/arch/um/os-Linux/drivers/tuntap_user.c
new file mode 100644
index 0000000..db24ce0
--- /dev/null
+++ b/arch/um/os-Linux/drivers/tuntap_user.c
@@ -0,0 +1,215 @@
+/* 
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <kern_util.h>
+#include <os.h>
+#include "tuntap.h"
+
+static int tuntap_user_init(void *data, void *dev)
+{
+	struct tuntap_data *pri = data;
+
+	pri->dev = dev;
+	return 0;
+}
+
+static void tuntap_add_addr(unsigned char *addr, unsigned char *netmask,
+			    void *data)
+{
+	struct tuntap_data *pri = data;
+
+	tap_check_ips(pri->gate_addr, addr);
+	if ((pri->fd == -1) || pri->fixed_config)
+		return;
+	open_addr(addr, netmask, pri->dev_name);
+}
+
+static void tuntap_del_addr(unsigned char *addr, unsigned char *netmask,
+			    void *data)
+{
+	struct tuntap_data *pri = data;
+
+	if ((pri->fd == -1) || pri->fixed_config)
+		return;
+	close_addr(addr, netmask, pri->dev_name);
+}
+
+struct tuntap_pre_exec_data {
+	int stdout_fd;
+	int close_me;
+};
+
+static void tuntap_pre_exec(void *arg)
+{
+	struct tuntap_pre_exec_data *data = arg;
+
+	dup2(data->stdout_fd, 1);
+	close(data->close_me);
+}
+
+static int tuntap_open_tramp(char *gate, int *fd_out, int me, int remote,
+			     char *buffer, int buffer_len, int *used_out)
+{
+	struct tuntap_pre_exec_data data;
+	char version_buf[sizeof("nnnnn\0")];
+	char *argv[] = { "uml_net", version_buf, "tuntap", "up", gate,
+			 NULL };
+	char buf[CMSG_SPACE(sizeof(*fd_out))];
+	struct msghdr msg;
+	struct cmsghdr *cmsg;
+	struct iovec iov;
+	int pid, n, err;
+
+	sprintf(version_buf, "%d", UML_NET_VERSION);
+
+	data.stdout_fd = remote;
+	data.close_me = me;
+
+	pid = run_helper(tuntap_pre_exec, &data, argv);
+
+	if (pid < 0)
+		return pid;
+
+	close(remote);
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	if (buffer != NULL) {
+		iov = ((struct iovec) { buffer, buffer_len });
+		msg.msg_iov = &iov;
+		msg.msg_iovlen = 1;
+	}
+	else {
+		msg.msg_iov = NULL;
+		msg.msg_iovlen = 0;
+	}
+	msg.msg_control = buf;
+	msg.msg_controllen = sizeof(buf);
+	msg.msg_flags = 0;
+	n = recvmsg(me, &msg, 0);
+	*used_out = n;
+	if (n < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "tuntap_open_tramp : recvmsg failed - "
+		       "errno = %d\n", errno);
+		return err;
+	}
+	helper_wait(pid);
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if (cmsg == NULL) {
+		printk(UM_KERN_ERR "tuntap_open_tramp : didn't receive a "
+		       "message\n");
+		return -EINVAL;
+	}
+	if ((cmsg->cmsg_level != SOL_SOCKET) ||
+	   (cmsg->cmsg_type != SCM_RIGHTS)) {
+		printk(UM_KERN_ERR "tuntap_open_tramp : didn't receive a "
+		       "descriptor\n");
+		return -EINVAL;
+	}
+	*fd_out = ((int *) CMSG_DATA(cmsg))[0];
+	os_set_exec_close(*fd_out);
+	return 0;
+}
+
+static int tuntap_open(void *data)
+{
+	struct ifreq ifr;
+	struct tuntap_data *pri = data;
+	char *output, *buffer;
+	int err, fds[2], len, used;
+
+	err = tap_open_common(pri->dev, pri->gate_addr);
+	if (err < 0)
+		return err;
+
+	if (pri->fixed_config) {
+		pri->fd = os_open_file("/dev/net/tun",
+				       of_cloexec(of_rdwr(OPENFLAGS())), 0);
+		if (pri->fd < 0) {
+			printk(UM_KERN_ERR "Failed to open /dev/net/tun, "
+			       "err = %d\n", -pri->fd);
+			return pri->fd;
+		}
+		memset(&ifr, 0, sizeof(ifr));
+		ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+		strlcpy(ifr.ifr_name, pri->dev_name, sizeof(ifr.ifr_name));
+		if (ioctl(pri->fd, TUNSETIFF, &ifr) < 0) {
+			err = -errno;
+			printk(UM_KERN_ERR "TUNSETIFF failed, errno = %d\n",
+			       errno);
+			close(pri->fd);
+			return err;
+		}
+	}
+	else {
+		err = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds);
+		if (err) {
+			err = -errno;
+			printk(UM_KERN_ERR "tuntap_open : socketpair failed - "
+			       "errno = %d\n", errno);
+			return err;
+		}
+
+		buffer = get_output_buffer(&len);
+		if (buffer != NULL)
+			len--;
+		used = 0;
+
+		err = tuntap_open_tramp(pri->gate_addr, &pri->fd, fds[0],
+					fds[1], buffer, len, &used);
+
+		output = buffer;
+		if (err < 0) {
+			printk("%s", output);
+			free_output_buffer(buffer);
+			printk(UM_KERN_ERR "tuntap_open_tramp failed - "
+			       "err = %d\n", -err);
+			return err;
+		}
+
+		pri->dev_name = uml_strdup(buffer);
+		output += IFNAMSIZ;
+		printk("%s", output);
+		free_output_buffer(buffer);
+
+		close(fds[0]);
+		iter_addresses(pri->dev, open_addr, pri->dev_name);
+	}
+
+	return pri->fd;
+}
+
+static void tuntap_close(int fd, void *data)
+{
+	struct tuntap_data *pri = data;
+
+	if (!pri->fixed_config)
+		iter_addresses(pri->dev, close_addr, pri->dev_name);
+	close(fd);
+	pri->fd = -1;
+}
+
+const struct net_user_info tuntap_user_info = {
+	.init		= tuntap_user_init,
+	.open		= tuntap_open,
+	.close	 	= tuntap_close,
+	.remove	 	= NULL,
+	.add_address	= tuntap_add_addr,
+	.delete_address = tuntap_del_addr,
+	.mtu		= ETH_MAX_PACKET,
+	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_OTHER,
+};
diff --git a/arch/um/os-Linux/elf_aux.c b/arch/um/os-Linux/elf_aux.c
new file mode 100644
index 0000000..77a9321
--- /dev/null
+++ b/arch/um/os-Linux/elf_aux.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  arch/um/kernel/elf_aux.c
+ *
+ *  Scan the Elf auxiliary vector provided by the host to extract
+ *  information about vsyscall-page, etc.
+ *
+ *  Copyright (C) 2004 Fujitsu Siemens Computers GmbH
+ *  Author: Bodo Stroesser (bodo.stroesser@fujitsu-siemens.com)
+ */
+#include <elf.h>
+#include <stddef.h>
+#include <init.h>
+#include <elf_user.h>
+#include <mem_user.h>
+
+typedef Elf32_auxv_t elf_auxv_t;
+
+/* These are initialized very early in boot and never changed */
+char * elf_aux_platform;
+extern long elf_aux_hwcap;
+unsigned long vsyscall_ehdr;
+unsigned long vsyscall_end;
+unsigned long __kernel_vsyscall;
+
+__init void scan_elf_aux( char **envp)
+{
+	long page_size = 0;
+	elf_auxv_t * auxv;
+
+	while ( *envp++ != NULL) ;
+
+	for ( auxv = (elf_auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) {
+		switch ( auxv->a_type ) {
+			case AT_SYSINFO:
+				__kernel_vsyscall = auxv->a_un.a_val;
+				/* See if the page is under TASK_SIZE */
+				if (__kernel_vsyscall < (unsigned long) envp)
+					__kernel_vsyscall = 0;
+				break;
+			case AT_SYSINFO_EHDR:
+				vsyscall_ehdr = auxv->a_un.a_val;
+				/* See if the page is under TASK_SIZE */
+				if (vsyscall_ehdr < (unsigned long) envp)
+					vsyscall_ehdr = 0;
+				break;
+			case AT_HWCAP:
+				elf_aux_hwcap = auxv->a_un.a_val;
+				break;
+			case AT_PLATFORM:
+                                /* elf.h removed the pointer elements from
+                                 * a_un, so we have to use a_val, which is
+                                 * all that's left.
+                                 */
+				elf_aux_platform =
+					(char *) (long) auxv->a_un.a_val;
+				break;
+			case AT_PAGESZ:
+				page_size = auxv->a_un.a_val;
+				break;
+		}
+	}
+	if ( ! __kernel_vsyscall || ! vsyscall_ehdr ||
+	     ! elf_aux_hwcap || ! elf_aux_platform ||
+	     ! page_size || (vsyscall_ehdr % page_size) ) {
+		__kernel_vsyscall = 0;
+		vsyscall_ehdr = 0;
+		elf_aux_hwcap = 0;
+		elf_aux_platform = "i586";
+	}
+	else {
+		vsyscall_end = vsyscall_ehdr + page_size;
+	}
+}
diff --git a/arch/um/os-Linux/execvp.c b/arch/um/os-Linux/execvp.c
new file mode 100644
index 0000000..84a0777
--- /dev/null
+++ b/arch/um/os-Linux/execvp.c
@@ -0,0 +1,149 @@
+/* Copyright (C) 2006 by Paolo Giarrusso - modified from glibc' execvp.c.
+   Original copyright notice follows:
+
+   Copyright (C) 1991,92,1995-99,2002,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+#include <unistd.h>
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef TEST
+#include <um_malloc.h>
+#else
+#include <stdio.h>
+#define um_kmalloc malloc
+#endif
+#include <os.h>
+
+/* Execute FILE, searching in the `PATH' environment variable if it contains
+   no slashes, with arguments ARGV and environment from `environ'.  */
+int execvp_noalloc(char *buf, const char *file, char *const argv[])
+{
+	if (*file == '\0') {
+		return -ENOENT;
+	}
+
+	if (strchr (file, '/') != NULL) {
+		/* Don't search when it contains a slash.  */
+		execv(file, argv);
+	} else {
+		int got_eacces;
+		size_t len, pathlen;
+		char *name, *p;
+		char *path = getenv("PATH");
+		if (path == NULL)
+			path = ":/bin:/usr/bin";
+
+		len = strlen(file) + 1;
+		pathlen = strlen(path);
+		/* Copy the file name at the top.  */
+		name = memcpy(buf + pathlen + 1, file, len);
+		/* And add the slash.  */
+		*--name = '/';
+
+		got_eacces = 0;
+		p = path;
+		do {
+			char *startp;
+
+			path = p;
+			//Let's avoid this GNU extension.
+			//p = strchrnul (path, ':');
+			p = strchr(path, ':');
+			if (!p)
+				p = strchr(path, '\0');
+
+			if (p == path)
+				/* Two adjacent colons, or a colon at the beginning or the end
+				   of `PATH' means to search the current directory.  */
+				startp = name + 1;
+			else
+				startp = memcpy(name - (p - path), path, p - path);
+
+			/* Try to execute this name.  If it works, execv will not return.  */
+			execv(startp, argv);
+
+			/*
+			if (errno == ENOEXEC) {
+			}
+			*/
+
+			switch (errno) {
+				case EACCES:
+					/* Record the we got a `Permission denied' error.  If we end
+					   up finding no executable we can use, we want to diagnose
+					   that we did find one but were denied access.  */
+					got_eacces = 1;
+				case ENOENT:
+				case ESTALE:
+				case ENOTDIR:
+					/* Those errors indicate the file is missing or not executable
+					   by us, in which case we want to just try the next path
+					   directory.  */
+				case ENODEV:
+				case ETIMEDOUT:
+					/* Some strange filesystems like AFS return even
+					   stranger error numbers.  They cannot reasonably mean
+					   anything else so ignore those, too.  */
+				case ENOEXEC:
+					/* We won't go searching for the shell
+					 * if it is not executable - the Linux
+					 * kernel already handles this enough,
+					 * for us. */
+					break;
+
+				default:
+					/* Some other error means we found an executable file, but
+					   something went wrong executing it; return the error to our
+					   caller.  */
+					return -errno;
+			}
+		} while (*p++ != '\0');
+
+		/* We tried every element and none of them worked.  */
+		if (got_eacces)
+			/* At least one failure was due to permissions, so report that
+			   error.  */
+			return -EACCES;
+	}
+
+	/* Return the error from the last attempt (probably ENOENT).  */
+	return -errno;
+}
+#ifdef TEST
+int main(int argc, char**argv)
+{
+	char buf[PATH_MAX];
+	int ret;
+	argc--;
+	if (!argc) {
+		os_warn("Not enough arguments\n");
+		return 1;
+	}
+	argv++;
+	if (ret = execvp_noalloc(buf, argv[0], argv)) {
+		errno = -ret;
+		perror("execvp_noalloc");
+	}
+	return 0;
+}
+#endif
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
new file mode 100644
index 0000000..c019709
--- /dev/null
+++ b/arch/um/os-Linux/file.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <os.h>
+
+static void copy_stat(struct uml_stat *dst, const struct stat64 *src)
+{
+	*dst = ((struct uml_stat) {
+		.ust_dev     = src->st_dev,     /* device */
+		.ust_ino     = src->st_ino,     /* inode */
+		.ust_mode    = src->st_mode,    /* protection */
+		.ust_nlink   = src->st_nlink,   /* number of hard links */
+		.ust_uid     = src->st_uid,     /* user ID of owner */
+		.ust_gid     = src->st_gid,     /* group ID of owner */
+		.ust_size    = src->st_size,    /* total size, in bytes */
+		.ust_blksize = src->st_blksize, /* blocksize for filesys I/O */
+		.ust_blocks  = src->st_blocks,  /* number of blocks allocated */
+		.ust_atime   = src->st_atime,   /* time of last access */
+		.ust_mtime   = src->st_mtime,   /* time of last modification */
+		.ust_ctime   = src->st_ctime,   /* time of last change */
+	});
+}
+
+int os_stat_fd(const int fd, struct uml_stat *ubuf)
+{
+	struct stat64 sbuf;
+	int err;
+
+	CATCH_EINTR(err = fstat64(fd, &sbuf));
+	if (err < 0)
+		return -errno;
+
+	if (ubuf != NULL)
+		copy_stat(ubuf, &sbuf);
+	return err;
+}
+
+int os_stat_file(const char *file_name, struct uml_stat *ubuf)
+{
+	struct stat64 sbuf;
+	int err;
+
+	CATCH_EINTR(err = stat64(file_name, &sbuf));
+	if (err < 0)
+		return -errno;
+
+	if (ubuf != NULL)
+		copy_stat(ubuf, &sbuf);
+	return err;
+}
+
+int os_access(const char *file, int mode)
+{
+	int amode, err;
+
+	amode = (mode & OS_ACC_R_OK ? R_OK : 0) |
+		(mode & OS_ACC_W_OK ? W_OK : 0) |
+		(mode & OS_ACC_X_OK ? X_OK : 0) |
+		(mode & OS_ACC_F_OK ? F_OK : 0);
+
+	err = access(file, amode);
+	if (err < 0)
+		return -errno;
+
+	return 0;
+}
+
+/* FIXME? required only by hostaudio (because it passes ioctls verbatim) */
+int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg)
+{
+	int err;
+
+	err = ioctl(fd, cmd, arg);
+	if (err < 0)
+		return -errno;
+
+	return err;
+}
+
+/* FIXME: ensure namebuf in os_get_if_name is big enough */
+int os_get_ifname(int fd, char* namebuf)
+{
+	if (ioctl(fd, SIOCGIFNAME, namebuf) < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_set_slip(int fd)
+{
+	int disc, sencap;
+
+	disc = N_SLIP;
+	if (ioctl(fd, TIOCSETD, &disc) < 0)
+		return -errno;
+
+	sencap = 0;
+	if (ioctl(fd, SIOCSIFENCAP, &sencap) < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_mode_fd(int fd, int mode)
+{
+	int err;
+
+	CATCH_EINTR(err = fchmod(fd, mode));
+	if (err < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_file_type(char *file)
+{
+	struct uml_stat buf;
+	int err;
+
+	err = os_stat_file(file, &buf);
+	if (err < 0)
+		return err;
+
+	if (S_ISDIR(buf.ust_mode))
+		return OS_TYPE_DIR;
+	else if (S_ISLNK(buf.ust_mode))
+		return OS_TYPE_SYMLINK;
+	else if (S_ISCHR(buf.ust_mode))
+		return OS_TYPE_CHARDEV;
+	else if (S_ISBLK(buf.ust_mode))
+		return OS_TYPE_BLOCKDEV;
+	else if (S_ISFIFO(buf.ust_mode))
+		return OS_TYPE_FIFO;
+	else if (S_ISSOCK(buf.ust_mode))
+		return OS_TYPE_SOCK;
+	else return OS_TYPE_FILE;
+}
+
+int os_file_mode(const char *file, struct openflags *mode_out)
+{
+	int err;
+
+	*mode_out = OPENFLAGS();
+
+	err = access(file, W_OK);
+	if (err && (errno != EACCES))
+		return -errno;
+	else if (!err)
+		*mode_out = of_write(*mode_out);
+
+	err = access(file, R_OK);
+	if (err && (errno != EACCES))
+		return -errno;
+	else if (!err)
+		*mode_out = of_read(*mode_out);
+
+	return err;
+}
+
+int os_open_file(const char *file, struct openflags flags, int mode)
+{
+	int fd, err, f = 0;
+
+	if (flags.r && flags.w)
+		f = O_RDWR;
+	else if (flags.r)
+		f = O_RDONLY;
+	else if (flags.w)
+		f = O_WRONLY;
+	else f = 0;
+
+	if (flags.s)
+		f |= O_SYNC;
+	if (flags.c)
+		f |= O_CREAT;
+	if (flags.t)
+		f |= O_TRUNC;
+	if (flags.e)
+		f |= O_EXCL;
+	if (flags.a)
+		f |= O_APPEND;
+
+	fd = open64(file, f, mode);
+	if (fd < 0)
+		return -errno;
+
+	if (flags.cl && fcntl(fd, F_SETFD, 1)) {
+		err = -errno;
+		close(fd);
+		return err;
+	}
+
+	return fd;
+}
+
+int os_connect_socket(const char *name)
+{
+	struct sockaddr_un sock;
+	int fd, err;
+
+	sock.sun_family = AF_UNIX;
+	snprintf(sock.sun_path, sizeof(sock.sun_path), "%s", name);
+
+	fd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0) {
+		err = -errno;
+		goto out;
+	}
+
+	err = connect(fd, (struct sockaddr *) &sock, sizeof(sock));
+	if (err) {
+		err = -errno;
+		goto out_close;
+	}
+
+	return fd;
+
+out_close:
+	close(fd);
+out:
+	return err;
+}
+
+void os_close_file(int fd)
+{
+	close(fd);
+}
+int os_fsync_file(int fd)
+{
+	if (fsync(fd) < 0)
+	    return -errno;
+	return 0;
+}
+
+int os_seek_file(int fd, unsigned long long offset)
+{
+	unsigned long long actual;
+
+	actual = lseek64(fd, offset, SEEK_SET);
+	if (actual != offset)
+		return -errno;
+	return 0;
+}
+
+int os_read_file(int fd, void *buf, int len)
+{
+	int n = read(fd, buf, len);
+
+	if (n < 0)
+		return -errno;
+	return n;
+}
+
+int os_pread_file(int fd, void *buf, int len, unsigned long long offset)
+{
+	int n = pread(fd, buf, len, offset);
+
+	if (n < 0)
+		return -errno;
+	return n;
+}
+
+int os_write_file(int fd, const void *buf, int len)
+{
+	int n = write(fd, (void *) buf, len);
+
+	if (n < 0)
+		return -errno;
+	return n;
+}
+
+int os_sync_file(int fd)
+{
+	int n = fsync(fd);
+
+	if (n < 0)
+		return -errno;
+	return n;
+}
+
+int os_pwrite_file(int fd, const void *buf, int len, unsigned long long offset)
+{
+	int n = pwrite(fd, (void *) buf, len, offset);
+
+	if (n < 0)
+		return -errno;
+	return n;
+}
+
+
+int os_file_size(const char *file, unsigned long long *size_out)
+{
+	struct uml_stat buf;
+	int err;
+
+	err = os_stat_file(file, &buf);
+	if (err < 0) {
+		printk(UM_KERN_ERR "Couldn't stat \"%s\" : err = %d\n", file,
+		       -err);
+		return err;
+	}
+
+	if (S_ISBLK(buf.ust_mode)) {
+		int fd;
+		long blocks;
+
+		fd = open(file, O_RDONLY, 0);
+		if (fd < 0) {
+			err = -errno;
+			printk(UM_KERN_ERR "Couldn't open \"%s\", "
+			       "errno = %d\n", file, errno);
+			return err;
+		}
+		if (ioctl(fd, BLKGETSIZE, &blocks) < 0) {
+			err = -errno;
+			printk(UM_KERN_ERR "Couldn't get the block size of "
+			       "\"%s\", errno = %d\n", file, errno);
+			close(fd);
+			return err;
+		}
+		*size_out = ((long long) blocks) * 512;
+		close(fd);
+	}
+	else *size_out = buf.ust_size;
+
+	return 0;
+}
+
+int os_file_modtime(const char *file, unsigned long *modtime)
+{
+	struct uml_stat buf;
+	int err;
+
+	err = os_stat_file(file, &buf);
+	if (err < 0) {
+		printk(UM_KERN_ERR "Couldn't stat \"%s\" : err = %d\n", file,
+		       -err);
+		return err;
+	}
+
+	*modtime = buf.ust_mtime;
+	return 0;
+}
+
+int os_set_exec_close(int fd)
+{
+	int err;
+
+	CATCH_EINTR(err = fcntl(fd, F_SETFD, FD_CLOEXEC));
+
+	if (err < 0)
+		return -errno;
+	return err;
+}
+
+int os_pipe(int *fds, int stream, int close_on_exec)
+{
+	int err, type = stream ? SOCK_STREAM : SOCK_DGRAM;
+
+	err = socketpair(AF_UNIX, type, 0, fds);
+	if (err < 0)
+		return -errno;
+
+	if (!close_on_exec)
+		return 0;
+
+	err = os_set_exec_close(fds[0]);
+	if (err < 0)
+		goto error;
+
+	err = os_set_exec_close(fds[1]);
+	if (err < 0)
+		goto error;
+
+	return 0;
+
+ error:
+	printk(UM_KERN_ERR "os_pipe : Setting FD_CLOEXEC failed, err = %d\n",
+	       -err);
+	close(fds[1]);
+	close(fds[0]);
+	return err;
+}
+
+int os_set_fd_async(int fd)
+{
+	int err, flags;
+
+	flags = fcntl(fd, F_GETFL);
+	if (flags < 0)
+		return -errno;
+
+	flags |= O_ASYNC | O_NONBLOCK;
+	if (fcntl(fd, F_SETFL, flags) < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "os_set_fd_async : failed to set O_ASYNC "
+		       "and O_NONBLOCK on fd # %d, errno = %d\n", fd, errno);
+		return err;
+	}
+
+	if ((fcntl(fd, F_SETSIG, SIGIO) < 0) ||
+	    (fcntl(fd, F_SETOWN, os_getpid()) < 0)) {
+		err = -errno;
+		printk(UM_KERN_ERR "os_set_fd_async : Failed to fcntl F_SETOWN "
+		       "(or F_SETSIG) fd %d, errno = %d\n", fd, errno);
+		return err;
+	}
+
+	return 0;
+}
+
+int os_clear_fd_async(int fd)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL);
+	if (flags < 0)
+		return -errno;
+
+	flags &= ~(O_ASYNC | O_NONBLOCK);
+	if (fcntl(fd, F_SETFL, flags) < 0)
+		return -errno;
+	return 0;
+}
+
+int os_set_fd_block(int fd, int blocking)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL);
+	if (flags < 0)
+		return -errno;
+
+	if (blocking)
+		flags &= ~O_NONBLOCK;
+	else
+		flags |= O_NONBLOCK;
+
+	if (fcntl(fd, F_SETFL, flags) < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_accept_connection(int fd)
+{
+	int new;
+
+	new = accept(fd, NULL, 0);
+	if (new < 0)
+		return -errno;
+	return new;
+}
+
+#ifndef SHUT_RD
+#define SHUT_RD 0
+#endif
+
+#ifndef SHUT_WR
+#define SHUT_WR 1
+#endif
+
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
+
+int os_shutdown_socket(int fd, int r, int w)
+{
+	int what, err;
+
+	if (r && w)
+		what = SHUT_RDWR;
+	else if (r)
+		what = SHUT_RD;
+	else if (w)
+		what = SHUT_WR;
+	else
+		return -EINVAL;
+
+	err = shutdown(fd, what);
+	if (err < 0)
+		return -errno;
+	return 0;
+}
+
+int os_rcv_fd(int fd, int *helper_pid_out)
+{
+	int new, n;
+	char buf[CMSG_SPACE(sizeof(new))];
+	struct msghdr msg;
+	struct cmsghdr *cmsg;
+	struct iovec iov;
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	iov = ((struct iovec) { .iov_base  = helper_pid_out,
+				.iov_len   = sizeof(*helper_pid_out) });
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = buf;
+	msg.msg_controllen = sizeof(buf);
+	msg.msg_flags = 0;
+
+	n = recvmsg(fd, &msg, 0);
+	if (n < 0)
+		return -errno;
+	else if (n != iov.iov_len)
+		*helper_pid_out = -1;
+
+	cmsg = CMSG_FIRSTHDR(&msg);
+	if (cmsg == NULL) {
+		printk(UM_KERN_ERR "rcv_fd didn't receive anything, "
+		       "error = %d\n", errno);
+		return -1;
+	}
+	if ((cmsg->cmsg_level != SOL_SOCKET) ||
+	    (cmsg->cmsg_type != SCM_RIGHTS)) {
+		printk(UM_KERN_ERR "rcv_fd didn't receive a descriptor\n");
+		return -1;
+	}
+
+	new = ((int *) CMSG_DATA(cmsg))[0];
+	return new;
+}
+
+int os_create_unix_socket(const char *file, int len, int close_on_exec)
+{
+	struct sockaddr_un addr;
+	int sock, err;
+
+	sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (sock < 0)
+		return -errno;
+
+	if (close_on_exec) {
+		err = os_set_exec_close(sock);
+		if (err < 0)
+			printk(UM_KERN_ERR "create_unix_socket : "
+			       "close_on_exec failed, err = %d", -err);
+	}
+
+	addr.sun_family = AF_UNIX;
+
+	snprintf(addr.sun_path, len, "%s", file);
+
+	err = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0)
+		return -errno;
+
+	return sock;
+}
+
+void os_flush_stdout(void)
+{
+	fflush(stdout);
+}
+
+int os_lock_file(int fd, int excl)
+{
+	int type = excl ? F_WRLCK : F_RDLCK;
+	struct flock lock = ((struct flock) { .l_type	= type,
+					      .l_whence	= SEEK_SET,
+					      .l_start	= 0,
+					      .l_len	= 0 } );
+	int err, save;
+
+	err = fcntl(fd, F_SETLK, &lock);
+	if (!err)
+		goto out;
+
+	save = -errno;
+	err = fcntl(fd, F_GETLK, &lock);
+	if (err) {
+		err = -errno;
+		goto out;
+	}
+
+	printk(UM_KERN_ERR "F_SETLK failed, file already locked by pid %d\n",
+	       lock.l_pid);
+	err = save;
+ out:
+	return err;
+}
+
+unsigned os_major(unsigned long long dev)
+{
+	return major(dev);
+}
+
+unsigned os_minor(unsigned long long dev)
+{
+	return minor(dev);
+}
+
+unsigned long long os_makedev(unsigned major, unsigned minor)
+{
+	return makedev(major, minor);
+}
diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c
new file mode 100644
index 0000000..3f02d42
--- /dev/null
+++ b/arch/um/os-Linux/helper.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sched.h>
+#include <linux/limits.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <kern_util.h>
+#include <os.h>
+#include <um_malloc.h>
+
+struct helper_data {
+	void (*pre_exec)(void*);
+	void *pre_data;
+	char **argv;
+	int fd;
+	char *buf;
+};
+
+static int helper_child(void *arg)
+{
+	struct helper_data *data = arg;
+	char **argv = data->argv;
+	int err, ret;
+
+	if (data->pre_exec != NULL)
+		(*data->pre_exec)(data->pre_data);
+	err = execvp_noalloc(data->buf, argv[0], argv);
+
+	/* If the exec succeeds, we don't get here */
+	CATCH_EINTR(ret = write(data->fd, &err, sizeof(err)));
+
+	return 0;
+}
+
+/* Returns either the pid of the child process we run or -E* on failure. */
+int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
+{
+	struct helper_data data;
+	unsigned long stack, sp;
+	int pid, fds[2], ret, n;
+
+	stack = alloc_stack(0, __cant_sleep());
+	if (stack == 0)
+		return -ENOMEM;
+
+	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+	if (ret < 0) {
+		ret = -errno;
+		printk(UM_KERN_ERR "run_helper : pipe failed, errno = %d\n",
+		       errno);
+		goto out_free;
+	}
+
+	ret = os_set_exec_close(fds[1]);
+	if (ret < 0) {
+		printk(UM_KERN_ERR "run_helper : setting FD_CLOEXEC failed, "
+		       "ret = %d\n", -ret);
+		goto out_close;
+	}
+
+	sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
+	data.pre_exec = pre_exec;
+	data.pre_data = pre_data;
+	data.argv = argv;
+	data.fd = fds[1];
+	data.buf = __cant_sleep() ? uml_kmalloc(PATH_MAX, UM_GFP_ATOMIC) :
+					uml_kmalloc(PATH_MAX, UM_GFP_KERNEL);
+	pid = clone(helper_child, (void *) sp, CLONE_VM, &data);
+	if (pid < 0) {
+		ret = -errno;
+		printk(UM_KERN_ERR "run_helper : clone failed, errno = %d\n",
+		       errno);
+		goto out_free2;
+	}
+
+	close(fds[1]);
+	fds[1] = -1;
+
+	/*
+	 * Read the errno value from the child, if the exec failed, or get 0 if
+	 * the exec succeeded because the pipe fd was set as close-on-exec.
+	 */
+	n = read(fds[0], &ret, sizeof(ret));
+	if (n == 0) {
+		ret = pid;
+	} else {
+		if (n < 0) {
+			n = -errno;
+			printk(UM_KERN_ERR "run_helper : read on pipe failed, "
+			       "ret = %d\n", -n);
+			ret = n;
+		}
+		CATCH_EINTR(waitpid(pid, NULL, __WALL));
+	}
+
+out_free2:
+	kfree(data.buf);
+out_close:
+	if (fds[1] != -1)
+		close(fds[1]);
+	close(fds[0]);
+out_free:
+	free_stack(stack, 0);
+	return ret;
+}
+
+int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
+		      unsigned long *stack_out)
+{
+	unsigned long stack, sp;
+	int pid, status, err;
+
+	stack = alloc_stack(0, __cant_sleep());
+	if (stack == 0)
+		return -ENOMEM;
+
+	sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
+	pid = clone(proc, (void *) sp, flags, arg);
+	if (pid < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "run_helper_thread : clone failed, "
+		       "errno = %d\n", errno);
+		return err;
+	}
+	if (stack_out == NULL) {
+		CATCH_EINTR(pid = waitpid(pid, &status, __WALL));
+		if (pid < 0) {
+			err = -errno;
+			printk(UM_KERN_ERR "run_helper_thread - wait failed, "
+			       "errno = %d\n", errno);
+			pid = err;
+		}
+		if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
+			printk(UM_KERN_ERR "run_helper_thread - thread "
+			       "returned status 0x%x\n", status);
+		free_stack(stack, 0);
+	} else
+		*stack_out = stack;
+	return pid;
+}
+
+int helper_wait(int pid)
+{
+	int ret, status;
+	int wflags = __WALL;
+
+	CATCH_EINTR(ret = waitpid(pid, &status, wflags));
+	if (ret < 0) {
+		printk(UM_KERN_ERR "helper_wait : waitpid process %d failed, "
+		       "errno = %d\n", pid, errno);
+		return -errno;
+	} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		printk(UM_KERN_ERR "helper_wait : process %d exited with "
+		       "status 0x%x\n", pid, status);
+		return -ECHILD;
+	} else
+		return 0;
+}
diff --git a/arch/um/os-Linux/irq.c b/arch/um/os-Linux/irq.c
new file mode 100644
index 0000000..3658230
--- /dev/null
+++ b/arch/um/os-Linux/irq.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 - Cambridge Greys Ltd
+ * Copyright (C) 2011 - 2014 Cisco Systems Inc
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/epoll.h>
+#include <signal.h>
+#include <string.h>
+#include <irq_user.h>
+#include <os.h>
+#include <um_malloc.h>
+
+/* Epoll support */
+
+static int epollfd = -1;
+
+#define MAX_EPOLL_EVENTS 64
+
+static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
+
+/* Helper to return an Epoll data pointer from an epoll event structure.
+ * We need to keep this one on the userspace side to keep includes separate
+ */
+
+void *os_epoll_get_data_pointer(int index)
+{
+	return epoll_events[index].data.ptr;
+}
+
+/* Helper to compare events versus the events in the epoll structure.
+ * Same as above - needs to be on the userspace side
+ */
+
+
+int os_epoll_triggered(int index, int events)
+{
+	return epoll_events[index].events & events;
+}
+/* Helper to set the event mask.
+ * The event mask is opaque to the kernel side, because it does not have
+ * access to the right includes/defines for EPOLL constants.
+ */
+
+int os_event_mask(int irq_type)
+{
+	if (irq_type == IRQ_READ)
+		return EPOLLIN | EPOLLPRI;
+	if (irq_type == IRQ_WRITE)
+		return EPOLLOUT;
+	return 0;
+}
+
+/*
+ * Initial Epoll Setup
+ */
+int os_setup_epoll(void)
+{
+	epollfd = epoll_create(MAX_EPOLL_EVENTS);
+	return epollfd;
+}
+
+/*
+ * Helper to run the actual epoll_wait
+ */
+int os_waiting_for_events_epoll(void)
+{
+	int n, err;
+
+	n = epoll_wait(epollfd,
+		(struct epoll_event *) &epoll_events, MAX_EPOLL_EVENTS, 0);
+	if (n < 0) {
+		err = -errno;
+		if (errno != EINTR)
+			printk(
+				UM_KERN_ERR "os_waiting_for_events:"
+				" epoll returned %d, error = %s\n", n,
+				strerror(errno)
+			);
+		return err;
+	}
+	return n;
+}
+
+
+/*
+ * Helper to add a fd to epoll
+ */
+int os_add_epoll_fd(int events, int fd, void *data)
+{
+	struct epoll_event event;
+	int result;
+
+	event.data.ptr = data;
+	event.events = events | EPOLLET;
+	result = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
+	if ((result) && (errno == EEXIST))
+		result = os_mod_epoll_fd(events, fd, data);
+	if (result)
+		printk("epollctl add err fd %d, %s\n", fd, strerror(errno));
+	return result;
+}
+
+/*
+ * Helper to mod the fd event mask and/or data backreference
+ */
+int os_mod_epoll_fd(int events, int fd, void *data)
+{
+	struct epoll_event event;
+	int result;
+
+	event.data.ptr = data;
+	event.events = events;
+	result = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
+	if (result)
+		printk(UM_KERN_ERR
+			"epollctl mod err fd %d, %s\n", fd, strerror(errno));
+	return result;
+}
+
+/*
+ * Helper to delete the epoll fd
+ */
+int os_del_epoll_fd(int fd)
+{
+	struct epoll_event event;
+	int result;
+	/* This is quiet as we use this as IO ON/OFF - so it is often
+	 * invoked on a non-existent fd
+	 */
+	result = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event);
+	return result;
+}
+
+void os_set_ioignore(void)
+{
+	signal(SIGIO, SIG_IGN);
+}
+
+void os_close_epoll_fd(void)
+{
+	/* Needed so we do not leak an fd when rebooting */
+	os_close_file(epollfd);
+}
diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c
new file mode 100644
index 0000000..f1fee2b
--- /dev/null
+++ b/arch/um/os-Linux/main.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <as-layout.h>
+#include <init.h>
+#include <kern_util.h>
+#include <os.h>
+#include <um_malloc.h>
+
+#define PGD_BOUND (4 * 1024 * 1024)
+#define STACKSIZE (8 * 1024 * 1024)
+#define THREAD_NAME_LEN (256)
+
+long elf_aux_hwcap;
+
+static void set_stklim(void)
+{
+	struct rlimit lim;
+
+	if (getrlimit(RLIMIT_STACK, &lim) < 0) {
+		perror("getrlimit");
+		exit(1);
+	}
+	if ((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)) {
+		lim.rlim_cur = STACKSIZE;
+		if (setrlimit(RLIMIT_STACK, &lim) < 0) {
+			perror("setrlimit");
+			exit(1);
+		}
+	}
+}
+
+static void last_ditch_exit(int sig)
+{
+	uml_cleanup();
+	exit(1);
+}
+
+static void install_fatal_handler(int sig)
+{
+	struct sigaction action;
+
+	/* All signals are enabled in this handler ... */
+	sigemptyset(&action.sa_mask);
+
+	/*
+	 * ... including the signal being handled, plus we want the
+	 * handler reset to the default behavior, so that if an exit
+	 * handler is hanging for some reason, the UML will just die
+	 * after this signal is sent a second time.
+	 */
+	action.sa_flags = SA_RESETHAND | SA_NODEFER;
+	action.sa_restorer = NULL;
+	action.sa_handler = last_ditch_exit;
+	if (sigaction(sig, &action, NULL) < 0) {
+		os_warn("failed to install handler for signal %d "
+			"- errno = %d\n", sig, errno);
+		exit(1);
+	}
+}
+
+#define UML_LIB_PATH	":" OS_LIB_PATH "/uml"
+
+static void setup_env_path(void)
+{
+	char *new_path = NULL;
+	char *old_path = NULL;
+	int path_len = 0;
+
+	old_path = getenv("PATH");
+	/*
+	 * if no PATH variable is set or it has an empty value
+	 * just use the default + /usr/lib/uml
+	 */
+	if (!old_path || (path_len = strlen(old_path)) == 0) {
+		if (putenv("PATH=:/bin:/usr/bin/" UML_LIB_PATH))
+			perror("couldn't putenv");
+		return;
+	}
+
+	/* append /usr/lib/uml to the existing path */
+	path_len += strlen("PATH=" UML_LIB_PATH) + 1;
+	new_path = malloc(path_len);
+	if (!new_path) {
+		perror("couldn't malloc to set a new PATH");
+		return;
+	}
+	snprintf(new_path, path_len, "PATH=%s" UML_LIB_PATH, old_path);
+	if (putenv(new_path)) {
+		perror("couldn't putenv to set a new PATH");
+		free(new_path);
+	}
+}
+
+extern void scan_elf_aux( char **envp);
+
+int __init main(int argc, char **argv, char **envp)
+{
+	char **new_argv;
+	int ret, i, err;
+
+	set_stklim();
+
+	setup_env_path();
+
+	setsid();
+
+	new_argv = malloc((argc + 1) * sizeof(char *));
+	if (new_argv == NULL) {
+		perror("Mallocing argv");
+		exit(1);
+	}
+	for (i = 0; i < argc; i++) {
+		new_argv[i] = strdup(argv[i]);
+		if (new_argv[i] == NULL) {
+			perror("Mallocing an arg");
+			exit(1);
+		}
+	}
+	new_argv[argc] = NULL;
+
+	/*
+	 * Allow these signals to bring down a UML if all other
+	 * methods of control fail.
+	 */
+	install_fatal_handler(SIGINT);
+	install_fatal_handler(SIGTERM);
+
+#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
+	scan_elf_aux(envp);
+#endif
+
+	change_sig(SIGPIPE, 0);
+	ret = linux_main(argc, argv);
+
+	/*
+	 * Disable SIGPROF - I have no idea why libc doesn't do this or turn
+	 * off the profiling time, but UML dies with a SIGPROF just before
+	 * exiting when profiling is active.
+	 */
+	change_sig(SIGPROF, 0);
+
+	/*
+	 * This signal stuff used to be in the reboot case.  However,
+	 * sometimes a timer signal can come in when we're halting (reproducably
+	 * when writing out gcov information, presumably because that takes
+	 * some time) and cause a segfault.
+	 */
+
+	/* stop timers and set timer signal to be ignored */
+	os_timer_disable();
+
+	/* disable SIGIO for the fds and set SIGIO to be ignored */
+	err = deactivate_all_fds();
+	if (err)
+		os_warn("deactivate_all_fds failed, errno = %d\n", -err);
+
+	/*
+	 * Let any pending signals fire now.  This ensures
+	 * that they won't be delivered after the exec, when
+	 * they are definitely not expected.
+	 */
+	unblock_signals();
+
+	os_info("\n");
+	/* Reboot */
+	if (ret) {
+		execvp(new_argv[0], new_argv);
+		perror("Failed to exec kernel");
+		ret = 1;
+	}
+	return uml_exitcode;
+}
+
+extern void *__real_malloc(int);
+
+void *__wrap_malloc(int size)
+{
+	void *ret;
+
+	if (!kmalloc_ok)
+		return __real_malloc(size);
+	else if (size <= UM_KERN_PAGE_SIZE)
+		/* finding contiguous pages can be hard*/
+		ret = uml_kmalloc(size, UM_GFP_KERNEL);
+	else ret = vmalloc(size);
+
+	/*
+	 * glibc people insist that if malloc fails, errno should be
+	 * set by malloc as well. So we do.
+	 */
+	if (ret == NULL)
+		errno = ENOMEM;
+
+	return ret;
+}
+
+void *__wrap_calloc(int n, int size)
+{
+	void *ptr = __wrap_malloc(n * size);
+
+	if (ptr == NULL)
+		return NULL;
+	memset(ptr, 0, n * size);
+	return ptr;
+}
+
+extern void __real_free(void *);
+
+extern unsigned long high_physmem;
+
+void __wrap_free(void *ptr)
+{
+	unsigned long addr = (unsigned long) ptr;
+
+	/*
+	 * We need to know how the allocation happened, so it can be correctly
+	 * freed.  This is done by seeing what region of memory the pointer is
+	 * in -
+	 * 	physical memory - kmalloc/kfree
+	 *	kernel virtual memory - vmalloc/vfree
+	 * 	anywhere else - malloc/free
+	 * If kmalloc is not yet possible, then either high_physmem and/or
+	 * end_vm are still 0 (as at startup), in which case we call free, or
+	 * we have set them, but anyway addr has not been allocated from those
+	 * areas. So, in both cases __real_free is called.
+	 *
+	 * CAN_KMALLOC is checked because it would be bad to free a buffer
+	 * with kmalloc/vmalloc after they have been turned off during
+	 * shutdown.
+	 * XXX: However, we sometimes shutdown CAN_KMALLOC temporarily, so
+	 * there is a possibility for memory leaks.
+	 */
+
+	if ((addr >= uml_physmem) && (addr < high_physmem)) {
+		if (kmalloc_ok)
+			kfree(ptr);
+	}
+	else if ((addr >= start_vm) && (addr < end_vm)) {
+		if (kmalloc_ok)
+			vfree(ptr);
+	}
+	else __real_free(ptr);
+}
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c
new file mode 100644
index 0000000..e162a95
--- /dev/null
+++ b/arch/um/os-Linux/mem.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <init.h>
+#include <os.h>
+
+/* Set by make_tempfile() during early boot. */
+static char *tempdir = NULL;
+
+/* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */
+static int __init check_tmpfs(const char *dir)
+{
+	struct statfs st;
+
+	os_info("Checking if %s is on tmpfs...", dir);
+	if (statfs(dir, &st) < 0) {
+		os_info("%s\n", strerror(errno));
+	} else if (st.f_type != TMPFS_MAGIC) {
+		os_info("no\n");
+	} else {
+		os_info("OK\n");
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * Choose the tempdir to use. We want something on tmpfs so that our memory is
+ * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the
+ * environment, we use that even if it's not on tmpfs, but we warn the user.
+ * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found
+ * then we fall back to /tmp.
+ */
+static char * __init choose_tempdir(void)
+{
+	static const char * const vars[] = {
+		"TMPDIR",
+		"TMP",
+		"TEMP",
+		NULL
+	};
+	static const char fallback_dir[] = "/tmp";
+	static const char * const tmpfs_dirs[] = {
+		"/dev/shm",
+		fallback_dir,
+		NULL
+	};
+	int i;
+	const char *dir;
+
+	os_info("Checking environment variables for a tempdir...");
+	for (i = 0; vars[i]; i++) {
+		dir = getenv(vars[i]);
+		if ((dir != NULL) && (*dir != '\0')) {
+			os_info("%s\n", dir);
+			if (check_tmpfs(dir) >= 0)
+				goto done;
+			else
+				goto warn;
+		}
+	}
+	os_info("none found\n");
+
+	for (i = 0; tmpfs_dirs[i]; i++) {
+		dir = tmpfs_dirs[i];
+		if (check_tmpfs(dir) >= 0)
+			goto done;
+	}
+
+	dir = fallback_dir;
+warn:
+	os_warn("Warning: tempdir %s is not on tmpfs\n", dir);
+done:
+	/* Make a copy since getenv results may not remain valid forever. */
+	return strdup(dir);
+}
+
+/*
+ * Create an unlinked tempfile in a suitable tempdir. template must be the
+ * basename part of the template with a leading '/'.
+ */
+static int __init make_tempfile(const char *template)
+{
+	char *tempname;
+	int fd;
+
+	if (tempdir == NULL) {
+		tempdir = choose_tempdir();
+		if (tempdir == NULL) {
+			os_warn("Failed to choose tempdir: %s\n",
+				strerror(errno));
+			return -1;
+		}
+	}
+
+#ifdef O_TMPFILE
+	fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700);
+	/*
+	 * If the running system does not support O_TMPFILE flag then retry
+	 * without it.
+	 */
+	if (fd != -1 || (errno != EINVAL && errno != EISDIR &&
+			errno != EOPNOTSUPP))
+		return fd;
+#endif
+
+	tempname = malloc(strlen(tempdir) + strlen(template) + 1);
+	if (tempname == NULL)
+		return -1;
+
+	strcpy(tempname, tempdir);
+	strcat(tempname, template);
+	fd = mkstemp(tempname);
+	if (fd < 0) {
+		os_warn("open - cannot create %s: %s\n", tempname,
+			strerror(errno));
+		goto out;
+	}
+	if (unlink(tempname) < 0) {
+		perror("unlink");
+		goto close;
+	}
+	free(tempname);
+	return fd;
+close:
+	close(fd);
+out:
+	free(tempname);
+	return -1;
+}
+
+#define TEMPNAME_TEMPLATE "/vm_file-XXXXXX"
+
+static int __init create_tmp_file(unsigned long long len)
+{
+	int fd, err;
+	char zero;
+
+	fd = make_tempfile(TEMPNAME_TEMPLATE);
+	if (fd < 0)
+		exit(1);
+
+	/*
+	 * Seek to len - 1 because writing a character there will
+	 * increase the file size by one byte, to the desired length.
+	 */
+	if (lseek64(fd, len - 1, SEEK_SET) < 0) {
+		perror("lseek64");
+		exit(1);
+	}
+
+	zero = 0;
+
+	err = write(fd, &zero, 1);
+	if (err != 1) {
+		perror("write");
+		exit(1);
+	}
+
+	return fd;
+}
+
+int __init create_mem_file(unsigned long long len)
+{
+	int err, fd;
+
+	fd = create_tmp_file(len);
+
+	err = os_set_exec_close(fd);
+	if (err < 0) {
+		errno = -err;
+		perror("exec_close");
+	}
+	return fd;
+}
+
+void __init check_tmpexec(void)
+{
+	void *addr;
+	int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
+
+	addr = mmap(NULL, UM_KERN_PAGE_SIZE,
+		    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
+	os_info("Checking PROT_EXEC mmap in %s...", tempdir);
+	if (addr == MAP_FAILED) {
+		err = errno;
+		os_warn("%s\n", strerror(err));
+		close(fd);
+		if (err == EPERM)
+			os_warn("%s must be not mounted noexec\n", tempdir);
+		exit(1);
+	}
+	os_info("OK\n");
+	munmap(addr, UM_KERN_PAGE_SIZE);
+
+	close(fd);
+}
diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c
new file mode 100644
index 0000000..b3e0d40
--- /dev/null
+++ b/arch/um/os-Linux/process.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
+ * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <asm/unistd.h>
+#include <init.h>
+#include <longjmp.h>
+#include <os.h>
+
+#define ARBITRARY_ADDR -1
+#define FAILURE_PID    -1
+
+#define STAT_PATH_LEN sizeof("/proc/#######/stat\0")
+#define COMM_SCANF "%*[^)])"
+
+unsigned long os_process_pc(int pid)
+{
+	char proc_stat[STAT_PATH_LEN], buf[256];
+	unsigned long pc = ARBITRARY_ADDR;
+	int fd, err;
+
+	sprintf(proc_stat, "/proc/%d/stat", pid);
+	fd = open(proc_stat, O_RDONLY, 0);
+	if (fd < 0) {
+		printk(UM_KERN_ERR "os_process_pc - couldn't open '%s', "
+		       "errno = %d\n", proc_stat, errno);
+		goto out;
+	}
+	CATCH_EINTR(err = read(fd, buf, sizeof(buf)));
+	if (err < 0) {
+		printk(UM_KERN_ERR "os_process_pc - couldn't read '%s', "
+		       "err = %d\n", proc_stat, errno);
+		goto out_close;
+	}
+	os_close_file(fd);
+	pc = ARBITRARY_ADDR;
+	if (sscanf(buf, "%*d " COMM_SCANF " %*c %*d %*d %*d %*d %*d %*d %*d "
+		   "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+		   "%*d %*d %*d %*d %*d %lu", &pc) != 1)
+		printk(UM_KERN_ERR "os_process_pc - couldn't find pc in '%s'\n",
+		       buf);
+ out_close:
+	close(fd);
+ out:
+	return pc;
+}
+
+int os_process_parent(int pid)
+{
+	char stat[STAT_PATH_LEN];
+	char data[256];
+	int parent = FAILURE_PID, n, fd;
+
+	if (pid == -1)
+		return parent;
+
+	snprintf(stat, sizeof(stat), "/proc/%d/stat", pid);
+	fd = open(stat, O_RDONLY, 0);
+	if (fd < 0) {
+		printk(UM_KERN_ERR "Couldn't open '%s', errno = %d\n", stat,
+		       errno);
+		return parent;
+	}
+
+	CATCH_EINTR(n = read(fd, data, sizeof(data)));
+	close(fd);
+
+	if (n < 0) {
+		printk(UM_KERN_ERR "Couldn't read '%s', errno = %d\n", stat,
+		       errno);
+		return parent;
+	}
+
+	parent = FAILURE_PID;
+	n = sscanf(data, "%*d " COMM_SCANF " %*c %d", &parent);
+	if (n != 1)
+		printk(UM_KERN_ERR "Failed to scan '%s'\n", data);
+
+	return parent;
+}
+
+void os_alarm_process(int pid)
+{
+	kill(pid, SIGALRM);
+}
+
+void os_stop_process(int pid)
+{
+	kill(pid, SIGSTOP);
+}
+
+void os_kill_process(int pid, int reap_child)
+{
+	kill(pid, SIGKILL);
+	if (reap_child)
+		CATCH_EINTR(waitpid(pid, NULL, __WALL));
+}
+
+/* Kill off a ptraced child by all means available.  kill it normally first,
+ * then PTRACE_KILL it, then PTRACE_CONT it in case it's in a run state from
+ * which it can't exit directly.
+ */
+
+void os_kill_ptraced_process(int pid, int reap_child)
+{
+	kill(pid, SIGKILL);
+	ptrace(PTRACE_KILL, pid);
+	ptrace(PTRACE_CONT, pid);
+	if (reap_child)
+		CATCH_EINTR(waitpid(pid, NULL, __WALL));
+}
+
+/* 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.
+ */
+
+int os_getpid(void)
+{
+	return syscall(__NR_getpid);
+}
+
+int os_getpgrp(void)
+{
+	return getpgrp();
+}
+
+int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len,
+		  int r, int w, int x)
+{
+	void *loc;
+	int prot;
+
+	prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
+		(x ? PROT_EXEC : 0);
+
+	loc = mmap64((void *) virt, len, prot, MAP_SHARED | MAP_FIXED,
+		     fd, off);
+	if (loc == MAP_FAILED)
+		return -errno;
+	return 0;
+}
+
+int os_protect_memory(void *addr, unsigned long len, int r, int w, int x)
+{
+	int prot = ((r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
+		    (x ? PROT_EXEC : 0));
+
+	if (mprotect(addr, len, prot) < 0)
+		return -errno;
+
+	return 0;
+}
+
+int os_unmap_memory(void *addr, int len)
+{
+	int err;
+
+	err = munmap(addr, len);
+	if (err < 0)
+		return -errno;
+	return 0;
+}
+
+#ifndef MADV_REMOVE
+#define MADV_REMOVE KERNEL_MADV_REMOVE
+#endif
+
+int os_drop_memory(void *addr, int length)
+{
+	int err;
+
+	err = madvise(addr, length, MADV_REMOVE);
+	if (err < 0)
+		err = -errno;
+	return err;
+}
+
+int __init can_drop_memory(void)
+{
+	void *addr;
+	int fd, ok = 0;
+
+	printk(UM_KERN_INFO "Checking host MADV_REMOVE support...");
+	fd = create_mem_file(UM_KERN_PAGE_SIZE);
+	if (fd < 0) {
+		printk(UM_KERN_ERR "Creating test memory file failed, "
+		       "err = %d\n", -fd);
+		goto out;
+	}
+
+	addr = mmap64(NULL, UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE,
+		      MAP_SHARED, fd, 0);
+	if (addr == MAP_FAILED) {
+		printk(UM_KERN_ERR "Mapping test memory file failed, "
+		       "err = %d\n", -errno);
+		goto out_close;
+	}
+
+	if (madvise(addr, UM_KERN_PAGE_SIZE, MADV_REMOVE) != 0) {
+		printk(UM_KERN_ERR "MADV_REMOVE failed, err = %d\n", -errno);
+		goto out_unmap;
+	}
+
+	printk(UM_KERN_CONT "OK\n");
+	ok = 1;
+
+out_unmap:
+	munmap(addr, UM_KERN_PAGE_SIZE);
+out_close:
+	close(fd);
+out:
+	return ok;
+}
+
+static int os_page_mincore(void *addr)
+{
+	char vec[2];
+	int ret;
+
+	ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
+	if (ret < 0) {
+		if (errno == ENOMEM || errno == EINVAL)
+			return 0;
+		else
+			return -errno;
+	}
+
+	return vec[0] & 1;
+}
+
+int os_mincore(void *addr, unsigned long len)
+{
+	char *vec;
+	int ret, i;
+
+	if (len <= UM_KERN_PAGE_SIZE)
+		return os_page_mincore(addr);
+
+	vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE);
+	if (!vec)
+		return -ENOMEM;
+
+	ret = mincore(addr, UM_KERN_PAGE_SIZE, vec);
+	if (ret < 0) {
+		if (errno == ENOMEM || errno == EINVAL)
+			ret = 0;
+		else
+			ret = -errno;
+
+		goto out;
+	}
+
+	for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) {
+		if (!(vec[i] & 1)) {
+			ret = 0;
+			goto out;
+		}
+	}
+
+	ret = 1;
+out:
+	free(vec);
+	return ret;
+}
+
+void init_new_thread_signals(void)
+{
+	set_handler(SIGSEGV);
+	set_handler(SIGTRAP);
+	set_handler(SIGFPE);
+	set_handler(SIGILL);
+	set_handler(SIGBUS);
+	signal(SIGHUP, SIG_IGN);
+	set_handler(SIGIO);
+	signal(SIGWINCH, SIG_IGN);
+}
diff --git a/arch/um/os-Linux/registers.c b/arch/um/os-Linux/registers.c
new file mode 100644
index 0000000..2ff8d4f
--- /dev/null
+++ b/arch/um/os-Linux/registers.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sysdep/ptrace.h>
+#include <sysdep/ptrace_user.h>
+#include <registers.h>
+
+int save_registers(int pid, struct uml_pt_regs *regs)
+{
+	int err;
+
+	err = ptrace(PTRACE_GETREGS, pid, 0, regs->gp);
+	if (err < 0)
+		return -errno;
+	return 0;
+}
+
+int restore_registers(int pid, struct uml_pt_regs *regs)
+{
+	int err;
+
+	err = ptrace(PTRACE_SETREGS, pid, 0, regs->gp);
+	if (err < 0)
+		return -errno;
+	return 0;
+}
+
+/* This is set once at boot time and not changed thereafter */
+
+static unsigned long exec_regs[MAX_REG_NR];
+static unsigned long exec_fp_regs[FP_SIZE];
+
+int init_registers(int pid)
+{
+	int err;
+
+	err = ptrace(PTRACE_GETREGS, pid, 0, exec_regs);
+	if (err < 0)
+		return -errno;
+
+	arch_init_registers(pid);
+	get_fp_registers(pid, exec_fp_regs);
+	return 0;
+}
+
+void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
+{
+	memcpy(regs, exec_regs, sizeof(exec_regs));
+
+	if (fp_regs)
+		memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs));
+}
diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c
new file mode 100644
index 0000000..46e762f
--- /dev/null
+++ b/arch/um/os-Linux/sigio.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pty.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <kern_util.h>
+#include <init.h>
+#include <os.h>
+#include <sigio.h>
+#include <um_malloc.h>
+
+/*
+ * Protected by sigio_lock(), also used by sigio_cleanup, which is an
+ * exitcall.
+ */
+static int write_sigio_pid = -1;
+static unsigned long write_sigio_stack;
+
+/*
+ * These arrays are initialized before the sigio thread is started, and
+ * the descriptors closed after it is killed.  So, it can't see them change.
+ * On the UML side, they are changed under the sigio_lock.
+ */
+#define SIGIO_FDS_INIT {-1, -1}
+
+static int write_sigio_fds[2] = SIGIO_FDS_INIT;
+static int sigio_private[2] = SIGIO_FDS_INIT;
+
+struct pollfds {
+	struct pollfd *poll;
+	int size;
+	int used;
+};
+
+/*
+ * Protected by sigio_lock().  Used by the sigio thread, but the UML thread
+ * synchronizes with it.
+ */
+static struct pollfds current_poll;
+static struct pollfds next_poll;
+static struct pollfds all_sigio_fds;
+
+static int write_sigio_thread(void *unused)
+{
+	struct pollfds *fds, tmp;
+	struct pollfd *p;
+	int i, n, respond_fd;
+	char c;
+
+	os_fix_helper_signals();
+	fds = &current_poll;
+	while (1) {
+		n = poll(fds->poll, fds->used, -1);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			printk(UM_KERN_ERR "write_sigio_thread : poll returned "
+			       "%d, errno = %d\n", n, errno);
+		}
+		for (i = 0; i < fds->used; i++) {
+			p = &fds->poll[i];
+			if (p->revents == 0)
+				continue;
+			if (p->fd == sigio_private[1]) {
+				CATCH_EINTR(n = read(sigio_private[1], &c,
+						     sizeof(c)));
+				if (n != sizeof(c))
+					printk(UM_KERN_ERR
+					       "write_sigio_thread : "
+					       "read on socket failed, "
+					       "err = %d\n", errno);
+				tmp = current_poll;
+				current_poll = next_poll;
+				next_poll = tmp;
+				respond_fd = sigio_private[1];
+			}
+			else {
+				respond_fd = write_sigio_fds[1];
+				fds->used--;
+				memmove(&fds->poll[i], &fds->poll[i + 1],
+					(fds->used - i) * sizeof(*fds->poll));
+			}
+
+			CATCH_EINTR(n = write(respond_fd, &c, sizeof(c)));
+			if (n != sizeof(c))
+				printk(UM_KERN_ERR "write_sigio_thread : "
+				       "write on socket failed, err = %d\n",
+				       errno);
+		}
+	}
+
+	return 0;
+}
+
+static int need_poll(struct pollfds *polls, int n)
+{
+	struct pollfd *new;
+
+	if (n <= polls->size)
+		return 0;
+
+	new = uml_kmalloc(n * sizeof(struct pollfd), UM_GFP_ATOMIC);
+	if (new == NULL) {
+		printk(UM_KERN_ERR "need_poll : failed to allocate new "
+		       "pollfds\n");
+		return -ENOMEM;
+	}
+
+	memcpy(new, polls->poll, polls->used * sizeof(struct pollfd));
+	kfree(polls->poll);
+
+	polls->poll = new;
+	polls->size = n;
+	return 0;
+}
+
+/*
+ * Must be called with sigio_lock held, because it's needed by the marked
+ * critical section.
+ */
+static void update_thread(void)
+{
+	unsigned long flags;
+	int n;
+	char c;
+
+	flags = set_signals(0);
+	CATCH_EINTR(n = write(sigio_private[0], &c, sizeof(c)));
+	if (n != sizeof(c)) {
+		printk(UM_KERN_ERR "update_thread : write failed, err = %d\n",
+		       errno);
+		goto fail;
+	}
+
+	CATCH_EINTR(n = read(sigio_private[0], &c, sizeof(c)));
+	if (n != sizeof(c)) {
+		printk(UM_KERN_ERR "update_thread : read failed, err = %d\n",
+		       errno);
+		goto fail;
+	}
+
+	set_signals(flags);
+	return;
+ fail:
+	/* Critical section start */
+	if (write_sigio_pid != -1) {
+		os_kill_process(write_sigio_pid, 1);
+		free_stack(write_sigio_stack, 0);
+	}
+	write_sigio_pid = -1;
+	close(sigio_private[0]);
+	close(sigio_private[1]);
+	close(write_sigio_fds[0]);
+	close(write_sigio_fds[1]);
+	/* Critical section end */
+	set_signals(flags);
+}
+
+int add_sigio_fd(int fd)
+{
+	struct pollfd *p;
+	int err = 0, i, n;
+
+	sigio_lock();
+	for (i = 0; i < all_sigio_fds.used; i++) {
+		if (all_sigio_fds.poll[i].fd == fd)
+			break;
+	}
+	if (i == all_sigio_fds.used)
+		goto out;
+
+	p = &all_sigio_fds.poll[i];
+
+	for (i = 0; i < current_poll.used; i++) {
+		if (current_poll.poll[i].fd == fd)
+			goto out;
+	}
+
+	n = current_poll.used;
+	err = need_poll(&next_poll, n + 1);
+	if (err)
+		goto out;
+
+	memcpy(next_poll.poll, current_poll.poll,
+	       current_poll.used * sizeof(struct pollfd));
+	next_poll.poll[n] = *p;
+	next_poll.used = n + 1;
+	update_thread();
+ out:
+	sigio_unlock();
+	return err;
+}
+
+int ignore_sigio_fd(int fd)
+{
+	struct pollfd *p;
+	int err = 0, i, n = 0;
+
+	/*
+	 * This is called from exitcalls elsewhere in UML - if
+	 * sigio_cleanup has already run, then update_thread will hang
+	 * or fail because the thread is no longer running.
+	 */
+	if (write_sigio_pid == -1)
+		return -EIO;
+
+	sigio_lock();
+	for (i = 0; i < current_poll.used; i++) {
+		if (current_poll.poll[i].fd == fd)
+			break;
+	}
+	if (i == current_poll.used)
+		goto out;
+
+	err = need_poll(&next_poll, current_poll.used - 1);
+	if (err)
+		goto out;
+
+	for (i = 0; i < current_poll.used; i++) {
+		p = &current_poll.poll[i];
+		if (p->fd != fd)
+			next_poll.poll[n++] = *p;
+	}
+	next_poll.used = current_poll.used - 1;
+
+	update_thread();
+ out:
+	sigio_unlock();
+	return err;
+}
+
+static struct pollfd *setup_initial_poll(int fd)
+{
+	struct pollfd *p;
+
+	p = uml_kmalloc(sizeof(struct pollfd), UM_GFP_KERNEL);
+	if (p == NULL) {
+		printk(UM_KERN_ERR "setup_initial_poll : failed to allocate "
+		       "poll\n");
+		return NULL;
+	}
+	*p = ((struct pollfd) { .fd		= fd,
+				.events 	= POLLIN,
+				.revents 	= 0 });
+	return p;
+}
+
+static void write_sigio_workaround(void)
+{
+	struct pollfd *p;
+	int err;
+	int l_write_sigio_fds[2];
+	int l_sigio_private[2];
+	int l_write_sigio_pid;
+
+	/* We call this *tons* of times - and most ones we must just fail. */
+	sigio_lock();
+	l_write_sigio_pid = write_sigio_pid;
+	sigio_unlock();
+
+	if (l_write_sigio_pid != -1)
+		return;
+
+	err = os_pipe(l_write_sigio_fds, 1, 1);
+	if (err < 0) {
+		printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 1 failed, "
+		       "err = %d\n", -err);
+		return;
+	}
+	err = os_pipe(l_sigio_private, 1, 1);
+	if (err < 0) {
+		printk(UM_KERN_ERR "write_sigio_workaround - os_pipe 2 failed, "
+		       "err = %d\n", -err);
+		goto out_close1;
+	}
+
+	p = setup_initial_poll(l_sigio_private[1]);
+	if (!p)
+		goto out_close2;
+
+	sigio_lock();
+
+	/*
+	 * Did we race? Don't try to optimize this, please, it's not so likely
+	 * to happen, and no more than once at the boot.
+	 */
+	if (write_sigio_pid != -1)
+		goto out_free;
+
+	current_poll = ((struct pollfds) { .poll 	= p,
+					   .used 	= 1,
+					   .size 	= 1 });
+
+	if (write_sigio_irq(l_write_sigio_fds[0]))
+		goto out_clear_poll;
+
+	memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds));
+	memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private));
+
+	write_sigio_pid = run_helper_thread(write_sigio_thread, NULL,
+					    CLONE_FILES | CLONE_VM,
+					    &write_sigio_stack);
+
+	if (write_sigio_pid < 0)
+		goto out_clear;
+
+	sigio_unlock();
+	return;
+
+out_clear:
+	write_sigio_pid = -1;
+	write_sigio_fds[0] = -1;
+	write_sigio_fds[1] = -1;
+	sigio_private[0] = -1;
+	sigio_private[1] = -1;
+out_clear_poll:
+	current_poll = ((struct pollfds) { .poll	= NULL,
+					   .size	= 0,
+					   .used	= 0 });
+out_free:
+	sigio_unlock();
+	kfree(p);
+out_close2:
+	close(l_sigio_private[0]);
+	close(l_sigio_private[1]);
+out_close1:
+	close(l_write_sigio_fds[0]);
+	close(l_write_sigio_fds[1]);
+}
+
+void sigio_broken(int fd, int read)
+{
+	int err;
+
+	write_sigio_workaround();
+
+	sigio_lock();
+	err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1);
+	if (err) {
+		printk(UM_KERN_ERR "maybe_sigio_broken - failed to add pollfd "
+		       "for descriptor %d\n", fd);
+		goto out;
+	}
+
+	all_sigio_fds.poll[all_sigio_fds.used++] =
+		((struct pollfd) { .fd  	= fd,
+				   .events 	= read ? POLLIN : POLLOUT,
+				   .revents 	= 0 });
+out:
+	sigio_unlock();
+}
+
+/* Changed during early boot */
+static int pty_output_sigio;
+static int pty_close_sigio;
+
+void maybe_sigio_broken(int fd, int read)
+{
+	if (!isatty(fd))
+		return;
+
+	if ((read || pty_output_sigio) && (!read || pty_close_sigio))
+		return;
+
+	sigio_broken(fd, read);
+}
+
+static void sigio_cleanup(void)
+{
+	if (write_sigio_pid == -1)
+		return;
+
+	os_kill_process(write_sigio_pid, 1);
+	free_stack(write_sigio_stack, 0);
+	write_sigio_pid = -1;
+}
+
+__uml_exitcall(sigio_cleanup);
+
+/* Used as a flag during SIGIO testing early in boot */
+static int got_sigio;
+
+static void __init handler(int sig)
+{
+	got_sigio = 1;
+}
+
+struct openpty_arg {
+	int master;
+	int slave;
+	int err;
+};
+
+static void openpty_cb(void *arg)
+{
+	struct openpty_arg *info = arg;
+
+	info->err = 0;
+	if (openpty(&info->master, &info->slave, NULL, NULL, NULL))
+		info->err = -errno;
+}
+
+static int async_pty(int master, int slave)
+{
+	int flags;
+
+	flags = fcntl(master, F_GETFL);
+	if (flags < 0)
+		return -errno;
+
+	if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) ||
+	    (fcntl(master, F_SETOWN, os_getpid()) < 0))
+		return -errno;
+
+	if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0))
+		return -errno;
+
+	return 0;
+}
+
+static void __init check_one_sigio(void (*proc)(int, int))
+{
+	struct sigaction old, new;
+	struct openpty_arg pty = { .master = -1, .slave = -1 };
+	int master, slave, err;
+
+	initial_thread_cb(openpty_cb, &pty);
+	if (pty.err) {
+		printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n",
+		       -pty.err);
+		return;
+	}
+
+	master = pty.master;
+	slave = pty.slave;
+
+	if ((master == -1) || (slave == -1)) {
+		printk(UM_KERN_ERR "check_one_sigio failed to allocate a "
+		       "pty\n");
+		return;
+	}
+
+	/* Not now, but complain so we now where we failed. */
+	err = raw(master);
+	if (err < 0) {
+		printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n",
+		      -err);
+		return;
+	}
+
+	err = async_pty(master, slave);
+	if (err < 0) {
+		printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, "
+		       "err = %d\n", -err);
+		return;
+	}
+
+	if (sigaction(SIGIO, NULL, &old) < 0) {
+		printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, "
+		       "errno = %d\n", errno);
+		return;
+	}
+
+	new = old;
+	new.sa_handler = handler;
+	if (sigaction(SIGIO, &new, NULL) < 0) {
+		printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, "
+		       "errno = %d\n", errno);
+		return;
+	}
+
+	got_sigio = 0;
+	(*proc)(master, slave);
+
+	close(master);
+	close(slave);
+
+	if (sigaction(SIGIO, &old, NULL) < 0)
+		printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, "
+		       "errno = %d\n", errno);
+}
+
+static void tty_output(int master, int slave)
+{
+	int n;
+	char buf[512];
+
+	printk(UM_KERN_INFO "Checking that host ptys support output SIGIO...");
+
+	memset(buf, 0, sizeof(buf));
+
+	while (write(master, buf, sizeof(buf)) > 0) ;
+	if (errno != EAGAIN)
+		printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n",
+		       errno);
+	while (((n = read(slave, buf, sizeof(buf))) > 0) &&
+	       !({ barrier(); got_sigio; }))
+		;
+
+	if (got_sigio) {
+		printk(UM_KERN_CONT "Yes\n");
+		pty_output_sigio = 1;
+	} else if (n == -EAGAIN)
+		printk(UM_KERN_CONT "No, enabling workaround\n");
+	else
+		printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n);
+}
+
+static void tty_close(int master, int slave)
+{
+	printk(UM_KERN_INFO "Checking that host ptys support SIGIO on "
+	       "close...");
+
+	close(slave);
+	if (got_sigio) {
+		printk(UM_KERN_CONT "Yes\n");
+		pty_close_sigio = 1;
+	} else
+		printk(UM_KERN_CONT "No, enabling workaround\n");
+}
+
+static void __init check_sigio(void)
+{
+	if ((access("/dev/ptmx", R_OK) < 0) &&
+	    (access("/dev/ptyp0", R_OK) < 0)) {
+		printk(UM_KERN_WARNING "No pseudo-terminals available - "
+		       "skipping pty SIGIO check\n");
+		return;
+	}
+	check_one_sigio(tty_output);
+	check_one_sigio(tty_close);
+}
+
+/* Here because it only does the SIGIO testing for now */
+void __init os_check_bugs(void)
+{
+	check_sigio();
+}
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
new file mode 100644
index 0000000..bf0acb8
--- /dev/null
+++ b/arch/um/os-Linux/signal.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
+ * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
+ * Copyright (C) 2004 PathScale, Inc
+ * Copyright (C) 2004 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <signal.h>
+#include <strings.h>
+#include <as-layout.h>
+#include <kern_util.h>
+#include <os.h>
+#include <sysdep/mcontext.h>
+#include <um_malloc.h>
+#include <sys/ucontext.h>
+
+void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
+	[SIGTRAP]	= relay_signal,
+	[SIGFPE]	= relay_signal,
+	[SIGILL]	= relay_signal,
+	[SIGWINCH]	= winch,
+	[SIGBUS]	= bus_handler,
+	[SIGSEGV]	= segv_handler,
+	[SIGIO]		= sigio_handler,
+	[SIGALRM]	= timer_handler
+};
+
+static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
+{
+	struct uml_pt_regs *r;
+	int save_errno = errno;
+
+	r = uml_kmalloc(sizeof(struct uml_pt_regs), UM_GFP_ATOMIC);
+	if (!r)
+		panic("out of memory");
+
+	r->is_user = 0;
+	if (sig == SIGSEGV) {
+		/* For segfaults, we want the data from the sigcontext. */
+		get_regs_from_mc(r, mc);
+		GET_FAULTINFO_FROM_MC(r->faultinfo, mc);
+	}
+
+	/* enable signals if sig isn't IRQ signal */
+	if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGALRM))
+		unblock_signals();
+
+	(*sig_info[sig])(sig, si, r);
+
+	errno = save_errno;
+
+	free(r);
+}
+
+/*
+ * These are the asynchronous signals.  SIGPROF is excluded because we want to
+ * be able to profile all of UML, not just the non-critical sections.  If
+ * profiling is not thread-safe, then that is not my problem.  We can disable
+ * profiling when SMP is enabled in that case.
+ */
+#define SIGIO_BIT 0
+#define SIGIO_MASK (1 << SIGIO_BIT)
+
+#define SIGALRM_BIT 1
+#define SIGALRM_MASK (1 << SIGALRM_BIT)
+
+static int signals_enabled;
+static unsigned int signals_pending;
+static unsigned int signals_active = 0;
+
+void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
+{
+	int enabled;
+
+	enabled = signals_enabled;
+	if (!enabled && (sig == SIGIO)) {
+		signals_pending |= SIGIO_MASK;
+		return;
+	}
+
+	block_signals();
+
+	sig_handler_common(sig, si, mc);
+
+	set_signals(enabled);
+}
+
+static void timer_real_alarm_handler(mcontext_t *mc)
+{
+	struct uml_pt_regs *regs;
+
+	regs = uml_kmalloc(sizeof(struct uml_pt_regs), UM_GFP_ATOMIC);
+	if (!regs)
+		panic("out of memory");
+
+	if (mc != NULL)
+		get_regs_from_mc(regs, mc);
+	timer_handler(SIGALRM, NULL, regs);
+
+	free(regs);
+}
+
+void timer_alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc)
+{
+	int enabled;
+
+	enabled = signals_enabled;
+	if (!signals_enabled) {
+		signals_pending |= SIGALRM_MASK;
+		return;
+	}
+
+	block_signals();
+
+	signals_active |= SIGALRM_MASK;
+
+	timer_real_alarm_handler(mc);
+
+	signals_active &= ~SIGALRM_MASK;
+
+	set_signals(enabled);
+}
+
+void deliver_alarm(void) {
+    timer_alarm_handler(SIGALRM, NULL, NULL);
+}
+
+void timer_set_signal_handler(void)
+{
+	set_handler(SIGALRM);
+}
+
+void set_sigstack(void *sig_stack, int size)
+{
+	stack_t stack = {
+		.ss_flags = 0,
+		.ss_sp = sig_stack,
+		.ss_size = size - sizeof(void *)
+	};
+
+	if (sigaltstack(&stack, NULL) != 0)
+		panic("enabling signal stack failed, errno = %d\n", errno);
+}
+
+static void (*handlers[_NSIG])(int sig, struct siginfo *si, mcontext_t *mc) = {
+	[SIGSEGV] = sig_handler,
+	[SIGBUS] = sig_handler,
+	[SIGILL] = sig_handler,
+	[SIGFPE] = sig_handler,
+	[SIGTRAP] = sig_handler,
+
+	[SIGIO] = sig_handler,
+	[SIGWINCH] = sig_handler,
+	[SIGALRM] = timer_alarm_handler
+};
+
+static void hard_handler(int sig, siginfo_t *si, void *p)
+{
+	ucontext_t *uc = p;
+	mcontext_t *mc = &uc->uc_mcontext;
+	unsigned long pending = 1UL << sig;
+
+	do {
+		int nested, bail;
+
+		/*
+		 * pending comes back with one bit set for each
+		 * interrupt that arrived while setting up the stack,
+		 * plus a bit for this interrupt, plus the zero bit is
+		 * set if this is a nested interrupt.
+		 * If bail is true, then we interrupted another
+		 * handler setting up the stack.  In this case, we
+		 * have to return, and the upper handler will deal
+		 * with this interrupt.
+		 */
+		bail = to_irq_stack(&pending);
+		if (bail)
+			return;
+
+		nested = pending & 1;
+		pending &= ~1;
+
+		while ((sig = ffs(pending)) != 0){
+			sig--;
+			pending &= ~(1 << sig);
+			(*handlers[sig])(sig, (struct siginfo *)si, mc);
+		}
+
+		/*
+		 * Again, pending comes back with a mask of signals
+		 * that arrived while tearing down the stack.  If this
+		 * is non-zero, we just go back, set up the stack
+		 * again, and handle the new interrupts.
+		 */
+		if (!nested)
+			pending = from_irq_stack(nested);
+	} while (pending);
+}
+
+void set_handler(int sig)
+{
+	struct sigaction action;
+	int flags = SA_SIGINFO | SA_ONSTACK;
+	sigset_t sig_mask;
+
+	action.sa_sigaction = hard_handler;
+
+	/* block irq ones */
+	sigemptyset(&action.sa_mask);
+	sigaddset(&action.sa_mask, SIGIO);
+	sigaddset(&action.sa_mask, SIGWINCH);
+	sigaddset(&action.sa_mask, SIGALRM);
+
+	if (sig == SIGSEGV)
+		flags |= SA_NODEFER;
+
+	if (sigismember(&action.sa_mask, sig))
+		flags |= SA_RESTART; /* if it's an irq signal */
+
+	action.sa_flags = flags;
+	action.sa_restorer = NULL;
+	if (sigaction(sig, &action, NULL) < 0)
+		panic("sigaction failed - errno = %d\n", errno);
+
+	sigemptyset(&sig_mask);
+	sigaddset(&sig_mask, sig);
+	if (sigprocmask(SIG_UNBLOCK, &sig_mask, NULL) < 0)
+		panic("sigprocmask failed - errno = %d\n", errno);
+}
+
+int change_sig(int signal, int on)
+{
+	sigset_t sigset;
+
+	sigemptyset(&sigset);
+	sigaddset(&sigset, signal);
+	if (sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, NULL) < 0)
+		return -errno;
+
+	return 0;
+}
+
+void block_signals(void)
+{
+	signals_enabled = 0;
+	/*
+	 * This must return with signals disabled, so this barrier
+	 * ensures that writes are flushed out before the return.
+	 * This might matter if gcc figures out how to inline this and
+	 * decides to shuffle this code into the caller.
+	 */
+	barrier();
+}
+
+void unblock_signals(void)
+{
+	int save_pending;
+
+	if (signals_enabled == 1)
+		return;
+
+	/*
+	 * We loop because the IRQ handler returns with interrupts off.  So,
+	 * interrupts may have arrived and we need to re-enable them and
+	 * recheck signals_pending.
+	 */
+	while (1) {
+		/*
+		 * Save and reset save_pending after enabling signals.  This
+		 * way, signals_pending won't be changed while we're reading it.
+		 */
+		signals_enabled = 1;
+
+		/*
+		 * Setting signals_enabled and reading signals_pending must
+		 * happen in this order.
+		 */
+		barrier();
+
+		save_pending = signals_pending;
+		if (save_pending == 0)
+			return;
+
+		signals_pending = 0;
+
+		/*
+		 * We have pending interrupts, so disable signals, as the
+		 * handlers expect them off when they are called.  They will
+		 * be enabled again above.
+		 */
+
+		signals_enabled = 0;
+
+		/*
+		 * Deal with SIGIO first because the alarm handler might
+		 * schedule, leaving the pending SIGIO stranded until we come
+		 * back here.
+		 *
+		 * SIGIO's handler doesn't use siginfo or mcontext,
+		 * so they can be NULL.
+		 */
+		if (save_pending & SIGIO_MASK)
+			sig_handler_common(SIGIO, NULL, NULL);
+
+		/* Do not reenter the handler */
+
+		if ((save_pending & SIGALRM_MASK) && (!(signals_active & SIGALRM_MASK)))
+			timer_real_alarm_handler(NULL);
+
+		/* Rerun the loop only if there is still pending SIGIO and not in TIMER handler */
+
+		if (!(signals_pending & SIGIO_MASK) && (signals_active & SIGALRM_MASK))
+			return;
+
+	}
+}
+
+int get_signals(void)
+{
+	return signals_enabled;
+}
+
+int set_signals(int enable)
+{
+	int ret;
+	if (signals_enabled == enable)
+		return enable;
+
+	ret = signals_enabled;
+	if (enable)
+		unblock_signals();
+	else block_signals();
+
+	return ret;
+}
+
+int os_is_signal_stack(void)
+{
+	stack_t ss;
+	sigaltstack(NULL, &ss);
+
+	return ss.ss_flags & SS_ONSTACK;
+}
diff --git a/arch/um/os-Linux/skas/Makefile b/arch/um/os-Linux/skas/Makefile
new file mode 100644
index 0000000..d2ea340
--- /dev/null
+++ b/arch/um/os-Linux/skas/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (C) 2002 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
+# Licensed under the GPL
+#
+
+obj-y := mem.o process.o
+
+USER_OBJS := $(obj-y)
+
+include arch/um/scripts/Makefile.rules
diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c
new file mode 100644
index 0000000..35015e3
--- /dev/null
+++ b/arch/um/os-Linux/skas/mem.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <init.h>
+#include <as-layout.h>
+#include <mm_id.h>
+#include <os.h>
+#include <ptrace_user.h>
+#include <registers.h>
+#include <skas.h>
+#include <sysdep/ptrace.h>
+#include <sysdep/stub.h>
+
+extern char batch_syscall_stub[], __syscall_stub_start[];
+
+extern void wait_stub_done(int pid);
+
+static inline unsigned long *check_init_stack(struct mm_id * mm_idp,
+					      unsigned long *stack)
+{
+	if (stack == NULL) {
+		stack = (unsigned long *) mm_idp->stack + 2;
+		*stack = 0;
+	}
+	return stack;
+}
+
+static unsigned long syscall_regs[MAX_REG_NR];
+
+static int __init init_syscall_regs(void)
+{
+	get_safe_registers(syscall_regs, NULL);
+	syscall_regs[REGS_IP_INDEX] = STUB_CODE +
+		((unsigned long) batch_syscall_stub -
+		 (unsigned long) __syscall_stub_start);
+	return 0;
+}
+
+__initcall(init_syscall_regs);
+
+static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr)
+{
+	int n, i;
+	long ret, offset;
+	unsigned long * data;
+	unsigned long * syscall;
+	int err, pid = mm_idp->u.pid;
+
+	n = ptrace_setregs(pid, syscall_regs);
+	if (n < 0) {
+		printk(UM_KERN_ERR "Registers - \n");
+		for (i = 0; i < MAX_REG_NR; i++)
+			printk(UM_KERN_ERR "\t%d\t0x%lx\n", i, syscall_regs[i]);
+		panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n",
+		      -n);
+	}
+
+	err = ptrace(PTRACE_CONT, pid, 0, 0);
+	if (err)
+		panic("Failed to continue stub, pid = %d, errno = %d\n", pid,
+		      errno);
+
+	wait_stub_done(pid);
+
+	/*
+	 * When the stub stops, we find the following values on the
+	 * beginning of the stack:
+	 * (long )return_value
+	 * (long )offset to failed sycall-data (0, if no error)
+	 */
+	ret = *((unsigned long *) mm_idp->stack);
+	offset = *((unsigned long *) mm_idp->stack + 1);
+	if (offset) {
+		data = (unsigned long *)(mm_idp->stack + offset - STUB_DATA);
+		printk(UM_KERN_ERR "do_syscall_stub : ret = %ld, offset = %ld, "
+		       "data = %p\n", ret, offset, data);
+		syscall = (unsigned long *)((unsigned long)data + data[0]);
+		printk(UM_KERN_ERR "do_syscall_stub: syscall %ld failed, "
+		       "return value = 0x%lx, expected return value = 0x%lx\n",
+		       syscall[0], ret, syscall[7]);
+		printk(UM_KERN_ERR "    syscall parameters: "
+		       "0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
+		       syscall[1], syscall[2], syscall[3],
+		       syscall[4], syscall[5], syscall[6]);
+		for (n = 1; n < data[0]/sizeof(long); n++) {
+			if (n == 1)
+				printk(UM_KERN_ERR "    additional syscall "
+				       "data:");
+			if (n % 4 == 1)
+				printk("\n" UM_KERN_ERR "      ");
+			printk("  0x%lx", data[n]);
+		}
+		if (n > 1)
+			printk("\n");
+	}
+	else ret = 0;
+
+	*addr = check_init_stack(mm_idp, NULL);
+
+	return ret;
+}
+
+long run_syscall_stub(struct mm_id * mm_idp, int syscall,
+		      unsigned long *args, long expected, void **addr,
+		      int done)
+{
+	unsigned long *stack = check_init_stack(mm_idp, *addr);
+
+	*stack += sizeof(long);
+	stack += *stack / sizeof(long);
+
+	*stack++ = syscall;
+	*stack++ = args[0];
+	*stack++ = args[1];
+	*stack++ = args[2];
+	*stack++ = args[3];
+	*stack++ = args[4];
+	*stack++ = args[5];
+	*stack++ = expected;
+	*stack = 0;
+
+	if (!done && ((((unsigned long) stack) & ~UM_KERN_PAGE_MASK) <
+		     UM_KERN_PAGE_SIZE - 10 * sizeof(long))) {
+		*addr = stack;
+		return 0;
+	}
+
+	return do_syscall_stub(mm_idp, addr);
+}
+
+long syscall_stub_data(struct mm_id * mm_idp,
+		       unsigned long *data, int data_count,
+		       void **addr, void **stub_addr)
+{
+	unsigned long *stack;
+	int ret = 0;
+
+	/*
+	 * If *addr still is uninitialized, it *must* contain NULL.
+	 * Thus in this case do_syscall_stub correctly won't be called.
+	 */
+	if ((((unsigned long) *addr) & ~UM_KERN_PAGE_MASK) >=
+	   UM_KERN_PAGE_SIZE - (10 + data_count) * sizeof(long)) {
+		ret = do_syscall_stub(mm_idp, addr);
+		/* in case of error, don't overwrite data on stack */
+		if (ret)
+			return ret;
+	}
+
+	stack = check_init_stack(mm_idp, *addr);
+	*addr = stack;
+
+	*stack = data_count * sizeof(long);
+
+	memcpy(stack + 1, data, data_count * sizeof(long));
+
+	*stub_addr = (void *)(((unsigned long)(stack + 1) &
+			       ~UM_KERN_PAGE_MASK) + STUB_DATA);
+
+	return 0;
+}
+
+int map(struct mm_id * mm_idp, unsigned long virt, unsigned long len, int prot,
+	int phys_fd, unsigned long long offset, int done, void **data)
+{
+	int ret;
+	unsigned long args[] = { virt, len, prot,
+				 MAP_SHARED | MAP_FIXED, phys_fd,
+				 MMAP_OFFSET(offset) };
+
+	ret = run_syscall_stub(mm_idp, STUB_MMAP_NR, args, virt,
+			       data, done);
+
+	return ret;
+}
+
+int unmap(struct mm_id * mm_idp, unsigned long addr, unsigned long len,
+	  int done, void **data)
+{
+	int ret;
+	unsigned long args[] = { (unsigned long) addr, len, 0, 0, 0,
+				 0 };
+
+	ret = run_syscall_stub(mm_idp, __NR_munmap, args, 0,
+			       data, done);
+
+	return ret;
+}
+
+int protect(struct mm_id * mm_idp, unsigned long addr, unsigned long len,
+	    unsigned int prot, int done, void **data)
+{
+	int ret;
+	unsigned long args[] = { addr, len, prot, 0, 0, 0 };
+
+	ret = run_syscall_stub(mm_idp, __NR_mprotect, args, 0,
+			       data, done);
+
+	return ret;
+}
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
new file mode 100644
index 0000000..df4a985
--- /dev/null
+++ b/arch/um/os-Linux/skas/process.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
+ * Copyright (C) 2002- 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <asm/unistd.h>
+#include <as-layout.h>
+#include <init.h>
+#include <kern_util.h>
+#include <mem.h>
+#include <os.h>
+#include <ptrace_user.h>
+#include <registers.h>
+#include <skas.h>
+#include <sysdep/stub.h>
+#include <linux/threads.h>
+
+int is_skas_winch(int pid, int fd, void *data)
+{
+	return pid == getpgrp();
+}
+
+static int ptrace_dump_regs(int pid)
+{
+	unsigned long regs[MAX_REG_NR];
+	int i;
+
+	if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
+		return -errno;
+
+	printk(UM_KERN_ERR "Stub registers -\n");
+	for (i = 0; i < ARRAY_SIZE(regs); i++)
+		printk(UM_KERN_ERR "\t%d - %lx\n", i, regs[i]);
+
+	return 0;
+}
+
+/*
+ * Signals that are OK to receive in the stub - we'll just continue it.
+ * SIGWINCH will happen when UML is inside a detached screen.
+ */
+#define STUB_SIG_MASK ((1 << SIGALRM) | (1 << SIGWINCH))
+
+/* Signals that the stub will finish with - anything else is an error */
+#define STUB_DONE_MASK (1 << SIGTRAP)
+
+void wait_stub_done(int pid)
+{
+	int n, status, err;
+
+	while (1) {
+		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED | __WALL));
+		if ((n < 0) || !WIFSTOPPED(status))
+			goto bad_wait;
+
+		if (((1 << WSTOPSIG(status)) & STUB_SIG_MASK) == 0)
+			break;
+
+		err = ptrace(PTRACE_CONT, pid, 0, 0);
+		if (err) {
+			printk(UM_KERN_ERR "wait_stub_done : continue failed, "
+			       "errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+	}
+
+	if (((1 << WSTOPSIG(status)) & STUB_DONE_MASK) != 0)
+		return;
+
+bad_wait:
+	err = ptrace_dump_regs(pid);
+	if (err)
+		printk(UM_KERN_ERR "Failed to get registers from stub, "
+		       "errno = %d\n", -err);
+	printk(UM_KERN_ERR "wait_stub_done : failed to wait for SIGTRAP, "
+	       "pid = %d, n = %d, errno = %d, status = 0x%x\n", pid, n, errno,
+	       status);
+	fatal_sigsegv();
+}
+
+extern unsigned long current_stub_stack(void);
+
+static void get_skas_faultinfo(int pid, struct faultinfo *fi, unsigned long *aux_fp_regs)
+{
+	int err;
+
+	err = get_fp_registers(pid, aux_fp_regs);
+	if (err < 0) {
+		printk(UM_KERN_ERR "save_fp_registers returned %d\n",
+		       err);
+		fatal_sigsegv();
+	}
+	err = ptrace(PTRACE_CONT, pid, 0, SIGSEGV);
+	if (err) {
+		printk(UM_KERN_ERR "Failed to continue stub, pid = %d, "
+		       "errno = %d\n", pid, errno);
+		fatal_sigsegv();
+	}
+	wait_stub_done(pid);
+
+	/*
+	 * faultinfo is prepared by the stub_segv_handler at start of
+	 * the stub stack page. We just have to copy it.
+	 */
+	memcpy(fi, (void *)current_stub_stack(), sizeof(*fi));
+
+	err = put_fp_registers(pid, aux_fp_regs);
+	if (err < 0) {
+		printk(UM_KERN_ERR "put_fp_registers returned %d\n",
+		       err);
+		fatal_sigsegv();
+	}
+}
+
+static void handle_segv(int pid, struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
+{
+	get_skas_faultinfo(pid, &regs->faultinfo, aux_fp_regs);
+	segv(regs->faultinfo, 0, 1, NULL);
+}
+
+/*
+ * To use the same value of using_sysemu as the caller, ask it that value
+ * (in local_using_sysemu
+ */
+static void handle_trap(int pid, struct uml_pt_regs *regs,
+			int local_using_sysemu)
+{
+	int err, status;
+
+	if ((UPT_IP(regs) >= STUB_START) && (UPT_IP(regs) < STUB_END))
+		fatal_sigsegv();
+
+	if (!local_using_sysemu)
+	{
+		err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
+			     __NR_getpid);
+		if (err < 0) {
+			printk(UM_KERN_ERR "handle_trap - nullifying syscall "
+			       "failed, errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		err = ptrace(PTRACE_SYSCALL, pid, 0, 0);
+		if (err < 0) {
+			printk(UM_KERN_ERR "handle_trap - continuing to end of "
+			       "syscall failed, errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED | __WALL));
+		if ((err < 0) || !WIFSTOPPED(status) ||
+		    (WSTOPSIG(status) != SIGTRAP + 0x80)) {
+			err = ptrace_dump_regs(pid);
+			if (err)
+				printk(UM_KERN_ERR "Failed to get registers "
+				       "from process, errno = %d\n", -err);
+			printk(UM_KERN_ERR "handle_trap - failed to wait at "
+			       "end of syscall, errno = %d, status = %d\n",
+			       errno, status);
+			fatal_sigsegv();
+		}
+	}
+
+	handle_syscall(regs);
+}
+
+extern char __syscall_stub_start[];
+
+/**
+ * userspace_tramp() - userspace trampoline
+ * @stack:	pointer to the new userspace stack page, can be NULL, if? FIXME:
+ *
+ * The userspace trampoline is used to setup a new userspace process in start_userspace() after it was clone()'ed.
+ * This function will run on a temporary stack page.
+ * It ptrace()'es itself, then
+ * Two pages are mapped into the userspace address space:
+ * - STUB_CODE (with EXEC), which contains the skas stub code
+ * - STUB_DATA (with R/W), which contains a data page that is used to transfer certain data between the UML userspace process and the UML kernel.
+ * Also for the userspace process a SIGSEGV handler is installed to catch pagefaults in the userspace process.
+ * And last the process stops itself to give control to the UML kernel for this userspace process.
+ *
+ * Return: Always zero, otherwise the current userspace process is ended with non null exit() call
+ */
+static int userspace_tramp(void *stack)
+{
+	void *addr;
+	int fd;
+	unsigned long long offset;
+
+	ptrace(PTRACE_TRACEME, 0, 0, 0);
+
+	signal(SIGTERM, SIG_DFL);
+	signal(SIGWINCH, SIG_IGN);
+
+	/*
+	 * This has a pte, but it can't be mapped in with the usual
+	 * tlb_flush mechanism because this is part of that mechanism
+	 */
+	fd = phys_mapping(to_phys(__syscall_stub_start), &offset);
+	addr = mmap64((void *) STUB_CODE, UM_KERN_PAGE_SIZE,
+		      PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset);
+	if (addr == MAP_FAILED) {
+		printk(UM_KERN_ERR "mapping mmap stub at 0x%lx failed, "
+		       "errno = %d\n", STUB_CODE, errno);
+		exit(1);
+	}
+
+	if (stack != NULL) {
+		fd = phys_mapping(to_phys(stack), &offset);
+		addr = mmap((void *) STUB_DATA,
+			    UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE,
+			    MAP_FIXED | MAP_SHARED, fd, offset);
+		if (addr == MAP_FAILED) {
+			printk(UM_KERN_ERR "mapping segfault stack "
+			       "at 0x%lx failed, errno = %d\n",
+			       STUB_DATA, errno);
+			exit(1);
+		}
+	}
+	if (stack != NULL) {
+		struct sigaction sa;
+
+		unsigned long v = STUB_CODE +
+				  (unsigned long) stub_segv_handler -
+				  (unsigned long) __syscall_stub_start;
+
+		set_sigstack((void *) STUB_DATA, UM_KERN_PAGE_SIZE);
+		sigemptyset(&sa.sa_mask);
+		sa.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO;
+		sa.sa_sigaction = (void *) v;
+		sa.sa_restorer = NULL;
+		if (sigaction(SIGSEGV, &sa, NULL) < 0) {
+			printk(UM_KERN_ERR "userspace_tramp - setting SIGSEGV "
+			       "handler failed - errno = %d\n", errno);
+			exit(1);
+		}
+	}
+
+	kill(os_getpid(), SIGSTOP);
+	return 0;
+}
+
+int userspace_pid[NR_CPUS];
+
+/**
+ * start_userspace() - prepare a new userspace process
+ * @stub_stack:	pointer to the stub stack. Can be NULL, if? FIXME:
+ *
+ * Setups a new temporary stack page that is used while userspace_tramp() runs
+ * Clones the kernel process into a new userspace process, with FDs only.
+ *
+ * Return: When positive: the process id of the new userspace process,
+ *         when negative: an error number.
+ * FIXME: can PIDs become negative?!
+ */
+int start_userspace(unsigned long stub_stack)
+{
+	void *stack;
+	unsigned long sp;
+	int pid, status, n, flags, err;
+
+	/* setup a temporary stack page */
+	stack = mmap(NULL, UM_KERN_PAGE_SIZE,
+		     PROT_READ | PROT_WRITE | PROT_EXEC,
+		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (stack == MAP_FAILED) {
+		err = -errno;
+		printk(UM_KERN_ERR "start_userspace : mmap failed, "
+		       "errno = %d\n", errno);
+		return err;
+	}
+
+	/* set stack pointer to the end of the stack page, so it can grow downwards */
+	sp = (unsigned long) stack + UM_KERN_PAGE_SIZE - sizeof(void *);
+
+	flags = CLONE_FILES | SIGCHLD;
+
+	/* clone into new userspace process */
+	pid = clone(userspace_tramp, (void *) sp, flags, (void *) stub_stack);
+	if (pid < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "start_userspace : clone failed, "
+		       "errno = %d\n", errno);
+		return err;
+	}
+
+	do {
+		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED | __WALL));
+		if (n < 0) {
+			err = -errno;
+			printk(UM_KERN_ERR "start_userspace : wait failed, "
+			       "errno = %d\n", errno);
+			goto out_kill;
+		}
+	} while (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGALRM));
+
+	if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) {
+		err = -EINVAL;
+		printk(UM_KERN_ERR "start_userspace : expected SIGSTOP, got "
+		       "status = %d\n", status);
+		goto out_kill;
+	}
+
+	if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
+		   (void *) PTRACE_O_TRACESYSGOOD) < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "start_userspace : PTRACE_OLDSETOPTIONS "
+		       "failed, errno = %d\n", errno);
+		goto out_kill;
+	}
+
+	if (munmap(stack, UM_KERN_PAGE_SIZE) < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "start_userspace : munmap failed, "
+		       "errno = %d\n", errno);
+		goto out_kill;
+	}
+
+	return pid;
+
+ out_kill:
+	os_kill_ptraced_process(pid, 1);
+	return err;
+}
+
+void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs)
+{
+	int err, status, op, pid = userspace_pid[0];
+	/* To prevent races if using_sysemu changes under us.*/
+	int local_using_sysemu;
+	siginfo_t si;
+
+	/* Handle any immediate reschedules or signals */
+	interrupt_end();
+
+	while (1) {
+
+		/*
+		 * This can legitimately fail if the process loads a
+		 * bogus value into a segment register.  It will
+		 * segfault and PTRACE_GETREGS will read that value
+		 * out of the process.  However, PTRACE_SETREGS will
+		 * fail.  In this case, there is nothing to do but
+		 * just kill the process.
+		 */
+		if (ptrace(PTRACE_SETREGS, pid, 0, regs->gp)) {
+			printk(UM_KERN_ERR "userspace - ptrace set regs "
+			       "failed, errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		if (put_fp_registers(pid, regs->fp)) {
+			printk(UM_KERN_ERR "userspace - ptrace set fp regs "
+			       "failed, errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		/* Now we set local_using_sysemu to be used for one loop */
+		local_using_sysemu = get_using_sysemu();
+
+		op = SELECT_PTRACE_OPERATION(local_using_sysemu,
+					     singlestepping(NULL));
+
+		if (ptrace(op, pid, 0, 0)) {
+			printk(UM_KERN_ERR "userspace - ptrace continue "
+			       "failed, op = %d, errno = %d\n", op, errno);
+			fatal_sigsegv();
+		}
+
+		CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED | __WALL));
+		if (err < 0) {
+			printk(UM_KERN_ERR "userspace - wait failed, "
+			       "errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		regs->is_user = 1;
+		if (ptrace(PTRACE_GETREGS, pid, 0, regs->gp)) {
+			printk(UM_KERN_ERR "userspace - PTRACE_GETREGS failed, "
+			       "errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		if (get_fp_registers(pid, regs->fp)) {
+			printk(UM_KERN_ERR "userspace -  get_fp_registers failed, "
+			       "errno = %d\n", errno);
+			fatal_sigsegv();
+		}
+
+		UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */
+
+		if (WIFSTOPPED(status)) {
+			int sig = WSTOPSIG(status);
+
+			ptrace(PTRACE_GETSIGINFO, pid, 0, (struct siginfo *)&si);
+
+			switch (sig) {
+			case SIGSEGV:
+				if (PTRACE_FULL_FAULTINFO) {
+					get_skas_faultinfo(pid,
+							   &regs->faultinfo, aux_fp_regs);
+					(*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si,
+							     regs);
+				}
+				else handle_segv(pid, regs, aux_fp_regs);
+				break;
+			case SIGTRAP + 0x80:
+			        handle_trap(pid, regs, local_using_sysemu);
+				break;
+			case SIGTRAP:
+				relay_signal(SIGTRAP, (struct siginfo *)&si, regs);
+				break;
+			case SIGALRM:
+				break;
+			case SIGIO:
+			case SIGILL:
+			case SIGBUS:
+			case SIGFPE:
+			case SIGWINCH:
+				block_signals();
+				(*sig_info[sig])(sig, (struct siginfo *)&si, regs);
+				unblock_signals();
+				break;
+			default:
+				printk(UM_KERN_ERR "userspace - child stopped "
+				       "with signal %d\n", sig);
+				fatal_sigsegv();
+			}
+			pid = userspace_pid[0];
+			interrupt_end();
+
+			/* Avoid -ERESTARTSYS handling in host */
+			if (PT_SYSCALL_NR_OFFSET != PT_SYSCALL_RET_OFFSET)
+				PT_SYSCALL_NR(regs->gp) = -1;
+		}
+	}
+}
+
+static unsigned long thread_regs[MAX_REG_NR];
+static unsigned long thread_fp_regs[FP_SIZE];
+
+static int __init init_thread_regs(void)
+{
+	get_safe_registers(thread_regs, thread_fp_regs);
+	/* Set parent's instruction pointer to start of clone-stub */
+	thread_regs[REGS_IP_INDEX] = STUB_CODE +
+				(unsigned long) stub_clone_handler -
+				(unsigned long) __syscall_stub_start;
+	thread_regs[REGS_SP_INDEX] = STUB_DATA + UM_KERN_PAGE_SIZE -
+		sizeof(void *);
+#ifdef __SIGNAL_FRAMESIZE
+	thread_regs[REGS_SP_INDEX] -= __SIGNAL_FRAMESIZE;
+#endif
+	return 0;
+}
+
+__initcall(init_thread_regs);
+
+int copy_context_skas0(unsigned long new_stack, int pid)
+{
+	int err;
+	unsigned long current_stack = current_stub_stack();
+	struct stub_data *data = (struct stub_data *) current_stack;
+	struct stub_data *child_data = (struct stub_data *) new_stack;
+	unsigned long long new_offset;
+	int new_fd = phys_mapping(to_phys((void *)new_stack), &new_offset);
+
+	/*
+	 * prepare offset and fd of child's stack as argument for parent's
+	 * and child's mmap2 calls
+	 */
+	*data = ((struct stub_data) {
+			.offset	= MMAP_OFFSET(new_offset),
+			.fd     = new_fd
+	});
+
+	err = ptrace_setregs(pid, thread_regs);
+	if (err < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "copy_context_skas0 : PTRACE_SETREGS "
+		       "failed, pid = %d, errno = %d\n", pid, -err);
+		return err;
+	}
+
+	err = put_fp_registers(pid, thread_fp_regs);
+	if (err < 0) {
+		printk(UM_KERN_ERR "copy_context_skas0 : put_fp_registers "
+		       "failed, pid = %d, err = %d\n", pid, err);
+		return err;
+	}
+
+	/* set a well known return code for detection of child write failure */
+	child_data->err = 12345678;
+
+	/*
+	 * Wait, until parent has finished its work: read child's pid from
+	 * parent's stack, and check, if bad result.
+	 */
+	err = ptrace(PTRACE_CONT, pid, 0, 0);
+	if (err) {
+		err = -errno;
+		printk(UM_KERN_ERR "Failed to continue new process, pid = %d, "
+		       "errno = %d\n", pid, errno);
+		return err;
+	}
+
+	wait_stub_done(pid);
+
+	pid = data->err;
+	if (pid < 0) {
+		printk(UM_KERN_ERR "copy_context_skas0 - stub-parent reports "
+		       "error %d\n", -pid);
+		return pid;
+	}
+
+	/*
+	 * Wait, until child has finished too: read child's result from
+	 * child's stack and check it.
+	 */
+	wait_stub_done(pid);
+	if (child_data->err != STUB_DATA) {
+		printk(UM_KERN_ERR "copy_context_skas0 - stub-child reports "
+		       "error %ld\n", child_data->err);
+		err = child_data->err;
+		goto out_kill;
+	}
+
+	if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
+		   (void *)PTRACE_O_TRACESYSGOOD) < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "copy_context_skas0 : PTRACE_OLDSETOPTIONS "
+		       "failed, errno = %d\n", errno);
+		goto out_kill;
+	}
+
+	return pid;
+
+ out_kill:
+	os_kill_ptraced_process(pid, 1);
+	return err;
+}
+
+void new_thread(void *stack, jmp_buf *buf, void (*handler)(void))
+{
+	(*buf)[0].JB_IP = (unsigned long) handler;
+	(*buf)[0].JB_SP = (unsigned long) stack + UM_THREAD_SIZE -
+		sizeof(void *);
+}
+
+#define INIT_JMP_NEW_THREAD 0
+#define INIT_JMP_CALLBACK 1
+#define INIT_JMP_HALT 2
+#define INIT_JMP_REBOOT 3
+
+void switch_threads(jmp_buf *me, jmp_buf *you)
+{
+	if (UML_SETJMP(me) == 0)
+		UML_LONGJMP(you, 1);
+}
+
+static jmp_buf initial_jmpbuf;
+
+/* XXX Make these percpu */
+static void (*cb_proc)(void *arg);
+static void *cb_arg;
+static jmp_buf *cb_back;
+
+int start_idle_thread(void *stack, jmp_buf *switch_buf)
+{
+	int n;
+
+	set_handler(SIGWINCH);
+
+	/*
+	 * Can't use UML_SETJMP or UML_LONGJMP here because they save
+	 * and restore signals, with the possible side-effect of
+	 * trying to handle any signals which came when they were
+	 * blocked, which can't be done on this stack.
+	 * Signals must be blocked when jumping back here and restored
+	 * after returning to the jumper.
+	 */
+	n = setjmp(initial_jmpbuf);
+	switch (n) {
+	case INIT_JMP_NEW_THREAD:
+		(*switch_buf)[0].JB_IP = (unsigned long) uml_finishsetup;
+		(*switch_buf)[0].JB_SP = (unsigned long) stack +
+			UM_THREAD_SIZE - sizeof(void *);
+		break;
+	case INIT_JMP_CALLBACK:
+		(*cb_proc)(cb_arg);
+		longjmp(*cb_back, 1);
+		break;
+	case INIT_JMP_HALT:
+		kmalloc_ok = 0;
+		return 0;
+	case INIT_JMP_REBOOT:
+		kmalloc_ok = 0;
+		return 1;
+	default:
+		printk(UM_KERN_ERR "Bad sigsetjmp return in "
+		       "start_idle_thread - %d\n", n);
+		fatal_sigsegv();
+	}
+	longjmp(*switch_buf, 1);
+
+	/* unreachable */
+	printk(UM_KERN_ERR "impossible long jump!");
+	fatal_sigsegv();
+	return 0;
+}
+
+void initial_thread_cb_skas(void (*proc)(void *), void *arg)
+{
+	jmp_buf here;
+
+	cb_proc = proc;
+	cb_arg = arg;
+	cb_back = &here;
+
+	block_signals();
+	if (UML_SETJMP(&here) == 0)
+		UML_LONGJMP(&initial_jmpbuf, INIT_JMP_CALLBACK);
+	unblock_signals();
+
+	cb_proc = NULL;
+	cb_arg = NULL;
+	cb_back = NULL;
+}
+
+void halt_skas(void)
+{
+	block_signals();
+	UML_LONGJMP(&initial_jmpbuf, INIT_JMP_HALT);
+}
+
+void reboot_skas(void)
+{
+	block_signals();
+	UML_LONGJMP(&initial_jmpbuf, INIT_JMP_REBOOT);
+}
+
+void __switch_mm(struct mm_id *mm_idp)
+{
+	userspace_pid[0] = mm_idp->u.pid;
+}
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
new file mode 100644
index 0000000..82bf5f8
--- /dev/null
+++ b/arch/um/os-Linux/start_up.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <asm/unistd.h>
+#include <init.h>
+#include <os.h>
+#include <mem_user.h>
+#include <ptrace_user.h>
+#include <registers.h>
+#include <skas.h>
+
+static void ptrace_child(void)
+{
+	int ret;
+	/* Calling os_getpid because some libcs cached getpid incorrectly */
+	int pid = os_getpid(), ppid = getppid();
+	int sc_result;
+
+	if (change_sig(SIGWINCH, 0) < 0 ||
+	    ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
+		perror("ptrace");
+		kill(pid, SIGKILL);
+	}
+	kill(pid, SIGSTOP);
+
+	/*
+	 * This syscall will be intercepted by the parent. Don't call more than
+	 * once, please.
+	 */
+	sc_result = os_getpid();
+
+	if (sc_result == pid)
+		/* Nothing modified by the parent, we are running normally. */
+		ret = 1;
+	else if (sc_result == ppid)
+		/*
+		 * Expected in check_ptrace and check_sysemu when they succeed
+		 * in modifying the stack frame
+		 */
+		ret = 0;
+	else
+		/* 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.
+		 */
+		ret = 2;
+
+	exit(ret);
+}
+
+static void fatal_perror(const char *str)
+{
+	perror(str);
+	exit(1);
+}
+
+static void fatal(char *fmt, ...)
+{
+	va_list list;
+
+	va_start(list, fmt);
+	vfprintf(stderr, fmt, list);
+	va_end(list);
+
+	exit(1);
+}
+
+static void non_fatal(char *fmt, ...)
+{
+	va_list list;
+
+	va_start(list, fmt);
+	vfprintf(stderr, fmt, list);
+	va_end(list);
+}
+
+static int start_ptraced_child(void)
+{
+	int pid, n, status;
+
+	fflush(stdout);
+
+	pid = fork();
+	if (pid == 0)
+		ptrace_child();
+	else if (pid < 0)
+		fatal_perror("start_ptraced_child : fork failed");
+
+	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+	if (n < 0)
+		fatal_perror("check_ptrace : waitpid failed");
+	if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
+		fatal("check_ptrace : expected SIGSTOP, got status = %d",
+		      status);
+
+	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, int exitcode, int mustexit)
+{
+	int status, n, ret = 0;
+
+	if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
+		perror("stop_ptraced_child : ptrace failed");
+		return -1;
+	}
+	CATCH_EINTR(n = waitpid(pid, &status, 0));
+	if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
+		int exit_with = WEXITSTATUS(status);
+		if (exit_with == 2)
+			non_fatal("check_ptrace : child exited with status 2. "
+				  "\nDisabling SYSEMU support.\n");
+		non_fatal("check_ptrace : child exited with exitcode %d, while "
+			  "expecting %d; status 0x%x\n", exit_with,
+			  exitcode, status);
+		if (mustexit)
+			exit(1);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+/* Changed only during early boot */
+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).\n"
+"    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
+"    behaviour of ptrace() and helps reduce host context switch rates.\n"
+"    To make it work, 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)
+{
+	unsigned long regs[MAX_REG_NR];
+	int pid, n, status, count=0;
+
+	os_info("Checking syscall emulation patch for ptrace...");
+	sysemu_supported = 0;
+	pid = start_ptraced_child();
+
+	if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
+		goto fail;
+
+	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+	if (n < 0)
+		fatal_perror("check_sysemu : wait failed");
+	if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
+		fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
+		      status);
+
+	if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
+		fatal_perror("check_sysemu : PTRACE_GETREGS failed");
+	if (PT_SYSCALL_NR(regs) != __NR_getpid) {
+		non_fatal("check_sysemu got system call number %d, "
+			  "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
+		goto fail;
+	}
+
+	n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
+	if (n < 0) {
+		non_fatal("check_sysemu : failed to modify system call "
+			  "return");
+		goto fail;
+	}
+
+	if (stop_ptraced_child(pid, 0, 0) < 0)
+		goto fail_stopped;
+
+	sysemu_supported = 1;
+	os_info("OK\n");
+	set_using_sysemu(!force_sysemu_disabled);
+
+	os_info("Checking advanced syscall emulation patch for ptrace...");
+	pid = start_ptraced_child();
+
+	if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
+		   (void *) PTRACE_O_TRACESYSGOOD) < 0))
+		fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
+
+	while (1) {
+		count++;
+		if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
+			goto fail;
+		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+		if (n < 0)
+			fatal_perror("check_sysemu: wait failed");
+
+		if (WIFSTOPPED(status) &&
+		    (WSTOPSIG(status) == (SIGTRAP|0x80))) {
+			if (!count) {
+				non_fatal("check_sysemu: SYSEMU_SINGLESTEP "
+					  "doesn't singlestep");
+				goto fail;
+			}
+			n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
+				   os_getpid());
+			if (n < 0)
+				fatal_perror("check_sysemu : failed to modify "
+					     "system call return");
+			break;
+		}
+		else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
+			count++;
+		else {
+			non_fatal("check_sysemu: expected SIGTRAP or "
+				  "(SIGTRAP | 0x80), got status = %d\n",
+				  status);
+			goto fail;
+		}
+	}
+	if (stop_ptraced_child(pid, 0, 0) < 0)
+		goto fail_stopped;
+
+	sysemu_supported = 2;
+	os_info("OK\n");
+
+	if (!force_sysemu_disabled)
+		set_using_sysemu(sysemu_supported);
+	return;
+
+fail:
+	stop_ptraced_child(pid, 1, 0);
+fail_stopped:
+	non_fatal("missing\n");
+}
+
+static void __init check_ptrace(void)
+{
+	int pid, syscall, n, status;
+
+	os_info("Checking that ptrace can change system call numbers...");
+	pid = start_ptraced_child();
+
+	if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
+		   (void *) PTRACE_O_TRACESYSGOOD) < 0))
+		fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
+
+	while (1) {
+		if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
+			fatal_perror("check_ptrace : ptrace failed");
+
+		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
+		if (n < 0)
+			fatal_perror("check_ptrace : wait failed");
+
+		if (!WIFSTOPPED(status) ||
+		   (WSTOPSIG(status) != (SIGTRAP | 0x80)))
+			fatal("check_ptrace : expected (SIGTRAP|0x80), "
+			       "got status = %d", status);
+
+		syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
+				 0);
+		if (syscall == __NR_getpid) {
+			n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
+				   __NR_getppid);
+			if (n < 0)
+				fatal_perror("check_ptrace : failed to modify "
+					     "system call");
+			break;
+		}
+	}
+	stop_ptraced_child(pid, 0, 1);
+	os_info("OK\n");
+	check_sysemu();
+}
+
+extern void check_tmpexec(void);
+
+static void __init check_coredump_limit(void)
+{
+	struct rlimit lim;
+	int err = getrlimit(RLIMIT_CORE, &lim);
+
+	if (err) {
+		perror("Getting core dump limit");
+		return;
+	}
+
+	os_info("Core dump limits :\n\tsoft - ");
+	if (lim.rlim_cur == RLIM_INFINITY)
+		os_info("NONE\n");
+	else
+		os_info("%llu\n", (unsigned long long)lim.rlim_cur);
+
+	os_info("\thard - ");
+	if (lim.rlim_max == RLIM_INFINITY)
+		os_info("NONE\n");
+	else
+		os_info("%llu\n", (unsigned long long)lim.rlim_max);
+}
+
+void __init os_early_checks(void)
+{
+	int pid;
+
+	/* Print out the core dump limits early */
+	check_coredump_limit();
+
+	check_ptrace();
+
+	/* Need to check this early because mmapping happens before the
+	 * kernel is running.
+	 */
+	check_tmpexec();
+
+	pid = start_ptraced_child();
+	if (init_registers(pid))
+		fatal("Failed to initialize default registers");
+	stop_ptraced_child(pid, 1, 1);
+}
+
+int __init parse_iomem(char *str, int *add)
+{
+	struct iomem_region *new;
+	struct stat64 buf;
+	char *file, *driver;
+	int fd, size;
+
+	driver = str;
+	file = strchr(str,',');
+	if (file == NULL) {
+		os_warn("parse_iomem : failed to parse iomem\n");
+		goto out;
+	}
+	*file = '\0';
+	file++;
+	fd = open(file, O_RDWR, 0);
+	if (fd < 0) {
+		perror("parse_iomem - Couldn't open io file");
+		goto out;
+	}
+
+	if (fstat64(fd, &buf) < 0) {
+		perror("parse_iomem - cannot stat_fd file");
+		goto out_close;
+	}
+
+	new = malloc(sizeof(*new));
+	if (new == NULL) {
+		perror("Couldn't allocate iomem_region struct");
+		goto out_close;
+	}
+
+	size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
+
+	*new = ((struct iomem_region) { .next		= iomem_regions,
+					.driver		= driver,
+					.fd		= fd,
+					.size		= size,
+					.phys		= 0,
+					.virt		= 0 });
+	iomem_regions = new;
+	iomem_size += new->size + UM_KERN_PAGE_SIZE;
+
+	return 0;
+ out_close:
+	close(fd);
+ out:
+	return 1;
+}
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c
new file mode 100644
index 0000000..0e39b99
--- /dev/null
+++ b/arch/um/os-Linux/time.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
+ * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
+ * Copyright (C) 2012-2014 Cisco Systems
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+#include <kern_util.h>
+#include <os.h>
+#include <string.h>
+#include <timer-internal.h>
+
+static timer_t event_high_res_timer = 0;
+
+static inline long long timeval_to_ns(const struct timeval *tv)
+{
+	return ((long long) tv->tv_sec * UM_NSEC_PER_SEC) +
+		tv->tv_usec * UM_NSEC_PER_USEC;
+}
+
+static inline long long timespec_to_ns(const struct timespec *ts)
+{
+	return ((long long) ts->tv_sec * UM_NSEC_PER_SEC) +
+		ts->tv_nsec;
+}
+
+long long os_persistent_clock_emulation (void) {
+	struct timespec realtime_tp;
+
+	clock_gettime(CLOCK_REALTIME, &realtime_tp);
+	return timespec_to_ns(&realtime_tp);
+}
+
+/**
+ * os_timer_create() - create an new posix (interval) timer
+ */
+int os_timer_create(void* timer) {
+
+	timer_t* t = timer;
+
+	if(t == NULL) {
+		t = &event_high_res_timer;
+	}
+
+	if (timer_create(
+		CLOCK_MONOTONIC,
+		NULL,
+		t) == -1) {
+		return -1;
+	}
+	return 0;
+}
+
+int os_timer_set_interval(void* timer, void* i)
+{
+	struct itimerspec its;
+	unsigned long long nsec;
+	timer_t* t = timer;
+	struct itimerspec* its_in = i;
+
+	if(t == NULL) {
+		t = &event_high_res_timer;
+	}
+
+	nsec = UM_NSEC_PER_SEC / UM_HZ;
+
+	if(its_in != NULL) {
+		its.it_value.tv_sec = its_in->it_value.tv_sec;
+		its.it_value.tv_nsec = its_in->it_value.tv_nsec;
+	} else {
+		its.it_value.tv_sec = 0;
+		its.it_value.tv_nsec = nsec;
+	}
+
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = nsec;
+
+	if(timer_settime(*t, 0, &its, NULL) == -1) {
+		return -errno;
+	}
+
+	return 0;
+}
+
+/**
+ * os_timer_remain() - returns the remaining nano seconds of the given interval
+ *                     timer
+ * Because this is the remaining time of an interval timer, which correspondends
+ * to HZ, this value can never be bigger than one second. Just
+ * the nanosecond part of the timer is returned.
+ * The returned time is relative to the start time of the interval timer.
+ * Return an negative value in an error case.
+ */
+long os_timer_remain(void* timer)
+{
+	struct itimerspec its;
+	timer_t* t = timer;
+
+	if(t == NULL) {
+		t = &event_high_res_timer;
+	}
+
+	if(timer_gettime(t, &its) == -1) {
+		return -errno;
+	}
+
+	return its.it_value.tv_nsec;
+}
+
+int os_timer_one_shot(int ticks)
+{
+	struct itimerspec its;
+	unsigned long long nsec;
+	unsigned long sec;
+
+    nsec = (ticks + 1);
+    sec = nsec / UM_NSEC_PER_SEC;
+	nsec = nsec % UM_NSEC_PER_SEC;
+
+	its.it_value.tv_sec = nsec / UM_NSEC_PER_SEC;
+	its.it_value.tv_nsec = nsec;
+
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = 0; // we cheat here
+
+	timer_settime(event_high_res_timer, 0, &its, NULL);
+	return 0;
+}
+
+/**
+ * os_timer_disable() - disable the posix (interval) timer
+ * Returns the remaining interval timer time in nanoseconds
+ */
+long long os_timer_disable(void)
+{
+	struct itimerspec its;
+
+	memset(&its, 0, sizeof(struct itimerspec));
+	timer_settime(event_high_res_timer, 0, &its, &its);
+
+	return its.it_value.tv_sec * UM_NSEC_PER_SEC + its.it_value.tv_nsec;
+}
+
+long long os_vnsecs(void)
+{
+	struct timespec ts;
+
+	clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&ts);
+	return timespec_to_ns(&ts);
+}
+
+long long os_nsecs(void)
+{
+	struct timespec ts;
+
+	clock_gettime(CLOCK_MONOTONIC,&ts);
+	return timespec_to_ns(&ts);
+}
+
+/**
+ * os_idle_sleep() - sleep for a given time of nsecs
+ * @nsecs: nanoseconds to sleep
+ */
+void os_idle_sleep(unsigned long long nsecs)
+{
+	struct timespec ts;
+
+	if (nsecs <= 0) {
+		return;
+	}
+
+	ts = ((struct timespec) {
+			.tv_sec  = nsecs / UM_NSEC_PER_SEC,
+			.tv_nsec = nsecs % UM_NSEC_PER_SEC
+	});
+
+	/*
+	 * Relay the signal if clock_nanosleep is interrupted.
+	 */
+	if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL)) {
+		deliver_alarm();
+	}
+}
diff --git a/arch/um/os-Linux/tty.c b/arch/um/os-Linux/tty.c
new file mode 100644
index 0000000..721d8af
--- /dev/null
+++ b/arch/um/os-Linux/tty.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kern_util.h>
+#include <os.h>
+
+struct grantpt_info {
+	int fd;
+	int res;
+	int err;
+};
+
+static void grantpt_cb(void *arg)
+{
+	struct grantpt_info *info = arg;
+
+	info->res = grantpt(info->fd);
+	info->err = errno;
+}
+
+int get_pty(void)
+{
+	struct grantpt_info info;
+	int fd, err;
+
+	fd = open("/dev/ptmx", O_RDWR);
+	if (fd < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "get_pty : Couldn't open /dev/ptmx - "
+		       "err = %d\n", errno);
+		return err;
+	}
+
+	info.fd = fd;
+	initial_thread_cb(grantpt_cb, &info);
+
+	if (info.res < 0) {
+		err = -info.err;
+		printk(UM_KERN_ERR "get_pty : Couldn't grant pty - "
+		       "errno = %d\n", -info.err);
+		goto out;
+	}
+
+	if (unlockpt(fd) < 0) {
+		err = -errno;
+		printk(UM_KERN_ERR "get_pty : Couldn't unlock pty - "
+		       "errno = %d\n", errno);
+		goto out;
+	}
+	return fd;
+out:
+	close(fd);
+	return err;
+}
diff --git a/arch/um/os-Linux/umid.c b/arch/um/os-Linux/umid.c
new file mode 100644
index 0000000..998fbb4
--- /dev/null
+++ b/arch/um/os-Linux/umid.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <init.h>
+#include <os.h>
+
+#define UML_DIR "~/.uml/"
+
+#define UMID_LEN 64
+
+/* Changed by set_umid, which is run early in boot */
+static char umid[UMID_LEN] = { 0 };
+
+/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
+static char *uml_dir = UML_DIR;
+
+static int __init make_uml_dir(void)
+{
+	char dir[512] = { '\0' };
+	int len, err;
+
+	if (*uml_dir == '~') {
+		char *home = getenv("HOME");
+
+		err = -ENOENT;
+		if (home == NULL) {
+			printk(UM_KERN_ERR
+				"%s: no value in environment for $HOME\n",
+				__func__);
+			goto err;
+		}
+		strlcpy(dir, home, sizeof(dir));
+		uml_dir++;
+	}
+	strlcat(dir, uml_dir, sizeof(dir));
+	len = strlen(dir);
+	if (len > 0 && dir[len - 1] != '/')
+		strlcat(dir, "/", sizeof(dir));
+
+	err = -ENOMEM;
+	uml_dir = malloc(strlen(dir) + 1);
+	if (uml_dir == NULL) {
+		printk(UM_KERN_ERR "%s : malloc failed, errno = %d\n",
+			__func__, errno);
+		goto err;
+	}
+	strcpy(uml_dir, dir);
+
+	if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) {
+		printk(UM_KERN_ERR "Failed to mkdir '%s': %s\n",
+			uml_dir, strerror(errno));
+		err = -errno;
+		goto err_free;
+	}
+	return 0;
+
+err_free:
+	free(uml_dir);
+err:
+	uml_dir = NULL;
+	return err;
+}
+
+/*
+ * Unlinks the files contained in @dir and then removes @dir.
+ * Doesn't handle directory trees, so it's not like rm -rf, but almost such. We
+ * ignore ENOENT errors for anything (they happen, strangely enough - possibly
+ * due to races between multiple dying UML threads).
+ */
+static int remove_files_and_dir(char *dir)
+{
+	DIR *directory;
+	struct dirent *ent;
+	int len;
+	char file[256];
+	int ret;
+
+	directory = opendir(dir);
+	if (directory == NULL) {
+		if (errno != ENOENT)
+			return -errno;
+		else
+			return 0;
+	}
+
+	while ((ent = readdir(directory)) != NULL) {
+		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
+			continue;
+		len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
+		if (len > sizeof(file)) {
+			ret = -E2BIG;
+			goto out;
+		}
+
+		sprintf(file, "%s/%s", dir, ent->d_name);
+		if (unlink(file) < 0 && errno != ENOENT) {
+			ret = -errno;
+			goto out;
+		}
+	}
+
+	if (rmdir(dir) < 0 && errno != ENOENT) {
+		ret = -errno;
+		goto out;
+	}
+
+	ret = 0;
+out:
+	closedir(directory);
+	return ret;
+}
+
+/*
+ * This says that there isn't already a user of the specified directory even if
+ * there are errors during the checking.  This is because if these errors
+ * happen, the directory is unusable by the pre-existing UML, so we might as
+ * well take it over.  This could happen either by
+ * 	the existing UML somehow corrupting its umid directory
+ * 	something other than UML sticking stuff in the directory
+ *	this boot racing with a shutdown of the other UML
+ * In any of these cases, the directory isn't useful for anything else.
+ *
+ * Boolean return: 1 if in use, 0 otherwise.
+ */
+static inline int is_umdir_used(char *dir)
+{
+	char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
+	char pid[sizeof("nnnnn\0")], *end;
+	int dead, fd, p, n, err;
+
+	n = snprintf(file, sizeof(file), "%s/pid", dir);
+	if (n >= sizeof(file)) {
+		printk(UM_KERN_ERR "is_umdir_used - pid filename too long\n");
+		err = -E2BIG;
+		goto out;
+	}
+
+	dead = 0;
+	fd = open(file, O_RDONLY);
+	if (fd < 0) {
+		fd = -errno;
+		if (fd != -ENOENT) {
+			printk(UM_KERN_ERR "is_umdir_used : couldn't open pid "
+			       "file '%s', err = %d\n", file, -fd);
+		}
+		goto out;
+	}
+
+	err = 0;
+	n = read(fd, pid, sizeof(pid));
+	if (n < 0) {
+		printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
+		       "'%s', err = %d\n", file, errno);
+		goto out_close;
+	} else if (n == 0) {
+		printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
+		       "'%s', 0-byte read\n", file);
+		goto out_close;
+	}
+
+	p = strtoul(pid, &end, 0);
+	if (end == pid) {
+		printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file "
+		       "'%s', errno = %d\n", file, errno);
+		goto out_close;
+	}
+
+	if ((kill(p, 0) == 0) || (errno != ESRCH)) {
+		printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n",
+		       umid, p);
+		return 1;
+	}
+
+out_close:
+	close(fd);
+out:
+	return 0;
+}
+
+/*
+ * Try to remove the directory @dir unless it's in use.
+ * Precondition: @dir exists.
+ * Returns 0 for success, < 0 for failure in removal or if the directory is in
+ * use.
+ */
+static int umdir_take_if_dead(char *dir)
+{
+	int ret;
+	if (is_umdir_used(dir))
+		return -EEXIST;
+
+	ret = remove_files_and_dir(dir);
+	if (ret) {
+		printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir "
+		       "failed with err = %d\n", ret);
+	}
+	return ret;
+}
+
+static void __init create_pid_file(void)
+{
+	char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
+	char pid[sizeof("nnnnn\0")];
+	int fd, n;
+
+	if (umid_file_name("pid", file, sizeof(file)))
+		return;
+
+	fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
+	if (fd < 0) {
+		printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: "
+		       "%s\n", file, strerror(errno));
+		return;
+	}
+
+	snprintf(pid, sizeof(pid), "%d\n", getpid());
+	n = write(fd, pid, strlen(pid));
+	if (n != strlen(pid))
+		printk(UM_KERN_ERR "Write of pid file failed - err = %d\n",
+		       errno);
+
+	close(fd);
+}
+
+int __init set_umid(char *name)
+{
+	if (strlen(name) > UMID_LEN - 1)
+		return -E2BIG;
+
+	strlcpy(umid, name, sizeof(umid));
+
+	return 0;
+}
+
+/* Changed in make_umid, which is called during early boot */
+static int umid_setup = 0;
+
+static int __init make_umid(void)
+{
+	int fd, err;
+	char tmp[256];
+
+	if (umid_setup)
+		return 0;
+
+	make_uml_dir();
+
+	if (*umid == '\0') {
+		strlcpy(tmp, uml_dir, sizeof(tmp));
+		strlcat(tmp, "XXXXXX", sizeof(tmp));
+		fd = mkstemp(tmp);
+		if (fd < 0) {
+			printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
+			       "%s\n", tmp, strerror(errno));
+			err = -errno;
+			goto err;
+		}
+
+		close(fd);
+
+		set_umid(&tmp[strlen(uml_dir)]);
+
+		/*
+		 * There's a nice tiny little race between this unlink and
+		 * the mkdir below.  It'd be nice if there were a mkstemp
+		 * for directories.
+		 */
+		if (unlink(tmp)) {
+			err = -errno;
+			goto err;
+		}
+	}
+
+	snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
+	err = mkdir(tmp, 0777);
+	if (err < 0) {
+		err = -errno;
+		if (err != -EEXIST)
+			goto err;
+
+		if (umdir_take_if_dead(tmp) < 0)
+			goto err;
+
+		err = mkdir(tmp, 0777);
+	}
+	if (err) {
+		err = -errno;
+		printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid,
+		       errno);
+		goto err;
+	}
+
+	umid_setup = 1;
+
+	create_pid_file();
+
+	err = 0;
+ err:
+	return err;
+}
+
+static int __init make_umid_init(void)
+{
+	if (!make_umid())
+		return 0;
+
+	/*
+	 * If initializing with the given umid failed, then try again with
+	 * a random one.
+	 */
+	printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a "
+	       "random umid\n", umid);
+	*umid = '\0';
+	make_umid();
+
+	return 0;
+}
+
+__initcall(make_umid_init);
+
+int __init umid_file_name(char *name, char *buf, int len)
+{
+	int n, err;
+
+	err = make_umid();
+	if (err)
+		return err;
+
+	n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
+	if (n >= len) {
+		printk(UM_KERN_ERR "umid_file_name : buffer too short\n");
+		return -E2BIG;
+	}
+
+	return 0;
+}
+
+char *get_umid(void)
+{
+	return umid;
+}
+
+static int __init set_uml_dir(char *name, int *add)
+{
+	if (*name == '\0') {
+		os_warn("uml_dir can't be an empty string\n");
+		return 0;
+	}
+
+	if (name[strlen(name) - 1] == '/') {
+		uml_dir = name;
+		return 0;
+	}
+
+	uml_dir = malloc(strlen(name) + 2);
+	if (uml_dir == NULL) {
+		os_warn("Failed to malloc uml_dir - error = %d\n", errno);
+
+		/*
+		 * Return 0 here because do_initcalls doesn't look at
+		 * the return value.
+		 */
+		return 0;
+	}
+	sprintf(uml_dir, "%s/", name);
+
+	return 0;
+}
+
+__uml_setup("uml_dir=", set_uml_dir,
+"uml_dir=<directory>\n"
+"    The location to place the pid and umid files.\n\n"
+);
+
+static void remove_umid_dir(void)
+{
+	char dir[strlen(uml_dir) + UMID_LEN + 1], err;
+
+	sprintf(dir, "%s%s", uml_dir, umid);
+	err = remove_files_and_dir(dir);
+	if (err)
+		os_warn("%s - remove_files_and_dir failed with err = %d\n",
+			__func__, err);
+}
+
+__uml_exitcall(remove_umid_dir);
diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c
new file mode 100644
index 0000000..715594f
--- /dev/null
+++ b/arch/um/os-Linux/user_syms.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/types.h>
+#include <linux/module.h>
+
+/* Some of this are builtin function (some are not but could in the future),
+ * so I *must* declare good prototypes for them and then EXPORT them.
+ * The kernel code uses the macro defined by include/linux/string.h,
+ * so I undef macros; the userspace code does not include that and I
+ * add an EXPORT for the glibc one.
+ */
+
+#undef strlen
+#undef strstr
+#undef memcpy
+#undef memset
+
+extern size_t strlen(const char *);
+extern void *memmove(void *, const void *, size_t);
+extern void *memset(void *, int, size_t);
+extern int printf(const char *, ...);
+
+/* If it's not defined, the export is included in lib/string.c.*/
+#ifdef __HAVE_ARCH_STRSTR
+EXPORT_SYMBOL(strstr);
+#endif
+
+#ifndef __x86_64__
+extern void *memcpy(void *, const void *, size_t);
+EXPORT_SYMBOL(memcpy);
+#endif
+
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(printf);
+
+/* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms.
+ * However, the modules will use the CRC defined *here*, no matter if it is
+ * good; so the versions of these symbols will always match
+ */
+#define EXPORT_SYMBOL_PROTO(sym)       \
+	int sym(void);                  \
+	EXPORT_SYMBOL(sym);
+
+extern void readdir64(void) __attribute__((weak));
+EXPORT_SYMBOL(readdir64);
+extern void truncate64(void) __attribute__((weak));
+EXPORT_SYMBOL(truncate64);
+
+#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
+EXPORT_SYMBOL(vsyscall_ehdr);
+EXPORT_SYMBOL(vsyscall_end);
+#endif
+
+EXPORT_SYMBOL_PROTO(__errno_location);
+
+EXPORT_SYMBOL_PROTO(access);
+EXPORT_SYMBOL_PROTO(open);
+EXPORT_SYMBOL_PROTO(open64);
+EXPORT_SYMBOL_PROTO(close);
+EXPORT_SYMBOL_PROTO(read);
+EXPORT_SYMBOL_PROTO(write);
+EXPORT_SYMBOL_PROTO(dup2);
+EXPORT_SYMBOL_PROTO(__xstat);
+EXPORT_SYMBOL_PROTO(__lxstat);
+EXPORT_SYMBOL_PROTO(__lxstat64);
+EXPORT_SYMBOL_PROTO(__fxstat64);
+EXPORT_SYMBOL_PROTO(lseek);
+EXPORT_SYMBOL_PROTO(lseek64);
+EXPORT_SYMBOL_PROTO(chown);
+EXPORT_SYMBOL_PROTO(fchown);
+EXPORT_SYMBOL_PROTO(truncate);
+EXPORT_SYMBOL_PROTO(ftruncate64);
+EXPORT_SYMBOL_PROTO(utime);
+EXPORT_SYMBOL_PROTO(utimes);
+EXPORT_SYMBOL_PROTO(futimes);
+EXPORT_SYMBOL_PROTO(chmod);
+EXPORT_SYMBOL_PROTO(fchmod);
+EXPORT_SYMBOL_PROTO(rename);
+EXPORT_SYMBOL_PROTO(__xmknod);
+
+EXPORT_SYMBOL_PROTO(symlink);
+EXPORT_SYMBOL_PROTO(link);
+EXPORT_SYMBOL_PROTO(unlink);
+EXPORT_SYMBOL_PROTO(readlink);
+
+EXPORT_SYMBOL_PROTO(mkdir);
+EXPORT_SYMBOL_PROTO(rmdir);
+EXPORT_SYMBOL_PROTO(opendir);
+EXPORT_SYMBOL_PROTO(readdir);
+EXPORT_SYMBOL_PROTO(closedir);
+EXPORT_SYMBOL_PROTO(seekdir);
+EXPORT_SYMBOL_PROTO(telldir);
+
+EXPORT_SYMBOL_PROTO(ioctl);
+
+EXPORT_SYMBOL_PROTO(pread64);
+EXPORT_SYMBOL_PROTO(pwrite64);
+
+EXPORT_SYMBOL_PROTO(statfs);
+EXPORT_SYMBOL_PROTO(statfs64);
+
+EXPORT_SYMBOL_PROTO(getuid);
+
+EXPORT_SYMBOL_PROTO(fsync);
+EXPORT_SYMBOL_PROTO(fdatasync);
+
+EXPORT_SYMBOL_PROTO(lstat64);
+EXPORT_SYMBOL_PROTO(fstat64);
+EXPORT_SYMBOL_PROTO(mknod);
+
+/* Export symbols used by GCC for the stack protector. */
+extern void __stack_smash_handler(void *) __attribute__((weak));
+EXPORT_SYMBOL(__stack_smash_handler);
+
+extern long __guard __attribute__((weak));
+EXPORT_SYMBOL(__guard);
+
+#ifdef _FORTIFY_SOURCE
+extern int __sprintf_chk(char *str, int flag, size_t strlen, const char *format);
+EXPORT_SYMBOL(__sprintf_chk);
+#endif
diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c
new file mode 100644
index 0000000..8cc8b26
--- /dev/null
+++ b/arch/um/os-Linux/util.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <wait.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <init.h>
+#include <os.h>
+
+void stack_protections(unsigned long address)
+{
+	if (mprotect((void *) address, UM_THREAD_SIZE,
+		    PROT_READ | PROT_WRITE | PROT_EXEC) < 0)
+		panic("protecting stack failed, errno = %d", errno);
+}
+
+int raw(int fd)
+{
+	struct termios tt;
+	int err;
+
+	CATCH_EINTR(err = tcgetattr(fd, &tt));
+	if (err < 0)
+		return -errno;
+
+	cfmakeraw(&tt);
+
+	CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt));
+	if (err < 0)
+		return -errno;
+
+	/*
+	 * XXX tcsetattr could have applied only some changes
+	 * (and cfmakeraw() is a set of changes)
+	 */
+	return 0;
+}
+
+void setup_machinename(char *machine_out)
+{
+	struct utsname host;
+
+	uname(&host);
+#ifdef UML_CONFIG_UML_X86
+# ifndef UML_CONFIG_64BIT
+	if (!strcmp(host.machine, "x86_64")) {
+		strcpy(machine_out, "i686");
+		return;
+	}
+# else
+	if (!strcmp(host.machine, "i686")) {
+		strcpy(machine_out, "x86_64");
+		return;
+	}
+# endif
+#endif
+	strcpy(machine_out, host.machine);
+}
+
+void setup_hostinfo(char *buf, int len)
+{
+	struct utsname host;
+
+	uname(&host);
+	snprintf(buf, len, "%s %s %s %s %s", host.sysname, host.nodename,
+		 host.release, host.version, host.machine);
+}
+
+/*
+ * We cannot use glibc's abort(). It makes use of tgkill() which
+ * has no effect within UML's kernel threads.
+ * After that glibc would execute an invalid instruction to kill
+ * the calling process and UML crashes with SIGSEGV.
+ */
+static inline void __attribute__ ((noreturn)) uml_abort(void)
+{
+	sigset_t sig;
+
+	fflush(NULL);
+
+	if (!sigemptyset(&sig) && !sigaddset(&sig, SIGABRT))
+		sigprocmask(SIG_UNBLOCK, &sig, 0);
+
+	for (;;)
+		if (kill(getpid(), SIGABRT) < 0)
+			exit(127);
+}
+
+/*
+ * UML helper threads must not handle SIGWINCH/INT/TERM
+ */
+void os_fix_helper_signals(void)
+{
+	signal(SIGWINCH, SIG_IGN);
+	signal(SIGINT, SIG_DFL);
+	signal(SIGTERM, SIG_DFL);
+}
+
+void os_dump_core(void)
+{
+	int pid;
+
+	signal(SIGSEGV, SIG_DFL);
+
+	/*
+	 * We are about to SIGTERM this entire process group to ensure that
+	 * nothing is around to run after the kernel exits.  The
+	 * kernel wants to abort, not die through SIGTERM, so we
+	 * ignore it here.
+	 */
+
+	signal(SIGTERM, SIG_IGN);
+	kill(0, SIGTERM);
+	/*
+	 * Most of the other processes associated with this UML are
+	 * likely sTopped, so give them a SIGCONT so they see the
+	 * SIGTERM.
+	 */
+	kill(0, SIGCONT);
+
+	/*
+	 * Now, having sent signals to everyone but us, make sure they
+	 * die by ptrace.  Processes can survive what's been done to
+	 * them so far - the mechanism I understand is receiving a
+	 * SIGSEGV and segfaulting immediately upon return.  There is
+	 * always a SIGSEGV pending, and (I'm guessing) signals are
+	 * processed in numeric order so the SIGTERM (signal 15 vs
+	 * SIGSEGV being signal 11) is never handled.
+	 *
+	 * Run a waitpid loop until we get some kind of error.
+	 * Hopefully, it's ECHILD, but there's not a lot we can do if
+	 * it's something else.  Tell os_kill_ptraced_process not to
+	 * wait for the child to report its death because there's
+	 * nothing reasonable to do if that fails.
+	 */
+
+	while ((pid = waitpid(-1, NULL, WNOHANG | __WALL)) > 0)
+		os_kill_ptraced_process(pid, 0);
+
+	uml_abort();
+}
+
+void um_early_printk(const char *s, unsigned int n)
+{
+	printf("%.*s", n, s);
+}
+
+static int quiet_info;
+
+static int __init quiet_cmd_param(char *str, int *add)
+{
+	quiet_info = 1;
+	return 0;
+}
+
+__uml_setup("quiet", quiet_cmd_param,
+"quiet\n"
+"    Turns off information messages during boot.\n\n");
+
+void os_info(const char *fmt, ...)
+{
+	va_list list;
+
+	if (quiet_info)
+		return;
+
+	va_start(list, fmt);
+	vfprintf(stderr, fmt, list);
+	va_end(list);
+}
+
+void os_warn(const char *fmt, ...)
+{
+	va_list list;
+
+	va_start(list, fmt);
+	vfprintf(stderr, fmt, list);
+	va_end(list);
+}