Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
28
arch/um/os-Linux/Makefile
Normal file
28
arch/um/os-Linux/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-y = aio.o elf_aux.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
|
||||
sigio.o signal.o start_up.o time.o trap.o tty.o uaccess.o umid.o tls.o \
|
||||
user_syms.o util.o drivers/ sys-$(SUBARCH)/
|
||||
|
||||
obj-$(CONFIG_MODE_SKAS) += skas/
|
||||
|
||||
obj-$(CONFIG_MODE_TT) += tt.o
|
||||
user-objs-$(CONFIG_MODE_TT) += tt.o
|
||||
|
||||
obj-$(CONFIG_TTY_LOG) += tty_log.o
|
||||
user-objs-$(CONFIG_TTY_LOG) += tty_log.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 sigio.o signal.o start_up.o time.o trap.o tty.o \
|
||||
tls.o uaccess.o umid.o util.o
|
||||
|
||||
CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH)
|
||||
|
||||
HAVE_AIO_ABI := $(shell [ -r /usr/include/linux/aio_abi.h ] && \
|
||||
echo -DHAVE_AIO_ABI )
|
||||
CFLAGS_aio.o += $(HAVE_AIO_ABI)
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
403
arch/um/os-Linux/aio.c
Normal file
403
arch/um/os-Linux/aio.c
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include "os.h"
|
||||
#include "aio.h"
|
||||
#include "init.h"
|
||||
#include "user.h"
|
||||
#include "mode.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 iocb, *iocbp = &iocb;
|
||||
char c;
|
||||
int err;
|
||||
|
||||
iocb = ((struct iocb) { .aio_data = (unsigned long) aio,
|
||||
.aio_reqprio = 0,
|
||||
.aio_fildes = fd,
|
||||
.aio_buf = (unsigned long) buf,
|
||||
.aio_nbytes = len,
|
||||
.aio_offset = offset,
|
||||
.aio_reserved1 = 0,
|
||||
.aio_reserved2 = 0,
|
||||
.aio_reserved3 = 0 });
|
||||
|
||||
switch(type){
|
||||
case AIO_READ:
|
||||
iocb.aio_lio_opcode = IOCB_CMD_PREAD;
|
||||
err = io_submit(ctx, 1, &iocbp);
|
||||
break;
|
||||
case AIO_WRITE:
|
||||
iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
|
||||
err = io_submit(ctx, 1, &iocbp);
|
||||
break;
|
||||
case AIO_MMAP:
|
||||
iocb.aio_lio_opcode = IOCB_CMD_PREAD;
|
||||
iocb.aio_buf = (unsigned long) &c;
|
||||
iocb.aio_nbytes = sizeof(c);
|
||||
err = io_submit(ctx, 1, &iocbp);
|
||||
break;
|
||||
default:
|
||||
printk("Bogus op in do_aio - %d\n", type);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if(err > 0)
|
||||
err = 0;
|
||||
else
|
||||
err = -errno;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
|
||||
while(1){
|
||||
n = io_getevents(ctx, 1, 1, &event, NULL);
|
||||
if(n < 0){
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
printk("aio_thread - io_getevents failed, "
|
||||
"errno = %d\n", errno);
|
||||
}
|
||||
else {
|
||||
reply = ((struct aio_thread_reply)
|
||||
{ .data = (void *) (long) event.data,
|
||||
.err = event.res });
|
||||
reply_fd = ((struct aio_context *) reply.data)->reply_fd;
|
||||
err = os_write_file(reply_fd, &reply, sizeof(reply));
|
||||
if(err != sizeof(reply))
|
||||
printk("aio_thread - write failed, fd = %d, "
|
||||
"err = %d\n", reply_fd, -err);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int do_not_aio(struct aio_thread_req *req)
|
||||
{
|
||||
char c;
|
||||
int err;
|
||||
|
||||
switch(req->type){
|
||||
case AIO_READ:
|
||||
err = os_seek_file(req->io_fd, req->offset);
|
||||
if(err)
|
||||
goto out;
|
||||
|
||||
err = os_read_file(req->io_fd, req->buf, req->len);
|
||||
break;
|
||||
case AIO_WRITE:
|
||||
err = os_seek_file(req->io_fd, req->offset);
|
||||
if(err)
|
||||
goto out;
|
||||
|
||||
err = os_write_file(req->io_fd, req->buf, req->len);
|
||||
break;
|
||||
case AIO_MMAP:
|
||||
err = os_seek_file(req->io_fd, req->offset);
|
||||
if(err)
|
||||
goto out;
|
||||
|
||||
err = os_read_file(req->io_fd, &c, sizeof(c));
|
||||
break;
|
||||
default:
|
||||
printk("do_not_aio - bad request type : %d\n", req->type);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 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 int not_aio_thread(void *arg)
|
||||
{
|
||||
struct aio_thread_req req;
|
||||
struct aio_thread_reply reply;
|
||||
int err;
|
||||
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
while(1){
|
||||
err = os_read_file(aio_req_fd_r, &req, sizeof(req));
|
||||
if(err != sizeof(req)){
|
||||
if(err < 0)
|
||||
printk("not_aio_thread - read failed, "
|
||||
"fd = %d, err = %d\n", aio_req_fd_r,
|
||||
-err);
|
||||
else {
|
||||
printk("not_aio_thread - short read, fd = %d, "
|
||||
"length = %d\n", aio_req_fd_r, err);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
err = do_not_aio(&req);
|
||||
reply = ((struct aio_thread_reply) { .data = req.aio,
|
||||
.err = err });
|
||||
err = os_write_file(req.aio->reply_fd, &reply, sizeof(reply));
|
||||
if(err != sizeof(reply))
|
||||
printk("not_aio_thread - write failed, fd = %d, "
|
||||
"err = %d\n", req.aio->reply_fd, -err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_aio_24(void)
|
||||
{
|
||||
unsigned long stack;
|
||||
int fds[2], err;
|
||||
|
||||
err = os_pipe(fds, 1, 1);
|
||||
if(err)
|
||||
goto out;
|
||||
|
||||
aio_req_fd_w = fds[0];
|
||||
aio_req_fd_r = fds[1];
|
||||
err = run_helper_thread(not_aio_thread, NULL,
|
||||
CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
|
||||
if(err < 0)
|
||||
goto out_close_pipe;
|
||||
|
||||
aio_pid = err;
|
||||
goto out;
|
||||
|
||||
out_close_pipe:
|
||||
os_close_file(fds[0]);
|
||||
os_close_file(fds[1]);
|
||||
aio_req_fd_w = -1;
|
||||
aio_req_fd_r = -1;
|
||||
out:
|
||||
#ifndef HAVE_AIO_ABI
|
||||
printk("/usr/include/linux/aio_abi.h not present during build\n");
|
||||
#endif
|
||||
printk("2.6 host AIO support not used - falling back to I/O "
|
||||
"thread\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_AIO_ABI
|
||||
#define DEFAULT_24_AIO 0
|
||||
static int init_aio_26(void)
|
||||
{
|
||||
unsigned long stack;
|
||||
int err;
|
||||
|
||||
if(io_setup(256, &ctx)){
|
||||
err = -errno;
|
||||
printk("aio_thread failed to initialize context, err = %d\n",
|
||||
errno);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = run_helper_thread(aio_thread, NULL,
|
||||
CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0);
|
||||
if(err < 0)
|
||||
return err;
|
||||
|
||||
aio_pid = err;
|
||||
|
||||
printk("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 = os_write_file(aio->reply_fd, &reply, sizeof(reply));
|
||||
if(err != sizeof(reply))
|
||||
printk("submit_aio_26 - write failed, "
|
||||
"fd = %d, err = %d\n", aio->reply_fd, -err);
|
||||
else err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
CHOOSE_MODE(({ if(!aio_24){
|
||||
printk("Disabling 2.6 AIO in tt mode\n");
|
||||
aio_24 = 1;
|
||||
} }), (void) 0);
|
||||
|
||||
if(!aio_24){
|
||||
err = init_aio_26();
|
||||
if(err && (errno == ENOSYS)){
|
||||
printk("2.6 AIO not supported on the host - "
|
||||
"reverting to 2.4 AIO\n");
|
||||
aio_24 = 1;
|
||||
}
|
||||
else return err;
|
||||
}
|
||||
|
||||
if(aio_24)
|
||||
return init_aio_24();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The reason for the __initcall/__uml_exitcall asymmetry is that init_aio
|
||||
* needs to be called when the kernel is running because it calls run_helper,
|
||||
* which needs get_free_page. exit_aio is a __uml_exitcall because the generic
|
||||
* kernel does not run __exitcalls on shutdown, and can't because many of them
|
||||
* break when called outside of module unloading.
|
||||
*/
|
||||
__initcall(init_aio);
|
||||
|
||||
static void exit_aio(void)
|
||||
{
|
||||
if(aio_pid != -1)
|
||||
os_kill_process(aio_pid, 1);
|
||||
}
|
||||
|
||||
__uml_exitcall(exit_aio);
|
||||
|
||||
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 = os_write_file(aio_req_fd_w, &req, sizeof(req));
|
||||
if(err == sizeof(req))
|
||||
err = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
13
arch/um/os-Linux/drivers/Makefile
Normal file
13
arch/um/os-Linux/drivers/Makefile
Normal file
@@ -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
|
||||
27
arch/um/os-Linux/drivers/etap.h
Normal file
27
arch/um/os-Linux/drivers/etap.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
108
arch/um/os-Linux/drivers/ethertap_kern.c
Normal file
108
arch/um/os-Linux/drivers/ethertap_kern.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 "linux/init.h"
|
||||
#include "linux/netdevice.h"
|
||||
#include "linux/etherdevice.h"
|
||||
#include "net_kern.h"
|
||||
#include "net_user.h"
|
||||
#include "etap.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 = dev->priv;
|
||||
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("ethertap backend - %s", epri->dev_name);
|
||||
if (epri->gate_addr != NULL)
|
||||
printk(", IP = %s", epri->gate_addr);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static int etap_read(int fd, struct sk_buff **skb, struct uml_net_private *lp)
|
||||
{
|
||||
int len;
|
||||
|
||||
*skb = ether_adjust_skb(*skb, ETH_HEADER_ETHERTAP);
|
||||
if(*skb == NULL) return(-ENOMEM);
|
||||
len = net_recvfrom(fd, (*skb)->mac.raw,
|
||||
(*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)
|
||||
{
|
||||
if(skb_headroom(*skb) < 2){
|
||||
struct sk_buff *skb2;
|
||||
|
||||
skb2 = skb_realloc_headroom(*skb, 2);
|
||||
dev_kfree_skb(*skb);
|
||||
if (skb2 == NULL) return(-ENOMEM);
|
||||
*skb = skb2;
|
||||
}
|
||||
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("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 = ðertap_user_info,
|
||||
.kern = ðertap_kern_info,
|
||||
.private_size = sizeof(struct ethertap_data),
|
||||
};
|
||||
|
||||
static int register_ethertap(void)
|
||||
{
|
||||
register_transport(ðertap_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_ethertap);
|
||||
240
arch/um/os-Linux/drivers/ethertap_user.c
Normal file
240
arch/um/os-Linux/drivers/ethertap_user.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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 <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <net/if.h>
|
||||
#include "user.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "net_user.h"
|
||||
#include "etap.h"
|
||||
#include "os.h"
|
||||
#include "um_malloc.h"
|
||||
|
||||
#define MAX_PACKET ETH_MAX_PACKET
|
||||
|
||||
void etap_user_init(void *data, void *dev)
|
||||
{
|
||||
struct ethertap_data *pri = data;
|
||||
|
||||
pri->dev = dev;
|
||||
}
|
||||
|
||||
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));
|
||||
n = os_write_file(fd, &change, sizeof(change));
|
||||
if(n != sizeof(change))
|
||||
printk("etap_change - request failed, err = %d\n", -n);
|
||||
output = um_kmalloc(page_size());
|
||||
if(output == NULL)
|
||||
printk("etap_change : Failed to allocate output buffer\n");
|
||||
read_output(fd, output, 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);
|
||||
os_close_file(data->data_me);
|
||||
os_close_file(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, status, 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){
|
||||
strcpy(gate_buf, gate);
|
||||
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, NULL);
|
||||
|
||||
if(pid < 0) err = pid;
|
||||
os_close_file(data_remote);
|
||||
os_close_file(control_remote);
|
||||
n = os_read_file(control_me, &c, sizeof(c));
|
||||
if(n != sizeof(c)){
|
||||
printk("etap_tramp : read of status failed, err = %d\n", -n);
|
||||
return(-EINVAL);
|
||||
}
|
||||
if(c != 1){
|
||||
printk("etap_tramp : uml_net failed\n");
|
||||
err = -EINVAL;
|
||||
CATCH_EINTR(n = waitpid(pid, &status, 0));
|
||||
if(n < 0)
|
||||
err = -errno;
|
||||
else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1))
|
||||
printk("uml_net didn't exit with status 1\n");
|
||||
}
|
||||
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 = os_pipe(data_fds, 0, 0);
|
||||
if(err < 0){
|
||||
printk("data os_pipe failed - err = %d\n", -err);
|
||||
return(err);
|
||||
}
|
||||
|
||||
err = os_pipe(control_fds, 1, 0);
|
||||
if(err < 0){
|
||||
printk("control os_pipe failed - err = %d\n", -err);
|
||||
return(err);
|
||||
}
|
||||
|
||||
err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0],
|
||||
control_fds[1], data_fds[0], data_fds[1]);
|
||||
output_len = page_size();
|
||||
output = um_kmalloc(output_len);
|
||||
read_output(control_fds[0], output, output_len);
|
||||
|
||||
if(output == NULL)
|
||||
printk("etap_open : failed to allocate output buffer\n");
|
||||
else {
|
||||
printk("%s", output);
|
||||
kfree(output);
|
||||
}
|
||||
|
||||
if(err < 0){
|
||||
printk("etap_tramp failed - err = %d\n", -err);
|
||||
return(err);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
static void etap_close(int fd, void *data)
|
||||
{
|
||||
struct ethertap_data *pri = data;
|
||||
|
||||
iter_addresses(pri->dev, etap_close_addr, &pri->control_fd);
|
||||
os_close_file(fd);
|
||||
os_shutdown_socket(pri->data_fd, 1, 1);
|
||||
os_close_file(pri->data_fd);
|
||||
pri->data_fd = -1;
|
||||
os_close_file(pri->control_fd);
|
||||
pri->control_fd = -1;
|
||||
}
|
||||
|
||||
static int etap_set_mtu(int mtu, void *data)
|
||||
{
|
||||
return(mtu);
|
||||
}
|
||||
|
||||
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,
|
||||
.set_mtu = etap_set_mtu,
|
||||
.add_address = etap_add_addr,
|
||||
.delete_address = etap_del_addr,
|
||||
.max_packet = MAX_PACKET - ETH_HEADER_ETHERTAP
|
||||
};
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
32
arch/um/os-Linux/drivers/tuntap.h
Normal file
32
arch/um/os-Linux/drivers/tuntap.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.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
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
93
arch/um/os-Linux/drivers/tuntap_kern.c
Normal file
93
arch/um/os-Linux/drivers/tuntap_kern.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include "linux/stddef.h"
|
||||
#include "linux/netdevice.h"
|
||||
#include "linux/etherdevice.h"
|
||||
#include "linux/skbuff.h"
|
||||
#include "linux/init.h"
|
||||
#include "asm/errno.h"
|
||||
#include "net_kern.h"
|
||||
#include "net_user.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 = dev->priv;
|
||||
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("TUN/TAP backend - ");
|
||||
if (tpri->gate_addr != NULL)
|
||||
printk("IP = %s", tpri->gate_addr);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static int tuntap_read(int fd, struct sk_buff **skb,
|
||||
struct uml_net_private *lp)
|
||||
{
|
||||
*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
|
||||
if(*skb == NULL) return(-ENOMEM);
|
||||
return(net_read(fd, (*skb)->mac.raw,
|
||||
(*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);
|
||||
228
arch/um/os-Linux/drivers/tuntap_user.c
Normal file
228
arch/um/os-Linux/drivers/tuntap_user.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include "net_user.h"
|
||||
#include "tuntap.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "user.h"
|
||||
#include "os.h"
|
||||
|
||||
#define MAX_PACKET ETH_MAX_PACKET
|
||||
|
||||
void tuntap_user_init(void *data, void *dev)
|
||||
{
|
||||
struct tuntap_data *pri = data;
|
||||
|
||||
pri->dev = dev;
|
||||
}
|
||||
|
||||
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;
|
||||
int close_me;
|
||||
};
|
||||
|
||||
static void tuntap_pre_exec(void *arg)
|
||||
{
|
||||
struct tuntap_pre_exec_data *data = arg;
|
||||
|
||||
dup2(data->stdout, 1);
|
||||
os_close_file(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 = remote;
|
||||
data.close_me = me;
|
||||
|
||||
pid = run_helper(tuntap_pre_exec, &data, argv, NULL);
|
||||
|
||||
if(pid < 0) return(-pid);
|
||||
|
||||
os_close_file(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("tuntap_open_tramp : recvmsg failed - errno = %d\n",
|
||||
errno);
|
||||
return err;
|
||||
}
|
||||
CATCH_EINTR(waitpid(pid, NULL, 0));
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if(cmsg == NULL){
|
||||
printk("tuntap_open_tramp : didn't receive a message\n");
|
||||
return(-EINVAL);
|
||||
}
|
||||
if((cmsg->cmsg_level != SOL_SOCKET) ||
|
||||
(cmsg->cmsg_type != SCM_RIGHTS)){
|
||||
printk("tuntap_open_tramp : didn't receive a descriptor\n");
|
||||
return(-EINVAL);
|
||||
}
|
||||
*fd_out = ((int *) CMSG_DATA(cmsg))[0];
|
||||
os_set_exec_close(*fd_out, 1);
|
||||
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("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, (void *) &ifr) < 0){
|
||||
err = -errno;
|
||||
printk("TUNSETIFF failed, errno = %d\n", errno);
|
||||
os_close_file(pri->fd);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
else {
|
||||
err = os_pipe(fds, 0, 0);
|
||||
if(err < 0){
|
||||
printk("tuntap_open : os_pipe failed - err = %d\n",
|
||||
-err);
|
||||
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("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);
|
||||
|
||||
os_close_file(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);
|
||||
os_close_file(fd);
|
||||
pri->fd = -1;
|
||||
}
|
||||
|
||||
static int tuntap_set_mtu(int mtu, void *data)
|
||||
{
|
||||
return(mtu);
|
||||
}
|
||||
|
||||
const struct net_user_info tuntap_user_info = {
|
||||
.init = tuntap_user_init,
|
||||
.open = tuntap_open,
|
||||
.close = tuntap_close,
|
||||
.remove = NULL,
|
||||
.set_mtu = tuntap_set_mtu,
|
||||
.add_address = tuntap_add_addr,
|
||||
.delete_address = tuntap_del_addr,
|
||||
.max_packet = MAX_PACKET
|
||||
};
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
79
arch/um/os-Linux/elf_aux.c
Normal file
79
arch/um/os-Linux/elf_aux.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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"
|
||||
#include <kern_constants.h>
|
||||
|
||||
/* Use the one from the kernel - the host may miss it, if having old headers. */
|
||||
#if UM_ELF_CLASS == UM_ELFCLASS32
|
||||
typedef Elf32_auxv_t elf_auxv_t;
|
||||
#else
|
||||
typedef Elf64_auxv_t elf_auxv_t;
|
||||
#endif
|
||||
|
||||
/* These are initialized very early in boot and never changed */
|
||||
char * elf_aux_platform;
|
||||
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;
|
||||
}
|
||||
}
|
||||
149
arch/um/os-Linux/execvp.c
Normal file
149
arch/um/os-Linux/execvp.c
Normal file
@@ -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) {
|
||||
fprintf(stderr, "Not enough arguments\n");
|
||||
return 1;
|
||||
}
|
||||
argv++;
|
||||
if (ret = execvp_noalloc(buf, argv[0], argv)) {
|
||||
errno = -ret;
|
||||
perror("execvp_noalloc");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
643
arch/um/os-Linux/file.c
Normal file
643
arch/um/os-Linux/file.c
Normal file
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/uio.h>
|
||||
#include "os.h"
|
||||
#include "user.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
|
||||
static void copy_stat(struct uml_stat *dst, 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;
|
||||
|
||||
do {
|
||||
err = stat64(file_name, &sbuf);
|
||||
} while((err < 0) && (errno == EINTR)) ;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void os_print_error(int error, const char* str)
|
||||
{
|
||||
errno = error < 0 ? -error : error;
|
||||
|
||||
perror(str);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
int os_window_size(int fd, int *rows, int *cols)
|
||||
{
|
||||
struct winsize size;
|
||||
|
||||
if(ioctl(fd, TIOCGWINSZ, &size) < 0)
|
||||
return -errno;
|
||||
|
||||
*rows = size.ws_row;
|
||||
*cols = size.ws_col;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int os_new_tty_pgrp(int fd, int pid)
|
||||
{
|
||||
if(ioctl(fd, TIOCSCTTY, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
if(tcsetpgrp(fd, pid) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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_set_owner(int fd, int pid)
|
||||
{
|
||||
if(fcntl(fd, F_SETOWN, pid) < 0){
|
||||
int save_errno = errno;
|
||||
|
||||
if(fcntl(fd, F_GETOWN, 0) != pid)
|
||||
return -save_errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int os_mode_fd(int fd, int mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
do {
|
||||
err = fchmod(fd, mode);
|
||||
} while((err < 0) && (errno==EINTR)) ;
|
||||
|
||||
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(char *file, struct openflags *mode_out)
|
||||
{
|
||||
int err;
|
||||
|
||||
*mode_out = OPENFLAGS();
|
||||
|
||||
err = os_access(file, OS_ACC_W_OK);
|
||||
if((err < 0) && (err != -EACCES))
|
||||
return(err);
|
||||
|
||||
*mode_out = of_write(*mode_out);
|
||||
|
||||
err = os_access(file, OS_ACC_R_OK);
|
||||
if((err < 0) && (err != -EACCES))
|
||||
return(err);
|
||||
|
||||
*mode_out = of_read(*mode_out);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
int os_open_file(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;
|
||||
|
||||
fd = open64(file, f, mode);
|
||||
if(fd < 0)
|
||||
return(-errno);
|
||||
|
||||
if(flags.cl && fcntl(fd, F_SETFD, 1)){
|
||||
err = -errno;
|
||||
os_close_file(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return(fd);
|
||||
}
|
||||
|
||||
int os_connect_socket(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_seek_file(int fd, __u64 offset)
|
||||
{
|
||||
__u64 actual;
|
||||
|
||||
actual = lseek64(fd, offset, SEEK_SET);
|
||||
if(actual != offset)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fault_buffer(void *start, int len,
|
||||
int (*copy_proc)(void *addr, void *buf, int len))
|
||||
{
|
||||
int page = getpagesize(), i;
|
||||
char c;
|
||||
|
||||
for(i = 0; i < len; i += page){
|
||||
if((*copy_proc)(start + i, &c, sizeof(c)))
|
||||
return -EFAULT;
|
||||
}
|
||||
if((len % page) != 0){
|
||||
if((*copy_proc)(start + len - 1, &c, sizeof(c)))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int file_io(int fd, void *buf, int len,
|
||||
int (*io_proc)(int fd, void *buf, int len),
|
||||
int (*copy_user_proc)(void *addr, void *buf, int len))
|
||||
{
|
||||
int n, err;
|
||||
|
||||
do {
|
||||
n = (*io_proc)(fd, buf, len);
|
||||
if((n < 0) && (errno == EFAULT)){
|
||||
err = fault_buffer(buf, len, copy_user_proc);
|
||||
if(err)
|
||||
return err;
|
||||
n = (*io_proc)(fd, buf, len);
|
||||
}
|
||||
} while((n < 0) && (errno == EINTR));
|
||||
|
||||
if(n < 0)
|
||||
return -errno;
|
||||
return n;
|
||||
}
|
||||
|
||||
int os_read_file(int fd, void *buf, int len)
|
||||
{
|
||||
return file_io(fd, buf, len, (int (*)(int, void *, int)) read,
|
||||
copy_from_user_proc);
|
||||
}
|
||||
|
||||
int os_write_file(int fd, const void *buf, int len)
|
||||
{
|
||||
return file_io(fd, (void *) buf, len,
|
||||
(int (*)(int, void *, int)) write, copy_to_user_proc);
|
||||
}
|
||||
|
||||
int os_file_size(char *file, unsigned long long *size_out)
|
||||
{
|
||||
struct uml_stat buf;
|
||||
int err;
|
||||
|
||||
err = os_stat_file(file, &buf);
|
||||
if(err < 0){
|
||||
printk("Couldn't stat \"%s\" : err = %d\n", file, -err);
|
||||
return(err);
|
||||
}
|
||||
|
||||
if(S_ISBLK(buf.ust_mode)){
|
||||
int fd, blocks;
|
||||
|
||||
fd = os_open_file(file, of_read(OPENFLAGS()), 0);
|
||||
if(fd < 0){
|
||||
printk("Couldn't open \"%s\", errno = %d\n", file, -fd);
|
||||
return(fd);
|
||||
}
|
||||
if(ioctl(fd, BLKGETSIZE, &blocks) < 0){
|
||||
err = -errno;
|
||||
printk("Couldn't get the block size of \"%s\", "
|
||||
"errno = %d\n", file, errno);
|
||||
os_close_file(fd);
|
||||
return(err);
|
||||
}
|
||||
*size_out = ((long long) blocks) * 512;
|
||||
os_close_file(fd);
|
||||
return(0);
|
||||
}
|
||||
*size_out = buf.ust_size;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int os_file_modtime(char *file, unsigned long *modtime)
|
||||
{
|
||||
struct uml_stat buf;
|
||||
int err;
|
||||
|
||||
err = os_stat_file(file, &buf);
|
||||
if(err < 0){
|
||||
printk("Couldn't stat \"%s\" : err = %d\n", file, -err);
|
||||
return err;
|
||||
}
|
||||
|
||||
*modtime = buf.ust_mtime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int os_get_exec_close(int fd, int* close_on_exec)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = fcntl(fd, F_GETFD);
|
||||
} while((ret < 0) && (errno == EINTR)) ;
|
||||
|
||||
if(ret < 0)
|
||||
return(-errno);
|
||||
|
||||
*close_on_exec = (ret&FD_CLOEXEC) ? 1 : 0;
|
||||
return(ret);
|
||||
}
|
||||
|
||||
int os_set_exec_close(int fd, int close_on_exec)
|
||||
{
|
||||
int flag, err;
|
||||
|
||||
if(close_on_exec) flag = FD_CLOEXEC;
|
||||
else flag = 0;
|
||||
|
||||
do {
|
||||
err = fcntl(fd, F_SETFD, flag);
|
||||
} while((err < 0) && (errno == EINTR)) ;
|
||||
|
||||
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], 1);
|
||||
if(err < 0)
|
||||
goto error;
|
||||
|
||||
err = os_set_exec_close(fds[1], 1);
|
||||
if(err < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
printk("os_pipe : Setting FD_CLOEXEC failed, err = %d\n", -err);
|
||||
os_close_file(fds[1]);
|
||||
os_close_file(fds[0]);
|
||||
return(err);
|
||||
}
|
||||
|
||||
int os_set_fd_async(int fd, int owner)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* XXX This should do F_GETFL first */
|
||||
if(fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK) < 0){
|
||||
err = -errno;
|
||||
printk("os_set_fd_async : failed to set O_ASYNC and "
|
||||
"O_NONBLOCK on fd # %d, errno = %d\n", fd, errno);
|
||||
return err;
|
||||
}
|
||||
#ifdef notdef
|
||||
if(fcntl(fd, F_SETFD, 1) < 0){
|
||||
printk("os_set_fd_async : Setting FD_CLOEXEC failed, "
|
||||
"errno = %d\n", errno);
|
||||
}
|
||||
#endif
|
||||
|
||||
if((fcntl(fd, F_SETSIG, SIGIO) < 0) ||
|
||||
(fcntl(fd, F_SETOWN, owner) < 0)){
|
||||
err = -errno;
|
||||
printk("os_set_fd_async : Failed to fcntl F_SETOWN "
|
||||
"(or F_SETSIG) fd %d to pid %d, errno = %d\n", fd,
|
||||
owner, errno);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int os_clear_fd_async(int fd)
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
|
||||
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(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 {
|
||||
printk("os_shutdown_socket : neither r or w was set\n");
|
||||
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 != sizeof(iov.iov_len))
|
||||
*helper_pid_out = -1;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if(cmsg == NULL){
|
||||
printk("rcv_fd didn't receive anything, error = %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
if((cmsg->cmsg_level != SOL_SOCKET) ||
|
||||
(cmsg->cmsg_type != SCM_RIGHTS)){
|
||||
printk("rcv_fd didn't receive a descriptor\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
new = ((int *) CMSG_DATA(cmsg))[0];
|
||||
return new;
|
||||
}
|
||||
|
||||
int os_create_unix_socket(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, 1);
|
||||
if(err < 0)
|
||||
printk("create_unix_socket : close_on_exec failed, "
|
||||
"err = %d", -err);
|
||||
}
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
/* XXX Be more careful about overflow */
|
||||
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("F_SETLK failed, file already locked by pid %d\n", lock.l_pid);
|
||||
err = save;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
173
arch/um/os-Linux/helper.c
Normal file
173
arch/um/os-Linux/helper.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <limits.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include "user.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_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;
|
||||
};
|
||||
|
||||
/* Debugging aid, changed only from gdb */
|
||||
int helper_pause = 0;
|
||||
|
||||
static void helper_hup(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
static int helper_child(void *arg)
|
||||
{
|
||||
struct helper_data *data = arg;
|
||||
char **argv = data->argv;
|
||||
int errval;
|
||||
|
||||
if (helper_pause){
|
||||
signal(SIGHUP, helper_hup);
|
||||
pause();
|
||||
}
|
||||
if (data->pre_exec != NULL)
|
||||
(*data->pre_exec)(data->pre_data);
|
||||
errval = execvp_noalloc(data->buf, argv[0], argv);
|
||||
printk("helper_child - execvp of '%s' failed - errno = %d\n", argv[0], -errval);
|
||||
os_write_file(data->fd, &errval, sizeof(errval));
|
||||
kill(os_getpid(), SIGKILL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns either the pid of the child process we run or -E* on failure.
|
||||
* XXX The alloc_stack here breaks if this is called in the tracing thread, so
|
||||
* we need to receive a preallocated stack (a local buffer is ok). */
|
||||
int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
|
||||
unsigned long *stack_out)
|
||||
{
|
||||
struct helper_data data;
|
||||
unsigned long stack, sp;
|
||||
int pid, fds[2], ret, n;
|
||||
|
||||
if ((stack_out != NULL) && (*stack_out != 0))
|
||||
stack = *stack_out;
|
||||
else
|
||||
stack = alloc_stack(0, __cant_sleep());
|
||||
if (stack == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = os_pipe(fds, 1, 0);
|
||||
if (ret < 0) {
|
||||
printk("run_helper : pipe failed, ret = %d\n", -ret);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = os_set_exec_close(fds[1], 1);
|
||||
if (ret < 0) {
|
||||
printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n",
|
||||
-ret);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
sp = stack + 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() ? um_kmalloc_atomic(PATH_MAX) :
|
||||
um_kmalloc(PATH_MAX);
|
||||
pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data);
|
||||
if (pid < 0) {
|
||||
ret = -errno;
|
||||
printk("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 = os_read_file(fds[0], &ret, sizeof(ret));
|
||||
if (n == 0) {
|
||||
ret = pid;
|
||||
} else {
|
||||
if (n < 0) {
|
||||
printk("run_helper : read on pipe failed, ret = %d\n",
|
||||
-n);
|
||||
ret = n;
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
CATCH_EINTR(waitpid(pid, NULL, 0));
|
||||
}
|
||||
|
||||
out_free2:
|
||||
kfree(data.buf);
|
||||
out_close:
|
||||
if (fds[1] != -1)
|
||||
close(fds[1]);
|
||||
close(fds[0]);
|
||||
out_free:
|
||||
if ((stack_out == NULL) || (*stack_out == 0))
|
||||
free_stack(stack, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
|
||||
unsigned long *stack_out, int stack_order)
|
||||
{
|
||||
unsigned long stack, sp;
|
||||
int pid, status, err;
|
||||
|
||||
stack = alloc_stack(stack_order, __cant_sleep());
|
||||
if (stack == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
sp = stack + (page_size() << stack_order) - sizeof(void *);
|
||||
pid = clone(proc, (void *) sp, flags | SIGCHLD, arg);
|
||||
if (pid < 0) {
|
||||
err = -errno;
|
||||
printk("run_helper_thread : clone failed, errno = %d\n",
|
||||
errno);
|
||||
return err;
|
||||
}
|
||||
if (stack_out == NULL) {
|
||||
CATCH_EINTR(pid = waitpid(pid, &status, 0));
|
||||
if (pid < 0) {
|
||||
err = -errno;
|
||||
printk("run_helper_thread - wait failed, errno = %d\n",
|
||||
errno);
|
||||
pid = err;
|
||||
}
|
||||
if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
|
||||
printk("run_helper_thread - thread returned status "
|
||||
"0x%x\n", status);
|
||||
free_stack(stack, stack_order);
|
||||
} else
|
||||
*stack_out = stack;
|
||||
return pid;
|
||||
}
|
||||
|
||||
int helper_wait(int pid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG));
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
printk("helper_wait : waitpid failed, errno = %d\n", errno);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
22
arch/um/os-Linux/include/file.h
Normal file
22
arch/um/os-Linux/include/file.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __OS_FILE_H__
|
||||
#define __OS_FILE_H__
|
||||
|
||||
#define DEV_NULL "/dev/null"
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
156
arch/um/os-Linux/irq.c
Normal file
156
arch/um/os-Linux/irq.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "process.h"
|
||||
#include "sigio.h"
|
||||
#include "irq_user.h"
|
||||
#include "os.h"
|
||||
#include "um_malloc.h"
|
||||
|
||||
/*
|
||||
* Locked by irq_lock in arch/um/kernel/irq.c. Changed by os_create_pollfd
|
||||
* and os_free_irq_by_cb, which are called under irq_lock.
|
||||
*/
|
||||
static struct pollfd *pollfds = NULL;
|
||||
static int pollfds_num = 0;
|
||||
static int pollfds_size = 0;
|
||||
|
||||
int os_waiting_for_events(struct irq_fd *active_fds)
|
||||
{
|
||||
struct irq_fd *irq_fd;
|
||||
int i, n, err;
|
||||
|
||||
n = poll(pollfds, pollfds_num, 0);
|
||||
if (n < 0) {
|
||||
err = -errno;
|
||||
if (errno != EINTR)
|
||||
printk("sigio_handler: os_waiting_for_events:"
|
||||
" poll returned %d, errno = %d\n", n, errno);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
irq_fd = active_fds;
|
||||
|
||||
for (i = 0; i < pollfds_num; i++) {
|
||||
if (pollfds[i].revents != 0) {
|
||||
irq_fd->current_events = pollfds[i].revents;
|
||||
pollfds[i].fd = -1;
|
||||
}
|
||||
irq_fd = irq_fd->next;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int os_create_pollfd(int fd, int events, void *tmp_pfd, int size_tmpfds)
|
||||
{
|
||||
if (pollfds_num == pollfds_size) {
|
||||
if (size_tmpfds <= pollfds_size * sizeof(pollfds[0])) {
|
||||
/* return min size needed for new pollfds area */
|
||||
return (pollfds_size + 1) * sizeof(pollfds[0]);
|
||||
}
|
||||
|
||||
if (pollfds != NULL) {
|
||||
memcpy(tmp_pfd, pollfds,
|
||||
sizeof(pollfds[0]) * pollfds_size);
|
||||
/* remove old pollfds */
|
||||
kfree(pollfds);
|
||||
}
|
||||
pollfds = tmp_pfd;
|
||||
pollfds_size++;
|
||||
} else
|
||||
kfree(tmp_pfd); /* remove not used tmp_pfd */
|
||||
|
||||
pollfds[pollfds_num] = ((struct pollfd) { .fd = fd,
|
||||
.events = events,
|
||||
.revents = 0 });
|
||||
pollfds_num++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg,
|
||||
struct irq_fd *active_fds, struct irq_fd ***last_irq_ptr2)
|
||||
{
|
||||
struct irq_fd **prev;
|
||||
int i = 0;
|
||||
|
||||
prev = &active_fds;
|
||||
while (*prev != NULL) {
|
||||
if ((*test)(*prev, arg)) {
|
||||
struct irq_fd *old_fd = *prev;
|
||||
if ((pollfds[i].fd != -1) &&
|
||||
(pollfds[i].fd != (*prev)->fd)) {
|
||||
printk("os_free_irq_by_cb - mismatch between "
|
||||
"active_fds and pollfds, fd %d vs %d\n",
|
||||
(*prev)->fd, pollfds[i].fd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pollfds_num--;
|
||||
|
||||
/* This moves the *whole* array after pollfds[i]
|
||||
* (though it doesn't spot as such)!
|
||||
*/
|
||||
memmove(&pollfds[i], &pollfds[i + 1],
|
||||
(pollfds_num - i) * sizeof(pollfds[0]));
|
||||
if(*last_irq_ptr2 == &old_fd->next)
|
||||
*last_irq_ptr2 = prev;
|
||||
|
||||
*prev = (*prev)->next;
|
||||
if(old_fd->type == IRQ_WRITE)
|
||||
ignore_sigio_fd(old_fd->fd);
|
||||
kfree(old_fd);
|
||||
continue;
|
||||
}
|
||||
prev = &(*prev)->next;
|
||||
i++;
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
int os_get_pollfd(int i)
|
||||
{
|
||||
return pollfds[i].fd;
|
||||
}
|
||||
|
||||
void os_set_pollfd(int i, int fd)
|
||||
{
|
||||
pollfds[i].fd = fd;
|
||||
}
|
||||
|
||||
void os_set_ioignore(void)
|
||||
{
|
||||
signal(SIGIO, SIG_IGN);
|
||||
}
|
||||
|
||||
void init_irq_signals(int on_sigstack)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = on_sigstack ? SA_ONSTACK : 0;
|
||||
|
||||
set_handler(SIGVTALRM, (__sighandler_t) alarm_handler,
|
||||
flags | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1);
|
||||
set_handler(SIGALRM, (__sighandler_t) alarm_handler,
|
||||
flags | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1);
|
||||
set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART,
|
||||
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
}
|
||||
298
arch/um/os-Linux/main.c
Normal file
298
arch/um/os-Linux/main.c
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/user.h>
|
||||
#include <asm/page.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "irq_user.h"
|
||||
#include "user.h"
|
||||
#include "init.h"
|
||||
#include "mode.h"
|
||||
#include "choose-mode.h"
|
||||
#include "uml-config.h"
|
||||
#include "os.h"
|
||||
#include "um_malloc.h"
|
||||
|
||||
/* Set in set_stklim, which is called from main and __wrap_malloc.
|
||||
* __wrap_malloc only calls it if main hasn't started.
|
||||
*/
|
||||
unsigned long stacksizelim;
|
||||
|
||||
/* Set in main */
|
||||
char *linux_prog;
|
||||
|
||||
#define PGD_BOUND (4 * 1024 * 1024)
|
||||
#define STACKSIZE (8 * 1024 * 1024)
|
||||
#define THREAD_NAME_LEN (256)
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1);
|
||||
}
|
||||
|
||||
static __init void do_uml_initcalls(void)
|
||||
{
|
||||
initcall_t *call;
|
||||
|
||||
call = &__uml_initcall_start;
|
||||
while (call < &__uml_initcall_end){
|
||||
(*call)();
|
||||
call++;
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
printf("failed to install handler for signal %d - errno = %d\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define UML_LIB_PATH ":/usr/lib/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) {
|
||||
putenv("PATH=:/bin:/usr/bin/" UML_LIB_PATH);
|
||||
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("coudn't malloc to set a new PATH");
|
||||
return;
|
||||
}
|
||||
snprintf(new_path, path_len, "PATH=%s" UML_LIB_PATH, old_path);
|
||||
putenv(new_path);
|
||||
}
|
||||
|
||||
extern int uml_exitcode;
|
||||
|
||||
extern void scan_elf_aux( char **envp);
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
char **new_argv;
|
||||
int ret, i, err;
|
||||
|
||||
#ifdef UML_CONFIG_CMDLINE_ON_HOST
|
||||
/* Allocate memory for thread command lines */
|
||||
if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){
|
||||
|
||||
char padding[THREAD_NAME_LEN] = {
|
||||
[ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0'
|
||||
};
|
||||
|
||||
new_argv = malloc((argc + 2) * sizeof(char*));
|
||||
if(!new_argv) {
|
||||
perror("Allocating extended argv");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
new_argv[0] = argv[0];
|
||||
new_argv[1] = padding;
|
||||
|
||||
for(i = 2; i <= argc; i++)
|
||||
new_argv[i] = argv[i - 1];
|
||||
new_argv[argc + 1] = NULL;
|
||||
|
||||
execvp(new_argv[0], new_argv);
|
||||
perror("execing with extended args");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
linux_prog = argv[0];
|
||||
|
||||
set_stklim();
|
||||
|
||||
setup_env_path();
|
||||
|
||||
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);
|
||||
install_fatal_handler(SIGHUP);
|
||||
|
||||
scan_elf_aux( envp);
|
||||
|
||||
do_uml_initcalls();
|
||||
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 SIGVTALRM 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 SIG*ALRM to be ignored */
|
||||
disable_timer();
|
||||
|
||||
/* disable SIGIO for the fds and set SIGIO to be ignored */
|
||||
err = deactivate_all_fds();
|
||||
if(err)
|
||||
printf("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();
|
||||
|
||||
/* Reboot */
|
||||
if(ret){
|
||||
printf("\n");
|
||||
execvp(new_argv[0], new_argv);
|
||||
perror("Failed to exec kernel");
|
||||
ret = 1;
|
||||
}
|
||||
printf("\n");
|
||||
return(uml_exitcode);
|
||||
}
|
||||
|
||||
#define CAN_KMALLOC() \
|
||||
(kmalloc_ok && CHOOSE_MODE((os_getpid() != tracing_pid), 1))
|
||||
|
||||
extern void *__real_malloc(int);
|
||||
|
||||
void *__wrap_malloc(int size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if(!CAN_KMALLOC())
|
||||
return(__real_malloc(size));
|
||||
else if(size <= PAGE_SIZE) /* finding contiguos pages can be hard*/
|
||||
ret = um_kmalloc(size);
|
||||
else ret = um_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(CAN_KMALLOC())
|
||||
kfree(ptr);
|
||||
}
|
||||
else if((addr >= start_vm) && (addr < end_vm)){
|
||||
if(CAN_KMALLOC())
|
||||
vfree(ptr);
|
||||
}
|
||||
else __real_free(ptr);
|
||||
}
|
||||
280
arch/um/os-Linux/mem.c
Normal file
280
arch/um/os-Linux/mem.c
Normal file
@@ -0,0 +1,280 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/statfs.h>
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "user_util.h"
|
||||
#include "mem_user.h"
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
#include "tempfile.h"
|
||||
#include "kern_constants.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
/* Modified by which_tmpdir, which is called during early boot */
|
||||
static char *default_tmpdir = "/tmp";
|
||||
|
||||
/*
|
||||
* Modified when creating the physical memory file and when checking
|
||||
* the tmp filesystem for usability, both happening during early boot.
|
||||
*/
|
||||
static char *tempdir = NULL;
|
||||
|
||||
static void __init find_tempdir(void)
|
||||
{
|
||||
char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL };
|
||||
int i;
|
||||
char *dir = NULL;
|
||||
|
||||
if(tempdir != NULL) /* We've already been called */
|
||||
return;
|
||||
for(i = 0; dirs[i]; i++){
|
||||
dir = getenv(dirs[i]);
|
||||
if((dir != NULL) && (*dir != '\0'))
|
||||
break;
|
||||
}
|
||||
if((dir == NULL) || (*dir == '\0'))
|
||||
dir = default_tmpdir;
|
||||
|
||||
tempdir = malloc(strlen(dir) + 2);
|
||||
if(tempdir == NULL){
|
||||
fprintf(stderr, "Failed to malloc tempdir, "
|
||||
"errno = %d\n", errno);
|
||||
return;
|
||||
}
|
||||
strcpy(tempdir, dir);
|
||||
strcat(tempdir, "/");
|
||||
}
|
||||
|
||||
/* This will return 1, with the first character in buf being the
|
||||
* character following the next instance of c in the file. This will
|
||||
* read the file as needed. If there's an error, -errno is returned;
|
||||
* if the end of the file is reached, 0 is returned.
|
||||
*/
|
||||
static int next(int fd, char *buf, int size, char c)
|
||||
{
|
||||
int n, len;
|
||||
char *ptr;
|
||||
|
||||
while((ptr = strchr(buf, c)) == NULL){
|
||||
n = read(fd, buf, size - 1);
|
||||
if(n == 0)
|
||||
return 0;
|
||||
else if(n < 0)
|
||||
return -errno;
|
||||
|
||||
buf[n] = '\0';
|
||||
}
|
||||
|
||||
ptr++;
|
||||
len = strlen(ptr);
|
||||
memmove(buf, ptr, len + 1);
|
||||
|
||||
/* Refill the buffer so that if there's a partial string that we care
|
||||
* about, it will be completed, and we can recognize it.
|
||||
*/
|
||||
n = read(fd, &buf[len], size - len - 1);
|
||||
if(n < 0)
|
||||
return -errno;
|
||||
|
||||
buf[len + n] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* which_tmpdir is called only during early boot */
|
||||
static int checked_tmpdir = 0;
|
||||
|
||||
/* Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner
|
||||
* way to do this than to parse /proc/mounts. statfs will return the
|
||||
* same filesystem magic number and fs id for both /dev and /dev/shm
|
||||
* when they are both tmpfs, so you can't tell if they are different
|
||||
* filesystems. Also, there seems to be no other way of finding the
|
||||
* mount point of a filesystem from within it.
|
||||
*
|
||||
* If a /dev/shm tmpfs entry is found, then we switch to using it.
|
||||
* Otherwise, we stay with the default /tmp.
|
||||
*/
|
||||
static void which_tmpdir(void)
|
||||
{
|
||||
int fd, found;
|
||||
char buf[128] = { '\0' };
|
||||
|
||||
if(checked_tmpdir)
|
||||
return;
|
||||
|
||||
checked_tmpdir = 1;
|
||||
|
||||
printf("Checking for tmpfs mount on /dev/shm...");
|
||||
|
||||
fd = open("/proc/mounts", O_RDONLY);
|
||||
if(fd < 0){
|
||||
printf("failed to open /proc/mounts, errno = %d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
while(1){
|
||||
found = next(fd, buf, ARRAY_SIZE(buf), ' ');
|
||||
if(found != 1)
|
||||
break;
|
||||
|
||||
if(!strncmp(buf, "/dev/shm", strlen("/dev/shm")))
|
||||
goto found;
|
||||
|
||||
found = next(fd, buf, ARRAY_SIZE(buf), '\n');
|
||||
if(found != 1)
|
||||
break;
|
||||
}
|
||||
|
||||
err:
|
||||
if(found == 0)
|
||||
printf("nothing mounted on /dev/shm\n");
|
||||
else if(found < 0)
|
||||
printf("read returned errno %d\n", -found);
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
|
||||
found:
|
||||
found = next(fd, buf, ARRAY_SIZE(buf), ' ');
|
||||
if(found != 1)
|
||||
goto err;
|
||||
|
||||
if(strncmp(buf, "tmpfs", strlen("tmpfs"))){
|
||||
printf("not tmpfs\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("OK\n");
|
||||
default_tmpdir = "/dev/shm";
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* This proc still used in tt-mode
|
||||
* (file: kernel/tt/ptproxy/proxy.c, proc: start_debugger).
|
||||
* So it isn't 'static' yet.
|
||||
*/
|
||||
int make_tempfile(const char *template, char **out_tempname, int do_unlink)
|
||||
{
|
||||
char *tempname;
|
||||
int fd;
|
||||
|
||||
which_tmpdir();
|
||||
tempname = malloc(MAXPATHLEN);
|
||||
|
||||
find_tempdir();
|
||||
if (template[0] != '/')
|
||||
strcpy(tempname, tempdir);
|
||||
else
|
||||
tempname[0] = '\0';
|
||||
strcat(tempname, template);
|
||||
fd = mkstemp(tempname);
|
||||
if(fd < 0){
|
||||
fprintf(stderr, "open - cannot create %s: %s\n", tempname,
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
if(do_unlink && (unlink(tempname) < 0)){
|
||||
perror("unlink");
|
||||
goto out;
|
||||
}
|
||||
if(out_tempname){
|
||||
*out_tempname = tempname;
|
||||
} else {
|
||||
free(tempname);
|
||||
}
|
||||
return fd;
|
||||
out:
|
||||
free(tempname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define TEMPNAME_TEMPLATE "vm_file-XXXXXX"
|
||||
|
||||
/*
|
||||
* This proc is used in start_up.c
|
||||
* So it isn't 'static'.
|
||||
*/
|
||||
int create_tmp_file(unsigned long long len)
|
||||
{
|
||||
int fd, err;
|
||||
char zero;
|
||||
|
||||
fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1);
|
||||
if(fd < 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
err = fchmod(fd, 0777);
|
||||
if(err < 0){
|
||||
perror("os_mode_fd");
|
||||
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("os_seek_file");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
zero = 0;
|
||||
|
||||
err = os_write_file(fd, &zero, 1);
|
||||
if(err != 1){
|
||||
errno = -err;
|
||||
perror("os_write_file");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int create_mem_file(unsigned long long len)
|
||||
{
|
||||
int err, fd;
|
||||
|
||||
fd = create_tmp_file(len);
|
||||
|
||||
err = os_set_exec_close(fd, 1);
|
||||
if(err < 0){
|
||||
errno = -err;
|
||||
perror("exec_close");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
void 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);
|
||||
printf("Checking PROT_EXEC mmap in %s...",tempdir);
|
||||
fflush(stdout);
|
||||
if(addr == MAP_FAILED){
|
||||
err = errno;
|
||||
perror("failed");
|
||||
if(err == EPERM)
|
||||
printf("%s must be not mounted noexec\n",tempdir);
|
||||
exit(1);
|
||||
}
|
||||
printf("OK\n");
|
||||
munmap(addr, UM_KERN_PAGE_SIZE);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
294
arch/um/os-Linux/process.c
Normal file
294
arch/um/os-Linux/process.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include "ptrace_user.h"
|
||||
#include "os.h"
|
||||
#include "user.h"
|
||||
#include "user_util.h"
|
||||
#include "process.h"
|
||||
#include "irq_user.h"
|
||||
#include "kern_util.h"
|
||||
#include "longjmp.h"
|
||||
#include "skas_ptrace.h"
|
||||
#include "kern_constants.h"
|
||||
#include "uml-config.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;
|
||||
int fd, err;
|
||||
|
||||
sprintf(proc_stat, "/proc/%d/stat", pid);
|
||||
fd = os_open_file(proc_stat, of_read(OPENFLAGS()), 0);
|
||||
if(fd < 0){
|
||||
printk("os_process_pc - couldn't open '%s', err = %d\n",
|
||||
proc_stat, -fd);
|
||||
return(ARBITRARY_ADDR);
|
||||
}
|
||||
err = os_read_file(fd, buf, sizeof(buf));
|
||||
if(err < 0){
|
||||
printk("os_process_pc - couldn't read '%s', err = %d\n",
|
||||
proc_stat, -err);
|
||||
os_close_file(fd);
|
||||
return(ARBITRARY_ADDR);
|
||||
}
|
||||
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("os_process_pc - couldn't find pc in '%s'\n", buf);
|
||||
}
|
||||
return(pc);
|
||||
}
|
||||
|
||||
int os_process_parent(int pid)
|
||||
{
|
||||
char stat[STAT_PATH_LEN];
|
||||
char data[256];
|
||||
int parent, n, fd;
|
||||
|
||||
if(pid == -1) return(-1);
|
||||
|
||||
snprintf(stat, sizeof(stat), "/proc/%d/stat", pid);
|
||||
fd = os_open_file(stat, of_read(OPENFLAGS()), 0);
|
||||
if(fd < 0){
|
||||
printk("Couldn't open '%s', err = %d\n", stat, -fd);
|
||||
return(FAILURE_PID);
|
||||
}
|
||||
|
||||
n = os_read_file(fd, data, sizeof(data));
|
||||
os_close_file(fd);
|
||||
|
||||
if(n < 0){
|
||||
printk("Couldn't read '%s', err = %d\n", stat, -n);
|
||||
return(FAILURE_PID);
|
||||
}
|
||||
|
||||
parent = FAILURE_PID;
|
||||
n = sscanf(data, "%*d " COMM_SCANF " %*c %d", &parent);
|
||||
if(n != 1)
|
||||
printk("Failed to scan '%s'\n", data);
|
||||
|
||||
return(parent);
|
||||
}
|
||||
|
||||
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, 0));
|
||||
|
||||
}
|
||||
|
||||
/* This is here uniquely to have access to the userspace errno, i.e. the one
|
||||
* used by ptrace in case of error.
|
||||
*/
|
||||
|
||||
long os_ptrace_ldt(long pid, long addr, long data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_LDT, pid, addr, data);
|
||||
|
||||
if (ret < 0)
|
||||
return -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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, 0));
|
||||
}
|
||||
|
||||
#ifdef UML_CONFIG_MODE_TT
|
||||
void os_usr1_process(int pid)
|
||||
{
|
||||
kill(pid, SIGUSR1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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 can_drop_memory(void)
|
||||
{
|
||||
void *addr;
|
||||
int fd, ok = 0;
|
||||
|
||||
printk("Checking host MADV_REMOVE support...");
|
||||
fd = create_mem_file(UM_KERN_PAGE_SIZE);
|
||||
if(fd < 0){
|
||||
printk("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("Mapping test memory file failed, err = %d\n", -errno);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if(madvise(addr, UM_KERN_PAGE_SIZE, MADV_REMOVE) != 0){
|
||||
printk("MADV_REMOVE failed, err = %d\n", -errno);
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
printk("OK\n");
|
||||
ok = 1;
|
||||
|
||||
out_unmap:
|
||||
munmap(addr, UM_KERN_PAGE_SIZE);
|
||||
out_close:
|
||||
close(fd);
|
||||
out:
|
||||
return ok;
|
||||
}
|
||||
|
||||
void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int))
|
||||
{
|
||||
int flags = 0, pages;
|
||||
|
||||
if(sig_stack != NULL){
|
||||
pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER);
|
||||
set_sigstack(sig_stack, pages * page_size());
|
||||
flags = SA_ONSTACK;
|
||||
}
|
||||
if(usr1_handler){
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_handler = usr1_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = flags;
|
||||
sa.sa_restorer = NULL;
|
||||
if(sigaction(SIGUSR1, &sa, NULL) < 0)
|
||||
panic("init_new_thread_stack - sigaction failed - "
|
||||
"errno = %d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
void init_new_thread_signals(void)
|
||||
{
|
||||
set_handler(SIGSEGV, (__sighandler_t) sig_handler, SA_ONSTACK,
|
||||
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
|
||||
set_handler(SIGTRAP, (__sighandler_t) sig_handler, SA_ONSTACK,
|
||||
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
|
||||
set_handler(SIGFPE, (__sighandler_t) sig_handler, SA_ONSTACK,
|
||||
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
|
||||
set_handler(SIGILL, (__sighandler_t) sig_handler, SA_ONSTACK,
|
||||
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
|
||||
set_handler(SIGBUS, (__sighandler_t) sig_handler, SA_ONSTACK,
|
||||
SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1);
|
||||
set_handler(SIGUSR2, (__sighandler_t) sig_handler,
|
||||
SA_ONSTACK, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM,
|
||||
-1);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
|
||||
init_irq_signals(1);
|
||||
}
|
||||
|
||||
int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr)
|
||||
{
|
||||
jmp_buf buf;
|
||||
int n;
|
||||
|
||||
*jmp_ptr = &buf;
|
||||
n = UML_SETJMP(&buf);
|
||||
if(n != 0)
|
||||
return n;
|
||||
(*fn)(arg);
|
||||
return 0;
|
||||
}
|
||||
359
arch/um/os-Linux/sigio.c
Normal file
359
arch/um/os-Linux/sigio.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include <pty.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include "init.h"
|
||||
#include "user.h"
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "sigio.h"
|
||||
#include "os.h"
|
||||
#include "um_malloc.h"
|
||||
|
||||
/* Protected by sigio_lock(), also used by sigio_cleanup, which is an
|
||||
* exitcall.
|
||||
*/
|
||||
static int write_sigio_pid = -1;
|
||||
|
||||
/* 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;
|
||||
|
||||
signal(SIGWINCH, SIG_IGN);
|
||||
fds = ¤t_poll;
|
||||
while(1){
|
||||
n = poll(fds->poll, fds->used, -1);
|
||||
if(n < 0){
|
||||
if(errno == EINTR) continue;
|
||||
printk("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]){
|
||||
n = os_read_file(sigio_private[1], &c, sizeof(c));
|
||||
if(n != sizeof(c))
|
||||
printk("write_sigio_thread : "
|
||||
"read on socket failed, "
|
||||
"err = %d\n", -n);
|
||||
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));
|
||||
}
|
||||
|
||||
n = os_write_file(respond_fd, &c, sizeof(c));
|
||||
if(n != sizeof(c))
|
||||
printk("write_sigio_thread : write on socket "
|
||||
"failed, err = %d\n", -n);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int need_poll(struct pollfds *polls, int n)
|
||||
{
|
||||
struct pollfd *new;
|
||||
|
||||
if(n <= polls->size)
|
||||
return 0;
|
||||
|
||||
new = um_kmalloc_atomic(n * sizeof(struct pollfd));
|
||||
if(new == NULL){
|
||||
printk("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);
|
||||
n = os_write_file(sigio_private[0], &c, sizeof(c));
|
||||
if(n != sizeof(c)){
|
||||
printk("update_thread : write failed, err = %d\n", -n);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
n = os_read_file(sigio_private[0], &c, sizeof(c));
|
||||
if(n != sizeof(c)){
|
||||
printk("update_thread : read failed, err = %d\n", -n);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
set_signals(flags);
|
||||
return;
|
||||
fail:
|
||||
/* Critical section start */
|
||||
if(write_sigio_pid != -1)
|
||||
os_kill_process(write_sigio_pid, 1);
|
||||
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 = ¤t_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 = um_kmalloc(sizeof(struct pollfd));
|
||||
if (p == NULL) {
|
||||
printk("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)
|
||||
{
|
||||
unsigned long stack;
|
||||
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("write_sigio_workaround - os_pipe 1 failed, "
|
||||
"err = %d\n", -err);
|
||||
return;
|
||||
}
|
||||
err = os_pipe(l_sigio_private, 1, 1);
|
||||
if(err < 0){
|
||||
printk("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, &stack, 0);
|
||||
|
||||
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 maybe_sigio_broken(int fd, int read)
|
||||
{
|
||||
int err;
|
||||
|
||||
if(!isatty(fd))
|
||||
return;
|
||||
|
||||
if((read || pty_output_sigio) && (!read || pty_close_sigio))
|
||||
return;
|
||||
|
||||
write_sigio_workaround();
|
||||
|
||||
sigio_lock();
|
||||
err = need_poll(&all_sigio_fds, all_sigio_fds.used + 1);
|
||||
if(err){
|
||||
printk("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();
|
||||
}
|
||||
|
||||
static void sigio_cleanup(void)
|
||||
{
|
||||
if(write_sigio_pid != -1){
|
||||
os_kill_process(write_sigio_pid, 1);
|
||||
write_sigio_pid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
__uml_exitcall(sigio_cleanup);
|
||||
245
arch/um/os-Linux/signal.c
Normal file
245
arch/um/os-Linux/signal.c
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (C) 2004 PathScale, Inc
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include "user_util.h"
|
||||
#include "user.h"
|
||||
#include "signal_kern.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "sysdep/barrier.h"
|
||||
#include "sigcontext.h"
|
||||
#include "mode.h"
|
||||
#include "os.h"
|
||||
|
||||
/* These are the asynchronous signals. SIGVTALRM and SIGARLM are handled
|
||||
* together under SIGVTALRM_BIT. 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 SIGVTALRM_BIT 1
|
||||
#define SIGVTALRM_MASK (1 << SIGVTALRM_BIT)
|
||||
|
||||
#define SIGALRM_BIT 2
|
||||
#define SIGALRM_MASK (1 << SIGALRM_BIT)
|
||||
|
||||
/* These are used by both the signal handlers and
|
||||
* block/unblock_signals. I don't want modifications cached in a
|
||||
* register - they must go straight to memory.
|
||||
*/
|
||||
static volatile int signals_enabled = 1;
|
||||
static volatile int pending = 0;
|
||||
|
||||
void sig_handler(int sig, struct sigcontext *sc)
|
||||
{
|
||||
int enabled;
|
||||
|
||||
enabled = signals_enabled;
|
||||
if(!enabled && (sig == SIGIO)){
|
||||
pending |= SIGIO_MASK;
|
||||
return;
|
||||
}
|
||||
|
||||
block_signals();
|
||||
|
||||
CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
|
||||
sig, sc);
|
||||
|
||||
set_signals(enabled);
|
||||
}
|
||||
|
||||
static void real_alarm_handler(int sig, struct sigcontext *sc)
|
||||
{
|
||||
if(sig == SIGALRM)
|
||||
switch_timers(0);
|
||||
|
||||
CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
|
||||
sig, sc);
|
||||
|
||||
if(sig == SIGALRM)
|
||||
switch_timers(1);
|
||||
|
||||
}
|
||||
|
||||
void alarm_handler(int sig, struct sigcontext *sc)
|
||||
{
|
||||
int enabled;
|
||||
|
||||
enabled = signals_enabled;
|
||||
if(!signals_enabled){
|
||||
if(sig == SIGVTALRM)
|
||||
pending |= SIGVTALRM_MASK;
|
||||
else pending |= SIGALRM_MASK;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
block_signals();
|
||||
|
||||
real_alarm_handler(sig, sc);
|
||||
set_signals(enabled);
|
||||
}
|
||||
|
||||
void set_sigstack(void *sig_stack, int size)
|
||||
{
|
||||
stack_t stack = ((stack_t) { .ss_flags = 0,
|
||||
.ss_sp = (__ptr_t) sig_stack,
|
||||
.ss_size = size - sizeof(void *) });
|
||||
|
||||
if(sigaltstack(&stack, NULL) != 0)
|
||||
panic("enabling signal stack failed, errno = %d\n", errno);
|
||||
}
|
||||
|
||||
void remove_sigstack(void)
|
||||
{
|
||||
stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE,
|
||||
.ss_sp = NULL,
|
||||
.ss_size = 0 });
|
||||
|
||||
if(sigaltstack(&stack, NULL) != 0)
|
||||
panic("disabling signal stack failed, errno = %d\n", errno);
|
||||
}
|
||||
|
||||
void (*handlers[_NSIG])(int sig, struct sigcontext *sc);
|
||||
|
||||
extern void hard_handler(int sig);
|
||||
|
||||
void set_handler(int sig, void (*handler)(int), int flags, ...)
|
||||
{
|
||||
struct sigaction action;
|
||||
va_list ap;
|
||||
sigset_t sig_mask;
|
||||
int mask;
|
||||
|
||||
handlers[sig] = (void (*)(int, struct sigcontext *)) handler;
|
||||
action.sa_handler = hard_handler;
|
||||
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
va_start(ap, flags);
|
||||
while((mask = va_arg(ap, int)) != -1)
|
||||
sigaddset(&action.sa_mask, mask);
|
||||
va_end(ap);
|
||||
|
||||
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, old;
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, signal);
|
||||
sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old);
|
||||
return(!sigismember(&old, signal));
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
mb();
|
||||
}
|
||||
|
||||
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 pending.
|
||||
*/
|
||||
while(1){
|
||||
/* Save and reset save_pending after enabling signals. This
|
||||
* way, pending won't be changed while we're reading it.
|
||||
*/
|
||||
signals_enabled = 1;
|
||||
|
||||
/* Setting signals_enabled and reading pending must
|
||||
* happen in this order.
|
||||
*/
|
||||
mb();
|
||||
|
||||
save_pending = pending;
|
||||
if(save_pending == 0){
|
||||
/* This must return with signals enabled, so
|
||||
* this barrier ensures that writes are
|
||||
* flushed out before the return. This might
|
||||
* matter if gcc figures out how to inline
|
||||
* this (unlikely, given its size) and decides
|
||||
* to shuffle this code into the caller.
|
||||
*/
|
||||
mb();
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
if(save_pending & SIGIO_MASK)
|
||||
CHOOSE_MODE_PROC(sig_handler_common_tt,
|
||||
sig_handler_common_skas, SIGIO, NULL);
|
||||
|
||||
if(save_pending & SIGALRM_MASK)
|
||||
real_alarm_handler(SIGALRM, NULL);
|
||||
|
||||
if(save_pending & SIGVTALRM_MASK)
|
||||
real_alarm_handler(SIGVTALRM, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
10
arch/um/os-Linux/skas/Makefile
Normal file
10
arch/um/os-Linux/skas/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-y := mem.o process.o trap.o
|
||||
|
||||
USER_OBJS := mem.o process.o trap.o
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
287
arch/um/os-Linux/skas/mem.c
Normal file
287
arch/um/os-Linux/skas/mem.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/unistd.h>
|
||||
#include "mem_user.h"
|
||||
#include "mem.h"
|
||||
#include "skas.h"
|
||||
#include "user.h"
|
||||
#include "os.h"
|
||||
#include "proc_mm.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "task.h"
|
||||
#include "registers.h"
|
||||
#include "uml-config.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sysdep/stub.h"
|
||||
|
||||
extern unsigned long batch_syscall_stub, __syscall_stub_start;
|
||||
|
||||
extern void wait_stub_done(int pid, int sig, char * fname);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
extern int proc_mm;
|
||||
|
||||
int single_count = 0;
|
||||
int multi_count = 0;
|
||||
int multi_op_count = 0;
|
||||
|
||||
static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr)
|
||||
{
|
||||
unsigned long regs[MAX_REG_NR];
|
||||
int n, i;
|
||||
long ret, offset;
|
||||
unsigned long * data;
|
||||
unsigned long * syscall;
|
||||
int pid = mm_idp->u.pid;
|
||||
|
||||
if(proc_mm)
|
||||
#warning Need to look up userspace_pid by cpu
|
||||
pid = userspace_pid[0];
|
||||
|
||||
multi_count++;
|
||||
|
||||
get_safe_registers(regs, NULL);
|
||||
regs[REGS_IP_INDEX] = UML_CONFIG_STUB_CODE +
|
||||
((unsigned long) &batch_syscall_stub -
|
||||
(unsigned long) &__syscall_stub_start);
|
||||
|
||||
n = ptrace_setregs(pid, regs);
|
||||
if(n < 0){
|
||||
printk("Registers - \n");
|
||||
for(i = 0; i < MAX_REG_NR; i++)
|
||||
printk("\t%d\t0x%lx\n", i, regs[i]);
|
||||
panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n",
|
||||
-n);
|
||||
}
|
||||
|
||||
wait_stub_done(pid, 0, "do_syscall_stub");
|
||||
|
||||
/* 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 - UML_CONFIG_STUB_DATA);
|
||||
printk("do_syscall_stub : ret = %ld, offset = %ld, "
|
||||
"data = %p\n", ret, offset, data);
|
||||
syscall = (unsigned long *)((unsigned long)data + data[0]);
|
||||
printk("do_syscall_stub: syscall %ld failed, return value = "
|
||||
"0x%lx, expected return value = 0x%lx\n",
|
||||
syscall[0], ret, syscall[7]);
|
||||
printk(" 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(" additional syscall data:");
|
||||
if(n % 4 == 1)
|
||||
printk("\n ");
|
||||
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);
|
||||
|
||||
if(done && *addr == NULL)
|
||||
single_count++;
|
||||
|
||||
*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;
|
||||
multi_op_count++;
|
||||
|
||||
if(!done && ((((unsigned long) stack) & ~PAGE_MASK) <
|
||||
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) & ~PAGE_MASK) >=
|
||||
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) & ~PAGE_MASK) +
|
||||
UML_CONFIG_STUB_DATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int map(struct mm_id * mm_idp, unsigned long virt, unsigned long len,
|
||||
int r, int w, int x, int phys_fd, unsigned long long offset,
|
||||
int done, void **data)
|
||||
{
|
||||
int prot, ret;
|
||||
|
||||
prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
|
||||
(x ? PROT_EXEC : 0);
|
||||
|
||||
if(proc_mm){
|
||||
struct proc_mm_op map;
|
||||
int fd = mm_idp->u.mm_fd;
|
||||
|
||||
map = ((struct proc_mm_op) { .op = MM_MMAP,
|
||||
.u =
|
||||
{ .mmap =
|
||||
{ .addr = virt,
|
||||
.len = len,
|
||||
.prot = prot,
|
||||
.flags = MAP_SHARED |
|
||||
MAP_FIXED,
|
||||
.fd = phys_fd,
|
||||
.offset= offset
|
||||
} } } );
|
||||
ret = os_write_file(fd, &map, sizeof(map));
|
||||
if(ret != sizeof(map))
|
||||
printk("map : /proc/mm map failed, err = %d\n", -ret);
|
||||
else ret = 0;
|
||||
}
|
||||
else {
|
||||
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, void *addr, unsigned long len, int done,
|
||||
void **data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(proc_mm){
|
||||
struct proc_mm_op unmap;
|
||||
int fd = mm_idp->u.mm_fd;
|
||||
|
||||
unmap = ((struct proc_mm_op) { .op = MM_MUNMAP,
|
||||
.u =
|
||||
{ .munmap =
|
||||
{ .addr =
|
||||
(unsigned long) addr,
|
||||
.len = len } } } );
|
||||
ret = os_write_file(fd, &unmap, sizeof(unmap));
|
||||
if(ret != sizeof(unmap))
|
||||
printk("unmap - proc_mm write returned %d\n", ret);
|
||||
else ret = 0;
|
||||
}
|
||||
else {
|
||||
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,
|
||||
int r, int w, int x, int done, void **data)
|
||||
{
|
||||
struct proc_mm_op protect;
|
||||
int prot, ret;
|
||||
|
||||
prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |
|
||||
(x ? PROT_EXEC : 0);
|
||||
if(proc_mm){
|
||||
int fd = mm_idp->u.mm_fd;
|
||||
|
||||
protect = ((struct proc_mm_op) { .op = MM_MPROTECT,
|
||||
.u =
|
||||
{ .mprotect =
|
||||
{ .addr =
|
||||
(unsigned long) addr,
|
||||
.len = len,
|
||||
.prot = prot } } } );
|
||||
|
||||
ret = os_write_file(fd, &protect, sizeof(protect));
|
||||
if(ret != sizeof(protect))
|
||||
printk("protect failed, err = %d", -ret);
|
||||
else ret = 0;
|
||||
}
|
||||
else {
|
||||
unsigned long args[] = { addr, len, prot, 0, 0, 0 };
|
||||
|
||||
ret = run_syscall_stub(mm_idp, __NR_mprotect, args, 0,
|
||||
data, done);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void before_mem_skas(unsigned long unused)
|
||||
{
|
||||
}
|
||||
550
arch/um/os-Linux/skas/process.c
Normal file
550
arch/um/os-Linux/skas/process.c
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* Copyright (C) 2002- 2004 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include "ptrace_user.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <asm/types.h>
|
||||
#include "user.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "skas.h"
|
||||
#include "stub-data.h"
|
||||
#include "mm_id.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "sysdep/stub.h"
|
||||
#include "os.h"
|
||||
#include "proc_mm.h"
|
||||
#include "skas_ptrace.h"
|
||||
#include "chan_user.h"
|
||||
#include "registers.h"
|
||||
#include "mem.h"
|
||||
#include "uml-config.h"
|
||||
#include "process.h"
|
||||
#include "longjmp.h"
|
||||
|
||||
int is_skas_winch(int pid, int fd, void *data)
|
||||
{
|
||||
if(pid != os_getpgrp())
|
||||
return(0);
|
||||
|
||||
register_winch_irq(-1, fd, -1, data);
|
||||
return(1);
|
||||
}
|
||||
|
||||
void wait_stub_done(int pid, int sig, char * fname)
|
||||
{
|
||||
int n, status, err;
|
||||
|
||||
do {
|
||||
if ( sig != -1 ) {
|
||||
err = ptrace(PTRACE_CONT, pid, 0, sig);
|
||||
if(err)
|
||||
panic("%s : continue failed, errno = %d\n",
|
||||
fname, errno);
|
||||
}
|
||||
sig = 0;
|
||||
|
||||
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
|
||||
} while((n >= 0) && WIFSTOPPED(status) &&
|
||||
((WSTOPSIG(status) == SIGVTALRM) ||
|
||||
/* running UML inside a detached screen can cause
|
||||
* SIGWINCHes
|
||||
*/
|
||||
(WSTOPSIG(status) == SIGWINCH)));
|
||||
|
||||
if((n < 0) || !WIFSTOPPED(status) ||
|
||||
(WSTOPSIG(status) != SIGUSR1 && WSTOPSIG(status) != SIGTRAP)){
|
||||
unsigned long regs[MAX_REG_NR];
|
||||
|
||||
if(ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
|
||||
printk("Failed to get registers from stub, "
|
||||
"errno = %d\n", errno);
|
||||
else {
|
||||
int i;
|
||||
|
||||
printk("Stub registers -\n");
|
||||
for(i = 0; i < ARRAY_SIZE(regs); i++)
|
||||
printk("\t%d - %lx\n", i, regs[i]);
|
||||
}
|
||||
panic("%s : failed to wait for SIGUSR1/SIGTRAP, "
|
||||
"pid = %d, n = %d, errno = %d, status = 0x%x\n",
|
||||
fname, pid, n, errno, status);
|
||||
}
|
||||
}
|
||||
|
||||
extern unsigned long current_stub_stack(void);
|
||||
|
||||
void get_skas_faultinfo(int pid, struct faultinfo * fi)
|
||||
{
|
||||
int err;
|
||||
|
||||
if(ptrace_faultinfo){
|
||||
err = ptrace(PTRACE_FAULTINFO, pid, 0, fi);
|
||||
if(err)
|
||||
panic("get_skas_faultinfo - PTRACE_FAULTINFO failed, "
|
||||
"errno = %d\n", errno);
|
||||
|
||||
/* Special handling for i386, which has different structs */
|
||||
if (sizeof(struct ptrace_faultinfo) < sizeof(struct faultinfo))
|
||||
memset((char *)fi + sizeof(struct ptrace_faultinfo), 0,
|
||||
sizeof(struct faultinfo) -
|
||||
sizeof(struct ptrace_faultinfo));
|
||||
}
|
||||
else {
|
||||
wait_stub_done(pid, SIGSEGV, "get_skas_faultinfo");
|
||||
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_segv(int pid, union uml_pt_regs * regs)
|
||||
{
|
||||
get_skas_faultinfo(pid, ®s->skas.faultinfo);
|
||||
segv(regs->skas.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, union uml_pt_regs *regs, int local_using_sysemu)
|
||||
{
|
||||
int err, status;
|
||||
|
||||
/* Mark this as a syscall */
|
||||
UPT_SYSCALL_NR(regs) = PT_SYSCALL_NR(regs->skas.regs);
|
||||
|
||||
if (!local_using_sysemu)
|
||||
{
|
||||
err = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
|
||||
__NR_getpid);
|
||||
if(err < 0)
|
||||
panic("handle_trap - nullifying syscall failed errno = %d\n",
|
||||
errno);
|
||||
|
||||
err = ptrace(PTRACE_SYSCALL, pid, 0, 0);
|
||||
if(err < 0)
|
||||
panic("handle_trap - continuing to end of syscall failed, "
|
||||
"errno = %d\n", errno);
|
||||
|
||||
CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
|
||||
if((err < 0) || !WIFSTOPPED(status) ||
|
||||
(WSTOPSIG(status) != SIGTRAP + 0x80))
|
||||
panic("handle_trap - failed to wait at end of syscall, "
|
||||
"errno = %d, status = %d\n", errno, status);
|
||||
}
|
||||
|
||||
handle_syscall(regs);
|
||||
}
|
||||
|
||||
extern int __syscall_stub_start;
|
||||
|
||||
static int userspace_tramp(void *stack)
|
||||
{
|
||||
void *addr;
|
||||
int err;
|
||||
|
||||
ptrace(PTRACE_TRACEME, 0, 0, 0);
|
||||
|
||||
init_new_thread_signals();
|
||||
err = set_interval(1);
|
||||
if(err)
|
||||
panic("userspace_tramp - setting timer failed, errno = %d\n",
|
||||
err);
|
||||
|
||||
if(!proc_mm){
|
||||
/* This has a pte, but it can't be mapped in with the usual
|
||||
* tlb_flush mechanism because this is part of that mechanism
|
||||
*/
|
||||
int fd;
|
||||
__u64 offset;
|
||||
fd = phys_mapping(to_phys(&__syscall_stub_start), &offset);
|
||||
addr = mmap64((void *) UML_CONFIG_STUB_CODE, page_size(),
|
||||
PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset);
|
||||
if(addr == MAP_FAILED){
|
||||
printk("mapping mmap stub failed, errno = %d\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(stack != NULL){
|
||||
fd = phys_mapping(to_phys(stack), &offset);
|
||||
addr = mmap((void *) UML_CONFIG_STUB_DATA, page_size(),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_SHARED, fd, offset);
|
||||
if(addr == MAP_FAILED){
|
||||
printk("mapping segfault stack failed, "
|
||||
"errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!ptrace_faultinfo && (stack != NULL)){
|
||||
struct sigaction sa;
|
||||
|
||||
unsigned long v = UML_CONFIG_STUB_CODE +
|
||||
(unsigned long) stub_segv_handler -
|
||||
(unsigned long) &__syscall_stub_start;
|
||||
|
||||
set_sigstack((void *) UML_CONFIG_STUB_DATA, page_size());
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGIO);
|
||||
sigaddset(&sa.sa_mask, SIGWINCH);
|
||||
sigaddset(&sa.sa_mask, SIGALRM);
|
||||
sigaddset(&sa.sa_mask, SIGVTALRM);
|
||||
sigaddset(&sa.sa_mask, SIGUSR1);
|
||||
sa.sa_flags = SA_ONSTACK;
|
||||
sa.sa_handler = (void *) v;
|
||||
sa.sa_restorer = NULL;
|
||||
if(sigaction(SIGSEGV, &sa, NULL) < 0)
|
||||
panic("userspace_tramp - setting SIGSEGV handler "
|
||||
"failed - errno = %d\n", errno);
|
||||
}
|
||||
|
||||
os_stop_process(os_getpid());
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Each element set once, and only accessed by a single processor anyway */
|
||||
#undef NR_CPUS
|
||||
#define NR_CPUS 1
|
||||
int userspace_pid[NR_CPUS];
|
||||
|
||||
int start_userspace(unsigned long stub_stack)
|
||||
{
|
||||
void *stack;
|
||||
unsigned long sp;
|
||||
int pid, status, n, flags;
|
||||
|
||||
stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if(stack == MAP_FAILED)
|
||||
panic("start_userspace : mmap failed, errno = %d", errno);
|
||||
sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
|
||||
|
||||
flags = CLONE_FILES | SIGCHLD;
|
||||
if(proc_mm) flags |= CLONE_VM;
|
||||
pid = clone(userspace_tramp, (void *) sp, flags, (void *) stub_stack);
|
||||
if(pid < 0)
|
||||
panic("start_userspace : clone failed, errno = %d", errno);
|
||||
|
||||
do {
|
||||
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
|
||||
if(n < 0)
|
||||
panic("start_userspace : wait failed, errno = %d",
|
||||
errno);
|
||||
} while(WIFSTOPPED(status) && (WSTOPSIG(status) == SIGVTALRM));
|
||||
|
||||
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
|
||||
panic("start_userspace : expected SIGSTOP, got status = %d",
|
||||
status);
|
||||
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL, (void *)PTRACE_O_TRACESYSGOOD) < 0)
|
||||
panic("start_userspace : PTRACE_OLDSETOPTIONS failed, errno=%d\n",
|
||||
errno);
|
||||
|
||||
if(munmap(stack, PAGE_SIZE) < 0)
|
||||
panic("start_userspace : munmap failed, errno = %d\n", errno);
|
||||
|
||||
return(pid);
|
||||
}
|
||||
|
||||
void userspace(union uml_pt_regs *regs)
|
||||
{
|
||||
int err, status, op, pid = userspace_pid[0];
|
||||
int local_using_sysemu; /*To prevent races if using_sysemu changes under us.*/
|
||||
|
||||
while(1){
|
||||
restore_registers(pid, regs);
|
||||
|
||||
/* 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));
|
||||
|
||||
err = ptrace(op, pid, 0, 0);
|
||||
if(err)
|
||||
panic("userspace - could not resume userspace process, "
|
||||
"pid=%d, ptrace operation = %d, errno = %d\n",
|
||||
pid, op, errno);
|
||||
|
||||
CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED));
|
||||
if(err < 0)
|
||||
panic("userspace - waitpid failed, errno = %d\n",
|
||||
errno);
|
||||
|
||||
regs->skas.is_user = 1;
|
||||
save_registers(pid, regs);
|
||||
UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */
|
||||
|
||||
if(WIFSTOPPED(status)){
|
||||
switch(WSTOPSIG(status)){
|
||||
case SIGSEGV:
|
||||
if(PTRACE_FULL_FAULTINFO || !ptrace_faultinfo)
|
||||
user_signal(SIGSEGV, regs, pid);
|
||||
else handle_segv(pid, regs);
|
||||
break;
|
||||
case SIGTRAP + 0x80:
|
||||
handle_trap(pid, regs, local_using_sysemu);
|
||||
break;
|
||||
case SIGTRAP:
|
||||
relay_signal(SIGTRAP, regs);
|
||||
break;
|
||||
case SIGIO:
|
||||
case SIGVTALRM:
|
||||
case SIGILL:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGWINCH:
|
||||
user_signal(WSTOPSIG(status), regs, pid);
|
||||
break;
|
||||
default:
|
||||
printk("userspace - child stopped with signal "
|
||||
"%d\n", WSTOPSIG(status));
|
||||
}
|
||||
pid = userspace_pid[0];
|
||||
interrupt_end();
|
||||
|
||||
/* Avoid -ERESTARTSYS handling in host */
|
||||
if(PT_SYSCALL_NR_OFFSET != PT_SYSCALL_RET_OFFSET)
|
||||
PT_SYSCALL_NR(regs->skas.regs) = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int copy_context_skas0(unsigned long new_stack, int pid)
|
||||
{
|
||||
int err;
|
||||
unsigned long regs[MAX_REG_NR];
|
||||
unsigned long fp_regs[HOST_FP_SIZE];
|
||||
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;
|
||||
__u64 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,
|
||||
.timer = ((struct itimerval)
|
||||
{ { 0, 1000000 / hz() },
|
||||
{ 0, 1000000 / hz() }})});
|
||||
get_safe_registers(regs, fp_regs);
|
||||
|
||||
/* Set parent's instruction pointer to start of clone-stub */
|
||||
regs[REGS_IP_INDEX] = UML_CONFIG_STUB_CODE +
|
||||
(unsigned long) stub_clone_handler -
|
||||
(unsigned long) &__syscall_stub_start;
|
||||
regs[REGS_SP_INDEX] = UML_CONFIG_STUB_DATA + PAGE_SIZE -
|
||||
sizeof(void *);
|
||||
#ifdef __SIGNAL_FRAMESIZE
|
||||
regs[REGS_SP_INDEX] -= __SIGNAL_FRAMESIZE;
|
||||
#endif
|
||||
err = ptrace_setregs(pid, regs);
|
||||
if(err < 0)
|
||||
panic("copy_context_skas0 : PTRACE_SETREGS failed, "
|
||||
"pid = %d, errno = %d\n", pid, -err);
|
||||
|
||||
err = ptrace_setfpregs(pid, fp_regs);
|
||||
if(err < 0)
|
||||
panic("copy_context_skas0 : PTRACE_SETFPREGS failed, "
|
||||
"pid = %d, errno = %d\n", pid, -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.
|
||||
*/
|
||||
wait_stub_done(pid, 0, "copy_context_skas0");
|
||||
|
||||
pid = data->err;
|
||||
if(pid < 0)
|
||||
panic("copy_context_skas0 - stub-parent reports error %d\n",
|
||||
-pid);
|
||||
|
||||
/* Wait, until child has finished too: read child's result from
|
||||
* child's stack and check it.
|
||||
*/
|
||||
wait_stub_done(pid, -1, "copy_context_skas0");
|
||||
if (child_data->err != UML_CONFIG_STUB_DATA)
|
||||
panic("copy_context_skas0 - stub-child reports error %ld\n",
|
||||
child_data->err);
|
||||
|
||||
if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL,
|
||||
(void *)PTRACE_O_TRACESYSGOOD) < 0)
|
||||
panic("copy_context_skas0 : PTRACE_OLDSETOPTIONS failed, "
|
||||
"errno = %d\n", errno);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is used only, if stub pages are needed, while proc_mm is
|
||||
* availabl. Opening /proc/mm creates a new mm_context, which lacks
|
||||
* the stub-pages. Thus, we map them using /proc/mm-fd
|
||||
*/
|
||||
void map_stub_pages(int fd, unsigned long code,
|
||||
unsigned long data, unsigned long stack)
|
||||
{
|
||||
struct proc_mm_op mmop;
|
||||
int n;
|
||||
__u64 code_offset;
|
||||
int code_fd = phys_mapping(to_phys((void *) &__syscall_stub_start),
|
||||
&code_offset);
|
||||
|
||||
mmop = ((struct proc_mm_op) { .op = MM_MMAP,
|
||||
.u =
|
||||
{ .mmap =
|
||||
{ .addr = code,
|
||||
.len = PAGE_SIZE,
|
||||
.prot = PROT_EXEC,
|
||||
.flags = MAP_FIXED | MAP_PRIVATE,
|
||||
.fd = code_fd,
|
||||
.offset = code_offset
|
||||
} } });
|
||||
n = os_write_file(fd, &mmop, sizeof(mmop));
|
||||
if(n != sizeof(mmop)){
|
||||
printk("mmap args - addr = 0x%lx, fd = %d, offset = %llx\n",
|
||||
code, code_fd, (unsigned long long) code_offset);
|
||||
panic("map_stub_pages : /proc/mm map for code failed, "
|
||||
"err = %d\n", -n);
|
||||
}
|
||||
|
||||
if ( stack ) {
|
||||
__u64 map_offset;
|
||||
int map_fd = phys_mapping(to_phys((void *)stack), &map_offset);
|
||||
mmop = ((struct proc_mm_op)
|
||||
{ .op = MM_MMAP,
|
||||
.u =
|
||||
{ .mmap =
|
||||
{ .addr = data,
|
||||
.len = PAGE_SIZE,
|
||||
.prot = PROT_READ | PROT_WRITE,
|
||||
.flags = MAP_FIXED | MAP_SHARED,
|
||||
.fd = map_fd,
|
||||
.offset = map_offset
|
||||
} } });
|
||||
n = os_write_file(fd, &mmop, sizeof(mmop));
|
||||
if(n != sizeof(mmop))
|
||||
panic("map_stub_pages : /proc/mm map for data failed, "
|
||||
"err = %d\n", -n);
|
||||
}
|
||||
}
|
||||
|
||||
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 +
|
||||
(PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) - 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, (__sighandler_t) sig_handler,
|
||||
SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGALRM,
|
||||
SIGVTALRM, -1);
|
||||
|
||||
n = UML_SETJMP(&initial_jmpbuf);
|
||||
switch(n){
|
||||
case INIT_JMP_NEW_THREAD:
|
||||
(*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler;
|
||||
(*switch_buf)[0].JB_SP = (unsigned long) stack +
|
||||
(PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) -
|
||||
sizeof(void *);
|
||||
break;
|
||||
case INIT_JMP_CALLBACK:
|
||||
(*cb_proc)(cb_arg);
|
||||
UML_LONGJMP(cb_back, 1);
|
||||
break;
|
||||
case INIT_JMP_HALT:
|
||||
kmalloc_ok = 0;
|
||||
return(0);
|
||||
case INIT_JMP_REBOOT:
|
||||
kmalloc_ok = 0;
|
||||
return(1);
|
||||
default:
|
||||
panic("Bad sigsetjmp return in start_idle_thread - %d\n", n);
|
||||
}
|
||||
UML_LONGJMP(switch_buf, 1);
|
||||
}
|
||||
|
||||
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_skas(struct mm_id *mm_idp)
|
||||
{
|
||||
int err;
|
||||
|
||||
#warning need cpu pid in switch_mm_skas
|
||||
if(proc_mm){
|
||||
err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0,
|
||||
mm_idp->u.mm_fd);
|
||||
if(err)
|
||||
panic("switch_mm_skas - PTRACE_SWITCH_MM failed, "
|
||||
"errno = %d\n", errno);
|
||||
}
|
||||
else userspace_pid[0] = mm_idp->u.pid;
|
||||
}
|
||||
73
arch/um/os-Linux/skas/trap.c
Normal file
73
arch/um/os-Linux/skas/trap.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "task.h"
|
||||
#include "sigcontext.h"
|
||||
#include "skas.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sysdep/ptrace_user.h"
|
||||
#include "os.h"
|
||||
|
||||
void sig_handler_common_skas(int sig, void *sc_ptr)
|
||||
{
|
||||
struct sigcontext *sc = sc_ptr;
|
||||
struct skas_regs *r;
|
||||
void (*handler)(int, union uml_pt_regs *);
|
||||
int save_errno = errno;
|
||||
int save_user;
|
||||
|
||||
/* This is done because to allow SIGSEGV to be delivered inside a SEGV
|
||||
* handler. This can happen in copy_user, and if SEGV is disabled,
|
||||
* the process will die.
|
||||
* XXX Figure out why this is better than SA_NODEFER
|
||||
*/
|
||||
if(sig == SIGSEGV)
|
||||
change_sig(SIGSEGV, 1);
|
||||
|
||||
r = &TASK_REGS(get_current())->skas;
|
||||
save_user = r->is_user;
|
||||
r->is_user = 0;
|
||||
if ( sig == SIGFPE || sig == SIGSEGV ||
|
||||
sig == SIGBUS || sig == SIGILL ||
|
||||
sig == SIGTRAP ) {
|
||||
GET_FAULTINFO_FROM_SC(r->faultinfo, sc);
|
||||
}
|
||||
|
||||
change_sig(SIGUSR1, 1);
|
||||
|
||||
handler = sig_info[sig];
|
||||
|
||||
/* unblock SIGALRM, SIGVTALRM, SIGIO if sig isn't IRQ signal */
|
||||
if (sig != SIGIO && sig != SIGWINCH &&
|
||||
sig != SIGVTALRM && sig != SIGALRM)
|
||||
unblock_signals();
|
||||
|
||||
handler(sig, (union uml_pt_regs *) r);
|
||||
|
||||
errno = save_errno;
|
||||
r->is_user = save_user;
|
||||
}
|
||||
|
||||
extern int ptrace_faultinfo;
|
||||
|
||||
void user_signal(int sig, union uml_pt_regs *regs, int pid)
|
||||
{
|
||||
void (*handler)(int, union uml_pt_regs *);
|
||||
int segv = ((sig == SIGFPE) || (sig == SIGSEGV) || (sig == SIGBUS) ||
|
||||
(sig == SIGILL) || (sig == SIGTRAP));
|
||||
|
||||
if (segv)
|
||||
get_skas_faultinfo(pid, ®s->skas.faultinfo);
|
||||
|
||||
handler = sig_info[sig];
|
||||
handler(sig, (union uml_pt_regs *) regs);
|
||||
|
||||
unblock_signals();
|
||||
}
|
||||
683
arch/um/os-Linux/start_up.c
Normal file
683
arch/um/os-Linux/start_up.c
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <pty.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/page.h>
|
||||
#include <sys/types.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "signal_kern.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "irq_user.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "mem_user.h"
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
#include "uml-config.h"
|
||||
#include "choose-mode.h"
|
||||
#include "mode.h"
|
||||
#include "tempfile.h"
|
||||
#include "kern_constants.h"
|
||||
|
||||
#ifdef UML_CONFIG_MODE_SKAS
|
||||
#include "skas.h"
|
||||
#include "skas_ptrace.h"
|
||||
#include "registers.h"
|
||||
#endif
|
||||
|
||||
static int ptrace_child(void *arg)
|
||||
{
|
||||
int ret;
|
||||
int pid = os_getpid(), ppid = getppid();
|
||||
int sc_result;
|
||||
|
||||
change_sig(SIGWINCH, 0);
|
||||
if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
|
||||
perror("ptrace");
|
||||
os_kill_process(pid, 0);
|
||||
}
|
||||
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)
|
||||
ret = 1; /*Nothing modified by the parent, we are running
|
||||
normally.*/
|
||||
else if (sc_result == ppid)
|
||||
ret = 0; /*Expected in check_ptrace and check_sysemu when they
|
||||
succeed in modifying the stack frame*/
|
||||
else
|
||||
ret = 2; /*Serious trouble! This could be caused by a bug in
|
||||
host 2.6 SKAS3/2.6 patch before release -V6, together
|
||||
with a bug in the UML code itself.*/
|
||||
_exit(ret);
|
||||
}
|
||||
|
||||
static void fatal_perror(char *str)
|
||||
{
|
||||
perror(str);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void fatal(char *fmt, ...)
|
||||
{
|
||||
va_list list;
|
||||
|
||||
va_start(list, fmt);
|
||||
vprintf(fmt, list);
|
||||
va_end(list);
|
||||
fflush(stdout);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void non_fatal(char *fmt, ...)
|
||||
{
|
||||
va_list list;
|
||||
|
||||
va_start(list, fmt);
|
||||
vprintf(fmt, list);
|
||||
va_end(list);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static int start_ptraced_child(void **stack_out)
|
||||
{
|
||||
void *stack;
|
||||
unsigned long sp;
|
||||
int pid, n, status;
|
||||
|
||||
stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if(stack == MAP_FAILED)
|
||||
fatal_perror("check_ptrace : mmap failed");
|
||||
sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *);
|
||||
pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL);
|
||||
if(pid < 0)
|
||||
fatal_perror("start_ptraced_child : clone failed");
|
||||
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
|
||||
if(n < 0)
|
||||
fatal_perror("check_ptrace : clone failed");
|
||||
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
|
||||
fatal("check_ptrace : expected SIGSTOP, got status = %d",
|
||||
status);
|
||||
|
||||
*stack_out = stack;
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* When testing for SYSEMU support, if it is one of the broken versions, we
|
||||
* must just avoid using sysemu, not panic, but only if SYSEMU features are
|
||||
* broken.
|
||||
* So only for SYSEMU features we test mustpanic, while normal host features
|
||||
* must work anyway!
|
||||
*/
|
||||
static int stop_ptraced_child(int pid, void *stack, int exitcode,
|
||||
int mustexit)
|
||||
{
|
||||
int status, n, ret = 0;
|
||||
|
||||
if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
|
||||
fatal_perror("stop_ptraced_child : ptrace failed");
|
||||
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;
|
||||
}
|
||||
|
||||
if(munmap(stack, PAGE_SIZE) < 0)
|
||||
fatal_perror("check_ptrace : munmap failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Changed only during early boot */
|
||||
int ptrace_faultinfo = 1;
|
||||
int ptrace_ldt = 1;
|
||||
int proc_mm = 1;
|
||||
int skas_needs_stub = 0;
|
||||
|
||||
static int __init skas0_cmd_param(char *str, int* add)
|
||||
{
|
||||
ptrace_faultinfo = proc_mm = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The two __uml_setup would conflict, without this stupid alias. */
|
||||
|
||||
static int __init mode_skas0_cmd_param(char *str, int* add)
|
||||
__attribute__((alias("skas0_cmd_param")));
|
||||
|
||||
__uml_setup("skas0", skas0_cmd_param,
|
||||
"skas0\n"
|
||||
" Disables SKAS3 usage, so that SKAS0 is used, unless \n"
|
||||
" you specify mode=tt.\n\n");
|
||||
|
||||
__uml_setup("mode=skas0", mode_skas0_cmd_param,
|
||||
"mode=skas0\n"
|
||||
" Disables SKAS3 usage, so that SKAS0 is used, unless you \n"
|
||||
" specify mode=tt. Note that this was recently added - on \n"
|
||||
" older kernels you must use simply \"skas0\".\n\n");
|
||||
|
||||
/* 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) on.\n"
|
||||
" SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
|
||||
" behaviour of ptrace() and helps reducing host context switch rate.\n"
|
||||
" To make it working, you need a kernel patch for your host, too.\n"
|
||||
" See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
|
||||
" information.\n\n");
|
||||
|
||||
static void __init check_sysemu(void)
|
||||
{
|
||||
void *stack;
|
||||
unsigned long regs[MAX_REG_NR];
|
||||
int pid, n, status, count=0;
|
||||
|
||||
non_fatal("Checking syscall emulation patch for ptrace...");
|
||||
sysemu_supported = 0;
|
||||
pid = start_ptraced_child(&stack);
|
||||
|
||||
if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
|
||||
goto fail;
|
||||
|
||||
CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
|
||||
if (n < 0)
|
||||
fatal_perror("check_sysemu : wait failed");
|
||||
if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
|
||||
fatal("check_sysemu : expected SIGTRAP, got status = %d",
|
||||
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_POKEUSR, 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, stack, 0, 0) < 0)
|
||||
goto fail_stopped;
|
||||
|
||||
sysemu_supported = 1;
|
||||
non_fatal("OK\n");
|
||||
set_using_sysemu(!force_sysemu_disabled);
|
||||
|
||||
non_fatal("Checking advanced syscall emulation patch for ptrace...");
|
||||
pid = start_ptraced_child(&stack);
|
||||
|
||||
if((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
|
||||
(void *) PTRACE_O_TRACESYSGOOD) < 0))
|
||||
fatal_perror("check_ptrace: 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_ptrace : wait failed");
|
||||
|
||||
if(WIFSTOPPED(status) && (WSTOPSIG(status) == (SIGTRAP|0x80))){
|
||||
if (!count)
|
||||
fatal("check_ptrace : SYSEMU_SINGLESTEP "
|
||||
"doesn't singlestep");
|
||||
n = ptrace(PTRACE_POKEUSR, 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
|
||||
fatal("check_ptrace : expected SIGTRAP or "
|
||||
"(SIGTRAP | 0x80), got status = %d", status);
|
||||
}
|
||||
if (stop_ptraced_child(pid, stack, 0, 0) < 0)
|
||||
goto fail_stopped;
|
||||
|
||||
sysemu_supported = 2;
|
||||
non_fatal("OK\n");
|
||||
|
||||
if ( !force_sysemu_disabled )
|
||||
set_using_sysemu(sysemu_supported);
|
||||
return;
|
||||
|
||||
fail:
|
||||
stop_ptraced_child(pid, stack, 1, 0);
|
||||
fail_stopped:
|
||||
non_fatal("missing\n");
|
||||
}
|
||||
|
||||
static void __init check_ptrace(void)
|
||||
{
|
||||
void *stack;
|
||||
int pid, syscall, n, status;
|
||||
|
||||
non_fatal("Checking that ptrace can change system call numbers...");
|
||||
pid = start_ptraced_child(&stack);
|
||||
|
||||
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_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
|
||||
0);
|
||||
if(syscall == __NR_getpid){
|
||||
n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
|
||||
__NR_getppid);
|
||||
if(n < 0)
|
||||
fatal_perror("check_ptrace : failed to modify "
|
||||
"system call");
|
||||
break;
|
||||
}
|
||||
}
|
||||
stop_ptraced_child(pid, stack, 0, 1);
|
||||
non_fatal("OK\n");
|
||||
check_sysemu();
|
||||
}
|
||||
|
||||
extern void check_tmpexec(void);
|
||||
|
||||
void os_early_checks(void)
|
||||
{
|
||||
check_ptrace();
|
||||
|
||||
/* Need to check this early because mmapping happens before the
|
||||
* kernel is running.
|
||||
*/
|
||||
check_tmpexec();
|
||||
}
|
||||
|
||||
static int __init noprocmm_cmd_param(char *str, int* add)
|
||||
{
|
||||
proc_mm = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("noprocmm", noprocmm_cmd_param,
|
||||
"noprocmm\n"
|
||||
" Turns off usage of /proc/mm, even if host supports it.\n"
|
||||
" To support /proc/mm, the host needs to be patched using\n"
|
||||
" the current skas3 patch.\n\n");
|
||||
|
||||
static int __init noptracefaultinfo_cmd_param(char *str, int* add)
|
||||
{
|
||||
ptrace_faultinfo = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param,
|
||||
"noptracefaultinfo\n"
|
||||
" Turns off usage of PTRACE_FAULTINFO, even if host supports\n"
|
||||
" it. To support PTRACE_FAULTINFO, the host needs to be patched\n"
|
||||
" using the current skas3 patch.\n\n");
|
||||
|
||||
static int __init noptraceldt_cmd_param(char *str, int* add)
|
||||
{
|
||||
ptrace_ldt = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("noptraceldt", noptraceldt_cmd_param,
|
||||
"noptraceldt\n"
|
||||
" Turns off usage of PTRACE_LDT, even if host supports it.\n"
|
||||
" To support PTRACE_LDT, the host needs to be patched using\n"
|
||||
" the current skas3 patch.\n\n");
|
||||
|
||||
#ifdef UML_CONFIG_MODE_SKAS
|
||||
static inline void check_skas3_ptrace_faultinfo(void)
|
||||
{
|
||||
struct ptrace_faultinfo fi;
|
||||
void *stack;
|
||||
int pid, n;
|
||||
|
||||
non_fatal(" - PTRACE_FAULTINFO...");
|
||||
pid = start_ptraced_child(&stack);
|
||||
|
||||
n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi);
|
||||
if (n < 0) {
|
||||
ptrace_faultinfo = 0;
|
||||
if(errno == EIO)
|
||||
non_fatal("not found\n");
|
||||
else
|
||||
perror("not found");
|
||||
}
|
||||
else {
|
||||
if (!ptrace_faultinfo)
|
||||
non_fatal("found but disabled on command line\n");
|
||||
else
|
||||
non_fatal("found\n");
|
||||
}
|
||||
|
||||
init_registers(pid);
|
||||
stop_ptraced_child(pid, stack, 1, 1);
|
||||
}
|
||||
|
||||
static inline void check_skas3_ptrace_ldt(void)
|
||||
{
|
||||
#ifdef PTRACE_LDT
|
||||
void *stack;
|
||||
int pid, n;
|
||||
unsigned char ldtbuf[40];
|
||||
struct ptrace_ldt ldt_op = (struct ptrace_ldt) {
|
||||
.func = 2, /* read default ldt */
|
||||
.ptr = ldtbuf,
|
||||
.bytecount = sizeof(ldtbuf)};
|
||||
|
||||
non_fatal(" - PTRACE_LDT...");
|
||||
pid = start_ptraced_child(&stack);
|
||||
|
||||
n = ptrace(PTRACE_LDT, pid, 0, (unsigned long) &ldt_op);
|
||||
if (n < 0) {
|
||||
if(errno == EIO)
|
||||
non_fatal("not found\n");
|
||||
else {
|
||||
perror("not found");
|
||||
}
|
||||
ptrace_ldt = 0;
|
||||
}
|
||||
else {
|
||||
if(ptrace_ldt)
|
||||
non_fatal("found\n");
|
||||
else
|
||||
non_fatal("found, but use is disabled\n");
|
||||
}
|
||||
|
||||
stop_ptraced_child(pid, stack, 1, 1);
|
||||
#else
|
||||
/* PTRACE_LDT might be disabled via cmdline option.
|
||||
* We want to override this, else we might use the stub
|
||||
* without real need
|
||||
*/
|
||||
ptrace_ldt = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void check_skas3_proc_mm(void)
|
||||
{
|
||||
non_fatal(" - /proc/mm...");
|
||||
if (access("/proc/mm", W_OK) < 0) {
|
||||
proc_mm = 0;
|
||||
perror("not found");
|
||||
}
|
||||
else {
|
||||
if (!proc_mm)
|
||||
non_fatal("found but disabled on command line\n");
|
||||
else
|
||||
non_fatal("found\n");
|
||||
}
|
||||
}
|
||||
|
||||
int can_do_skas(void)
|
||||
{
|
||||
non_fatal("Checking for the skas3 patch in the host:\n");
|
||||
|
||||
check_skas3_proc_mm();
|
||||
check_skas3_ptrace_faultinfo();
|
||||
check_skas3_ptrace_ldt();
|
||||
|
||||
if(!proc_mm || !ptrace_faultinfo || !ptrace_ldt)
|
||||
skas_needs_stub = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
int can_do_skas(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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){
|
||||
printf("parse_iomem : failed to parse iomem\n");
|
||||
goto out;
|
||||
}
|
||||
*file = '\0';
|
||||
file++;
|
||||
fd = open(file, O_RDWR, 0);
|
||||
if(fd < 0){
|
||||
os_print_error(fd, "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;
|
||||
}
|
||||
|
||||
|
||||
/* Changed during early boot */
|
||||
int pty_output_sigio = 0;
|
||||
int pty_close_sigio = 0;
|
||||
|
||||
/* Used as a flag during SIGIO testing early in boot */
|
||||
static volatile int got_sigio = 0;
|
||||
|
||||
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("openpty failed, errno = %d\n", -pty.err);
|
||||
return;
|
||||
}
|
||||
|
||||
master = pty.master;
|
||||
slave = pty.slave;
|
||||
|
||||
if((master == -1) || (slave == -1)){
|
||||
printk("openpty failed to allocate a pty\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not now, but complain so we now where we failed. */
|
||||
err = raw(master);
|
||||
if (err < 0)
|
||||
panic("check_sigio : __raw failed, errno = %d\n", -err);
|
||||
|
||||
err = async_pty(master, slave);
|
||||
if(err < 0)
|
||||
panic("tty_fds : sigio_async failed, err = %d\n", -err);
|
||||
|
||||
if(sigaction(SIGIO, NULL, &old) < 0)
|
||||
panic("check_sigio : sigaction 1 failed, errno = %d\n", errno);
|
||||
new = old;
|
||||
new.sa_handler = handler;
|
||||
if(sigaction(SIGIO, &new, NULL) < 0)
|
||||
panic("check_sigio : sigaction 2 failed, errno = %d\n", errno);
|
||||
|
||||
got_sigio = 0;
|
||||
(*proc)(master, slave);
|
||||
|
||||
close(master);
|
||||
close(slave);
|
||||
|
||||
if(sigaction(SIGIO, &old, NULL) < 0)
|
||||
panic("check_sigio : sigaction 3 failed, errno = %d\n", errno);
|
||||
}
|
||||
|
||||
static void tty_output(int master, int slave)
|
||||
{
|
||||
int n;
|
||||
char buf[512];
|
||||
|
||||
printk("Checking that host ptys support output SIGIO...");
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
while(os_write_file(master, buf, sizeof(buf)) > 0) ;
|
||||
if(errno != EAGAIN)
|
||||
panic("check_sigio : write failed, errno = %d\n", errno);
|
||||
while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ;
|
||||
|
||||
if(got_sigio){
|
||||
printk("Yes\n");
|
||||
pty_output_sigio = 1;
|
||||
}
|
||||
else if(n == -EAGAIN) printk("No, enabling workaround\n");
|
||||
else panic("check_sigio : read failed, err = %d\n", n);
|
||||
}
|
||||
|
||||
static void tty_close(int master, int slave)
|
||||
{
|
||||
printk("Checking that host ptys support SIGIO on close...");
|
||||
|
||||
close(slave);
|
||||
if(got_sigio){
|
||||
printk("Yes\n");
|
||||
pty_close_sigio = 1;
|
||||
}
|
||||
else printk("No, enabling workaround\n");
|
||||
}
|
||||
|
||||
void __init check_sigio(void)
|
||||
{
|
||||
if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) &&
|
||||
(os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){
|
||||
printk("No pseudo-terminals available - skipping pty SIGIO "
|
||||
"check\n");
|
||||
return;
|
||||
}
|
||||
check_one_sigio(tty_output);
|
||||
check_one_sigio(tty_close);
|
||||
}
|
||||
|
||||
void os_check_bugs(void)
|
||||
{
|
||||
check_ptrace();
|
||||
check_sigio();
|
||||
}
|
||||
|
||||
10
arch/um/os-Linux/sys-i386/Makefile
Normal file
10
arch/um/os-Linux/sys-i386/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MODE_SKAS) = registers.o signal.o tls.o
|
||||
|
||||
USER_OBJS := $(obj-y)
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
144
arch/um/os-Linux/sys-i386/registers.c
Normal file
144
arch/um/os-Linux/sys-i386/registers.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2004 PathScale, Inc
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "sysdep/ptrace_user.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "uml-config.h"
|
||||
#include "skas_ptregs.h"
|
||||
#include "registers.h"
|
||||
#include "longjmp.h"
|
||||
#include "user.h"
|
||||
|
||||
/* These are set once at boot time and not changed thereafter */
|
||||
|
||||
static unsigned long exec_regs[MAX_REG_NR];
|
||||
static unsigned long exec_fp_regs[HOST_FP_SIZE];
|
||||
static unsigned long exec_fpx_regs[HOST_XFP_SIZE];
|
||||
static int have_fpx_regs = 1;
|
||||
|
||||
void init_thread_registers(union uml_pt_regs *to)
|
||||
{
|
||||
memcpy(to->skas.regs, exec_regs, sizeof(to->skas.regs));
|
||||
memcpy(to->skas.fp, exec_fp_regs, sizeof(to->skas.fp));
|
||||
if(have_fpx_regs)
|
||||
memcpy(to->skas.xfp, exec_fpx_regs, sizeof(to->skas.xfp));
|
||||
}
|
||||
|
||||
/* XXX These need to use [GS]ETFPXREGS and copy_sc_{to,from}_user_skas needs
|
||||
* to pass in a sufficiently large buffer
|
||||
*/
|
||||
int save_fp_registers(int pid, unsigned long *fp_regs)
|
||||
{
|
||||
if(ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int restore_fp_registers(int pid, unsigned long *fp_regs)
|
||||
{
|
||||
if(ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0)
|
||||
return -errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int move_registers(int pid, int int_op, union uml_pt_regs *regs,
|
||||
int fp_op, unsigned long *fp_regs)
|
||||
{
|
||||
if(ptrace(int_op, pid, 0, regs->skas.regs) < 0)
|
||||
return -errno;
|
||||
|
||||
if(ptrace(fp_op, pid, 0, fp_regs) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void save_registers(int pid, union uml_pt_regs *regs)
|
||||
{
|
||||
unsigned long *fp_regs;
|
||||
int err, fp_op;
|
||||
|
||||
if(have_fpx_regs){
|
||||
fp_op = PTRACE_GETFPXREGS;
|
||||
fp_regs = regs->skas.xfp;
|
||||
}
|
||||
else {
|
||||
fp_op = PTRACE_GETFPREGS;
|
||||
fp_regs = regs->skas.fp;
|
||||
}
|
||||
|
||||
err = move_registers(pid, PTRACE_GETREGS, regs, fp_op, fp_regs);
|
||||
if(err)
|
||||
panic("save_registers - saving registers failed, errno = %d\n",
|
||||
-err);
|
||||
}
|
||||
|
||||
void restore_registers(int pid, union uml_pt_regs *regs)
|
||||
{
|
||||
unsigned long *fp_regs;
|
||||
int err, fp_op;
|
||||
|
||||
if(have_fpx_regs){
|
||||
fp_op = PTRACE_SETFPXREGS;
|
||||
fp_regs = regs->skas.xfp;
|
||||
}
|
||||
else {
|
||||
fp_op = PTRACE_SETFPREGS;
|
||||
fp_regs = regs->skas.fp;
|
||||
}
|
||||
|
||||
err = move_registers(pid, PTRACE_SETREGS, regs, fp_op, fp_regs);
|
||||
if(err)
|
||||
panic("restore_registers - saving registers failed, "
|
||||
"errno = %d\n", -err);
|
||||
}
|
||||
|
||||
void init_registers(int pid)
|
||||
{
|
||||
int err;
|
||||
|
||||
memset(exec_regs, 0, sizeof(exec_regs));
|
||||
err = ptrace(PTRACE_GETREGS, pid, 0, exec_regs);
|
||||
if(err)
|
||||
panic("check_ptrace : PTRACE_GETREGS failed, errno = %d",
|
||||
errno);
|
||||
|
||||
errno = 0;
|
||||
err = ptrace(PTRACE_GETFPXREGS, pid, 0, exec_fpx_regs);
|
||||
if(!err)
|
||||
return;
|
||||
if(errno != EIO)
|
||||
panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
|
||||
errno);
|
||||
|
||||
have_fpx_regs = 0;
|
||||
|
||||
err = ptrace(PTRACE_GETFPREGS, pid, 0, exec_fp_regs);
|
||||
if(err)
|
||||
panic("check_ptrace : PTRACE_GETFPREGS failed, errno = %d",
|
||||
errno);
|
||||
}
|
||||
|
||||
void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
|
||||
{
|
||||
memcpy(regs, exec_regs, sizeof(exec_regs));
|
||||
if(fp_regs != NULL)
|
||||
memcpy(fp_regs, exec_fp_regs,
|
||||
HOST_FP_SIZE * sizeof(unsigned long));
|
||||
}
|
||||
|
||||
unsigned long get_thread_reg(int reg, jmp_buf *buf)
|
||||
{
|
||||
switch(reg){
|
||||
case EIP: return buf[0]->__eip;
|
||||
case UESP: return buf[0]->__esp;
|
||||
case EBP: return buf[0]->__ebp;
|
||||
default:
|
||||
printk("get_thread_regs - unknown register %d\n", reg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
15
arch/um/os-Linux/sys-i386/signal.c
Normal file
15
arch/um/os-Linux/sys-i386/signal.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
extern void (*handlers[])(int sig, struct sigcontext *sc);
|
||||
|
||||
void hard_handler(int sig)
|
||||
{
|
||||
struct sigcontext *sc = (struct sigcontext *) (&sig + 1);
|
||||
|
||||
(*handlers[sig])(sig, sc);
|
||||
}
|
||||
36
arch/um/os-Linux/sys-i386/tls.c
Normal file
36
arch/um/os-Linux/sys-i386/tls.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <errno.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sysdep/tls.h"
|
||||
#include "user_util.h"
|
||||
|
||||
/* Checks whether host supports TLS, and sets *tls_min according to the value
|
||||
* valid on the host.
|
||||
* i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */
|
||||
void check_host_supports_tls(int *supports_tls, int *tls_min) {
|
||||
/* Values for x86 and x86_64.*/
|
||||
int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(val); i++) {
|
||||
user_desc_t info;
|
||||
info.entry_number = val[i];
|
||||
|
||||
if (syscall(__NR_get_thread_area, &info) == 0) {
|
||||
*tls_min = val[i];
|
||||
*supports_tls = 1;
|
||||
return;
|
||||
} else {
|
||||
if (errno == EINVAL)
|
||||
continue;
|
||||
else if (errno == ENOSYS)
|
||||
*supports_tls = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*supports_tls = 0;
|
||||
}
|
||||
10
arch/um/os-Linux/sys-x86_64/Makefile
Normal file
10
arch/um/os-Linux/sys-x86_64/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MODE_SKAS) = registers.o prctl.o signal.o
|
||||
|
||||
USER_OBJS := $(obj-y)
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
||||
12
arch/um/os-Linux/sys-x86_64/prctl.c
Normal file
12
arch/um/os-Linux/sys-x86_64/prctl.c
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com})
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
int os_arch_prctl(int pid, int code, unsigned long *addr)
|
||||
{
|
||||
return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code);
|
||||
}
|
||||
91
arch/um/os-Linux/sys-x86_64/registers.c
Normal file
91
arch/um/os-Linux/sys-x86_64/registers.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2004 PathScale, Inc
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "ptrace_user.h"
|
||||
#include "uml-config.h"
|
||||
#include "skas_ptregs.h"
|
||||
#include "registers.h"
|
||||
#include "longjmp.h"
|
||||
#include "user.h"
|
||||
|
||||
/* These are set once at boot time and not changed thereafter */
|
||||
|
||||
static unsigned long exec_regs[MAX_REG_NR];
|
||||
static unsigned long exec_fp_regs[HOST_FP_SIZE];
|
||||
|
||||
void init_thread_registers(union uml_pt_regs *to)
|
||||
{
|
||||
memcpy(to->skas.regs, exec_regs, sizeof(to->skas.regs));
|
||||
memcpy(to->skas.fp, exec_fp_regs, sizeof(to->skas.fp));
|
||||
}
|
||||
|
||||
static int move_registers(int pid, int int_op, int fp_op,
|
||||
union uml_pt_regs *regs)
|
||||
{
|
||||
if(ptrace(int_op, pid, 0, regs->skas.regs) < 0)
|
||||
return -errno;
|
||||
|
||||
if(ptrace(fp_op, pid, 0, regs->skas.fp) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void save_registers(int pid, union uml_pt_regs *regs)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = move_registers(pid, PTRACE_GETREGS, PTRACE_GETFPREGS, regs);
|
||||
if(err)
|
||||
panic("save_registers - saving registers failed, errno = %d\n",
|
||||
-err);
|
||||
}
|
||||
|
||||
void restore_registers(int pid, union uml_pt_regs *regs)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = move_registers(pid, PTRACE_SETREGS, PTRACE_SETFPREGS, regs);
|
||||
if(err)
|
||||
panic("restore_registers - saving registers failed, "
|
||||
"errno = %d\n", -err);
|
||||
}
|
||||
|
||||
void init_registers(int pid)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ptrace(PTRACE_GETREGS, pid, 0, exec_regs);
|
||||
if(err)
|
||||
panic("check_ptrace : PTRACE_GETREGS failed, errno = %d",
|
||||
errno);
|
||||
|
||||
err = ptrace(PTRACE_GETFPREGS, pid, 0, exec_fp_regs);
|
||||
if(err)
|
||||
panic("check_ptrace : PTRACE_GETFPREGS failed, errno = %d",
|
||||
errno);
|
||||
}
|
||||
|
||||
void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
|
||||
{
|
||||
memcpy(regs, exec_regs, sizeof(exec_regs));
|
||||
if(fp_regs != NULL)
|
||||
memcpy(fp_regs, exec_fp_regs,
|
||||
HOST_FP_SIZE * sizeof(unsigned long));
|
||||
}
|
||||
|
||||
unsigned long get_thread_reg(int reg, jmp_buf *buf)
|
||||
{
|
||||
switch(reg){
|
||||
case RIP: return buf[0]->__rip;
|
||||
case RSP: return buf[0]->__rsp;
|
||||
case RBP: return buf[0]->__rbp;
|
||||
default:
|
||||
printk("get_thread_regs - unknown register %d\n", reg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
16
arch/um/os-Linux/sys-x86_64/signal.c
Normal file
16
arch/um/os-Linux/sys-x86_64/signal.c
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Jeff Dike (jdike@addtoit.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
extern void (*handlers[])(int sig, struct sigcontext *sc);
|
||||
|
||||
void hard_handler(int sig)
|
||||
{
|
||||
struct ucontext *uc;
|
||||
asm("movq %%rdx, %0" : "=r" (uc));
|
||||
|
||||
(*handlers[sig])(sig, (struct sigcontext *) &uc->uc_mcontext);
|
||||
}
|
||||
102
arch/um/os-Linux/time.c
Normal file
102
arch/um/os-Linux/time.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "process.h"
|
||||
#include "kern_constants.h"
|
||||
#include "os.h"
|
||||
#include "uml-config.h"
|
||||
|
||||
int set_interval(int is_virtual)
|
||||
{
|
||||
int usec = 1000000/hz();
|
||||
int timer_type = is_virtual ? ITIMER_VIRTUAL : ITIMER_REAL;
|
||||
struct itimerval interval = ((struct itimerval) { { 0, usec },
|
||||
{ 0, usec } });
|
||||
|
||||
if(setitimer(timer_type, &interval, NULL) == -1)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef UML_CONFIG_MODE_TT
|
||||
void enable_timer(void)
|
||||
{
|
||||
set_interval(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
void disable_timer(void)
|
||||
{
|
||||
struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
|
||||
if((setitimer(ITIMER_VIRTUAL, &disable, NULL) < 0) ||
|
||||
(setitimer(ITIMER_REAL, &disable, NULL) < 0))
|
||||
printk("disnable_timer - setitimer failed, errno = %d\n",
|
||||
errno);
|
||||
/* If there are signals already queued, after unblocking ignore them */
|
||||
signal(SIGALRM, SIG_IGN);
|
||||
signal(SIGVTALRM, SIG_IGN);
|
||||
}
|
||||
|
||||
void switch_timers(int to_real)
|
||||
{
|
||||
struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }});
|
||||
struct itimerval enable = ((struct itimerval) { { 0, 1000000/hz() },
|
||||
{ 0, 1000000/hz() }});
|
||||
int old, new;
|
||||
|
||||
if(to_real){
|
||||
old = ITIMER_VIRTUAL;
|
||||
new = ITIMER_REAL;
|
||||
}
|
||||
else {
|
||||
old = ITIMER_REAL;
|
||||
new = ITIMER_VIRTUAL;
|
||||
}
|
||||
|
||||
if((setitimer(old, &disable, NULL) < 0) ||
|
||||
(setitimer(new, &enable, NULL)))
|
||||
printk("switch_timers - setitimer failed, errno = %d\n",
|
||||
errno);
|
||||
}
|
||||
|
||||
#ifdef UML_CONFIG_MODE_TT
|
||||
void uml_idle_timer(void)
|
||||
{
|
||||
if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR)
|
||||
panic("Couldn't unset SIGVTALRM handler");
|
||||
|
||||
set_handler(SIGALRM, (__sighandler_t) alarm_handler,
|
||||
SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1);
|
||||
set_interval(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned long long os_nsecs(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
return((unsigned long long) tv.tv_sec * BILLION + tv.tv_usec * 1000);
|
||||
}
|
||||
|
||||
void idle_sleep(int secs)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = secs;
|
||||
ts.tv_nsec = 0;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
75
arch/um/os-Linux/tls.c
Normal file
75
arch/um/os-Linux/tls.c
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <asm/ldt.h>
|
||||
#include "sysdep/tls.h"
|
||||
#include "uml-config.h"
|
||||
|
||||
/* TLS support - we basically rely on the host's one.*/
|
||||
|
||||
/* In TT mode, this should be called only by the tracing thread, and makes sense
|
||||
* only for PTRACE_SET_THREAD_AREA. In SKAS mode, it's used normally.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PTRACE_GET_THREAD_AREA
|
||||
#define PTRACE_GET_THREAD_AREA 25
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_SET_THREAD_AREA
|
||||
#define PTRACE_SET_THREAD_AREA 26
|
||||
#endif
|
||||
|
||||
int os_set_thread_area(user_desc_t *info, int pid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
|
||||
(unsigned long) info);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef UML_CONFIG_MODE_SKAS
|
||||
|
||||
int os_get_thread_area(user_desc_t *info, int pid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number,
|
||||
(unsigned long) info);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef UML_CONFIG_MODE_TT
|
||||
#include "linux/unistd.h"
|
||||
|
||||
int do_set_thread_area_tt(user_desc_t *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = syscall(__NR_set_thread_area,info);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int do_get_thread_area_tt(user_desc_t *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = syscall(__NR_get_thread_area,info);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* UML_CONFIG_MODE_TT */
|
||||
41
arch/um/os-Linux/trap.c
Normal file
41
arch/um/os-Linux/trap.c
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include "kern_util.h"
|
||||
#include "user_util.h"
|
||||
#include "os.h"
|
||||
#include "mode.h"
|
||||
#include "longjmp.h"
|
||||
|
||||
void usr2_handler(int sig, union uml_pt_regs *regs)
|
||||
{
|
||||
CHOOSE_MODE(syscall_handler_tt(sig, regs), (void) 0);
|
||||
}
|
||||
|
||||
/* Initialized from linux_main() */
|
||||
void (*sig_info[NSIG])(int, union uml_pt_regs *);
|
||||
|
||||
void os_fill_handlinfo(struct kern_handlers h)
|
||||
{
|
||||
sig_info[SIGTRAP] = h.relay_signal;
|
||||
sig_info[SIGFPE] = h.relay_signal;
|
||||
sig_info[SIGILL] = h.relay_signal;
|
||||
sig_info[SIGWINCH] = h.winch;
|
||||
sig_info[SIGBUS] = h.bus_handler;
|
||||
sig_info[SIGSEGV] = h.page_fault;
|
||||
sig_info[SIGIO] = h.sigio_handler;
|
||||
sig_info[SIGVTALRM] = h.timer_handler;
|
||||
sig_info[SIGALRM] = h.timer_handler;
|
||||
sig_info[SIGUSR2] = usr2_handler;
|
||||
}
|
||||
|
||||
void do_longjmp(void *b, int val)
|
||||
{
|
||||
jmp_buf *buf = b;
|
||||
|
||||
UML_LONGJMP(buf, val);
|
||||
}
|
||||
196
arch/um/os-Linux/tt.c
Normal file
196
arch/um/os-Linux/tt.c
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/page.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "signal_kern.h"
|
||||
#include "sysdep/ptrace.h"
|
||||
#include "sysdep/sigcontext.h"
|
||||
#include "irq_user.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
#include "uml-config.h"
|
||||
#include "choose-mode.h"
|
||||
#include "mode.h"
|
||||
#include "tempfile.h"
|
||||
|
||||
int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x,
|
||||
int must_succeed)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = os_protect_memory((void *) addr, len, r, w, x);
|
||||
if(err < 0){
|
||||
if(must_succeed)
|
||||
panic("protect failed, err = %d", -err);
|
||||
else return(err);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
void kill_child_dead(int pid)
|
||||
{
|
||||
kill(pid, SIGKILL);
|
||||
kill(pid, SIGCONT);
|
||||
do {
|
||||
int n;
|
||||
CATCH_EINTR(n = waitpid(pid, NULL, 0));
|
||||
if (n > 0)
|
||||
kill(pid, SIGCONT);
|
||||
else
|
||||
break;
|
||||
} while(1);
|
||||
}
|
||||
|
||||
void stop(void)
|
||||
{
|
||||
while(1) sleep(1000000);
|
||||
}
|
||||
|
||||
int wait_for_stop(int pid, int sig, int cont_type, void *relay)
|
||||
{
|
||||
sigset_t *relay_signals = relay;
|
||||
int status, ret;
|
||||
|
||||
while(1){
|
||||
CATCH_EINTR(ret = waitpid(pid, &status, WUNTRACED));
|
||||
if((ret < 0) ||
|
||||
!WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){
|
||||
if(ret < 0){
|
||||
printk("wait failed, errno = %d\n",
|
||||
errno);
|
||||
}
|
||||
else if(WIFEXITED(status))
|
||||
printk("process %d exited with status %d\n",
|
||||
pid, WEXITSTATUS(status));
|
||||
else if(WIFSIGNALED(status))
|
||||
printk("process %d exited with signal %d\n",
|
||||
pid, WTERMSIG(status));
|
||||
else if((WSTOPSIG(status) == SIGVTALRM) ||
|
||||
(WSTOPSIG(status) == SIGALRM) ||
|
||||
(WSTOPSIG(status) == SIGIO) ||
|
||||
(WSTOPSIG(status) == SIGPROF) ||
|
||||
(WSTOPSIG(status) == SIGCHLD) ||
|
||||
(WSTOPSIG(status) == SIGWINCH) ||
|
||||
(WSTOPSIG(status) == SIGINT)){
|
||||
ptrace(cont_type, pid, 0, WSTOPSIG(status));
|
||||
continue;
|
||||
}
|
||||
else if((relay_signals != NULL) &&
|
||||
sigismember(relay_signals, WSTOPSIG(status))){
|
||||
ptrace(cont_type, pid, 0, WSTOPSIG(status));
|
||||
continue;
|
||||
}
|
||||
else printk("process %d stopped with signal %d\n",
|
||||
pid, WSTOPSIG(status));
|
||||
panic("wait_for_stop failed to wait for %d to stop "
|
||||
"with %d\n", pid, sig);
|
||||
}
|
||||
return(status);
|
||||
}
|
||||
}
|
||||
|
||||
void forward_ipi(int fd, int pid)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = os_set_owner(fd, pid);
|
||||
if(err < 0)
|
||||
printk("forward_ipi: set_owner failed, fd = %d, me = %d, "
|
||||
"target = %d, err = %d\n", fd, os_getpid(), pid, -err);
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------
|
||||
* only for tt mode (will be deleted in future...)
|
||||
*-------------------------
|
||||
*/
|
||||
|
||||
struct tramp {
|
||||
int (*tramp)(void *);
|
||||
void *tramp_data;
|
||||
unsigned long temp_stack;
|
||||
int flags;
|
||||
int pid;
|
||||
};
|
||||
|
||||
/* See above for why sigkill is here */
|
||||
|
||||
int sigkill = SIGKILL;
|
||||
|
||||
int outer_tramp(void *arg)
|
||||
{
|
||||
struct tramp *t;
|
||||
int sig = sigkill;
|
||||
|
||||
t = arg;
|
||||
t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2,
|
||||
t->flags, t->tramp_data);
|
||||
if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL);
|
||||
kill(os_getpid(), sig);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int start_fork_tramp(void *thread_arg, unsigned long temp_stack,
|
||||
int clone_flags, int (*tramp)(void *))
|
||||
{
|
||||
struct tramp arg;
|
||||
unsigned long sp;
|
||||
int new_pid, status, err;
|
||||
|
||||
/* The trampoline will run on the temporary stack */
|
||||
sp = stack_sp(temp_stack);
|
||||
|
||||
clone_flags |= CLONE_FILES | SIGCHLD;
|
||||
|
||||
arg.tramp = tramp;
|
||||
arg.tramp_data = thread_arg;
|
||||
arg.temp_stack = temp_stack;
|
||||
arg.flags = clone_flags;
|
||||
|
||||
/* Start the process and wait for it to kill itself */
|
||||
new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg);
|
||||
if(new_pid < 0)
|
||||
return(new_pid);
|
||||
|
||||
CATCH_EINTR(err = waitpid(new_pid, &status, 0));
|
||||
if(err < 0)
|
||||
panic("Waiting for outer trampoline failed - errno = %d",
|
||||
errno);
|
||||
|
||||
if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL))
|
||||
panic("outer trampoline didn't exit with SIGKILL, "
|
||||
"status = %d", status);
|
||||
|
||||
return(arg.pid);
|
||||
}
|
||||
|
||||
void forward_pending_sigio(int target)
|
||||
{
|
||||
sigset_t sigs;
|
||||
|
||||
if(sigpending(&sigs))
|
||||
panic("forward_pending_sigio : sigpending failed");
|
||||
if(sigismember(&sigs, SIGIO))
|
||||
kill(target, SIGIO);
|
||||
}
|
||||
|
||||
61
arch/um/os-Linux/tty.c
Normal file
61
arch/um/os-Linux/tty.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "os.h"
|
||||
#include "user.h"
|
||||
#include "kern_util.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;
|
||||
|
||||
fd = os_open_file("/dev/ptmx", of_rdwr(OPENFLAGS()), 0);
|
||||
if(fd < 0){
|
||||
printk("get_pty : Couldn't open /dev/ptmx - err = %d\n", -fd);
|
||||
return(fd);
|
||||
}
|
||||
|
||||
info.fd = fd;
|
||||
initial_thread_cb(grantpt_cb, &info);
|
||||
|
||||
if(info.res < 0){
|
||||
printk("get_pty : Couldn't grant pty - errno = %d\n",
|
||||
-info.err);
|
||||
return(-1);
|
||||
}
|
||||
if(unlockpt(fd) < 0){
|
||||
printk("get_pty : Couldn't unlock pty - errno = %d\n", errno);
|
||||
return(-1);
|
||||
}
|
||||
return(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
218
arch/um/os-Linux/tty_log.c
Normal file
218
arch/um/os-Linux/tty_log.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and
|
||||
* geoffrey hing <ghing@net.ohio-state.edu>
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include "init.h"
|
||||
#include "user.h"
|
||||
#include "kern_util.h"
|
||||
#include "os.h"
|
||||
|
||||
#define TTY_LOG_DIR "./"
|
||||
|
||||
/* Set early in boot and then unchanged */
|
||||
static char *tty_log_dir = TTY_LOG_DIR;
|
||||
static int tty_log_fd = -1;
|
||||
|
||||
#define TTY_LOG_OPEN 1
|
||||
#define TTY_LOG_CLOSE 2
|
||||
#define TTY_LOG_WRITE 3
|
||||
#define TTY_LOG_EXEC 4
|
||||
|
||||
#define TTY_READ 1
|
||||
#define TTY_WRITE 2
|
||||
|
||||
struct tty_log_buf {
|
||||
int what;
|
||||
unsigned long tty;
|
||||
int len;
|
||||
int direction;
|
||||
unsigned long sec;
|
||||
unsigned long usec;
|
||||
};
|
||||
|
||||
int open_tty_log(void *tty, void *current_tty)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tty_log_buf data;
|
||||
char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")];
|
||||
int fd;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
if(tty_log_fd != -1){
|
||||
data = ((struct tty_log_buf) { .what = TTY_LOG_OPEN,
|
||||
.tty = (unsigned long) tty,
|
||||
.len = sizeof(current_tty),
|
||||
.direction = 0,
|
||||
.sec = tv.tv_sec,
|
||||
.usec = tv.tv_usec } );
|
||||
os_write_file(tty_log_fd, &data, sizeof(data));
|
||||
os_write_file(tty_log_fd, ¤t_tty, data.len);
|
||||
return(tty_log_fd);
|
||||
}
|
||||
|
||||
sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec,
|
||||
(unsigned int) tv.tv_usec);
|
||||
|
||||
fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))),
|
||||
0644);
|
||||
if(fd < 0){
|
||||
printk("open_tty_log : couldn't open '%s', errno = %d\n",
|
||||
buf, -fd);
|
||||
}
|
||||
return(fd);
|
||||
}
|
||||
|
||||
void close_tty_log(int fd, void *tty)
|
||||
{
|
||||
struct tty_log_buf data;
|
||||
struct timeval tv;
|
||||
|
||||
if(tty_log_fd != -1){
|
||||
gettimeofday(&tv, NULL);
|
||||
data = ((struct tty_log_buf) { .what = TTY_LOG_CLOSE,
|
||||
.tty = (unsigned long) tty,
|
||||
.len = 0,
|
||||
.direction = 0,
|
||||
.sec = tv.tv_sec,
|
||||
.usec = tv.tv_usec } );
|
||||
os_write_file(tty_log_fd, &data, sizeof(data));
|
||||
return;
|
||||
}
|
||||
os_close_file(fd);
|
||||
}
|
||||
|
||||
static int log_chunk(int fd, const char *buf, int len)
|
||||
{
|
||||
int total = 0, try, missed, n;
|
||||
char chunk[64];
|
||||
|
||||
while(len > 0){
|
||||
try = (len > sizeof(chunk)) ? sizeof(chunk) : len;
|
||||
missed = copy_from_user_proc(chunk, (char *) buf, try);
|
||||
try -= missed;
|
||||
n = os_write_file(fd, chunk, try);
|
||||
if(n != try) {
|
||||
if(n < 0)
|
||||
return(n);
|
||||
return(-EIO);
|
||||
}
|
||||
if(missed != 0)
|
||||
return(-EFAULT);
|
||||
|
||||
len -= try;
|
||||
total += try;
|
||||
buf += try;
|
||||
}
|
||||
|
||||
return(total);
|
||||
}
|
||||
|
||||
int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tty_log_buf data;
|
||||
int direction;
|
||||
|
||||
if(fd == tty_log_fd){
|
||||
gettimeofday(&tv, NULL);
|
||||
direction = is_read ? TTY_READ : TTY_WRITE;
|
||||
data = ((struct tty_log_buf) { .what = TTY_LOG_WRITE,
|
||||
.tty = (unsigned long) tty,
|
||||
.len = len,
|
||||
.direction = direction,
|
||||
.sec = tv.tv_sec,
|
||||
.usec = tv.tv_usec } );
|
||||
os_write_file(tty_log_fd, &data, sizeof(data));
|
||||
}
|
||||
|
||||
return(log_chunk(fd, buf, len));
|
||||
}
|
||||
|
||||
void log_exec(char **argv, void *tty)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tty_log_buf data;
|
||||
char **ptr,*arg;
|
||||
int len;
|
||||
|
||||
if(tty_log_fd == -1) return;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
len = 0;
|
||||
for(ptr = argv; ; ptr++){
|
||||
if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
|
||||
return;
|
||||
if(arg == NULL) break;
|
||||
len += strlen_user_proc(arg);
|
||||
}
|
||||
|
||||
data = ((struct tty_log_buf) { .what = TTY_LOG_EXEC,
|
||||
.tty = (unsigned long) tty,
|
||||
.len = len,
|
||||
.direction = 0,
|
||||
.sec = tv.tv_sec,
|
||||
.usec = tv.tv_usec } );
|
||||
os_write_file(tty_log_fd, &data, sizeof(data));
|
||||
|
||||
for(ptr = argv; ; ptr++){
|
||||
if(copy_from_user_proc(&arg, ptr, sizeof(arg)))
|
||||
return;
|
||||
if(arg == NULL) break;
|
||||
log_chunk(tty_log_fd, arg, strlen_user_proc(arg));
|
||||
}
|
||||
}
|
||||
|
||||
extern void register_tty_logger(int (*opener)(void *, void *),
|
||||
int (*writer)(int, const char *, int,
|
||||
void *, int),
|
||||
void (*closer)(int, void *));
|
||||
|
||||
static int register_logger(void)
|
||||
{
|
||||
register_tty_logger(open_tty_log, write_tty_log, close_tty_log);
|
||||
return(0);
|
||||
}
|
||||
|
||||
__uml_initcall(register_logger);
|
||||
|
||||
static int __init set_tty_log_dir(char *name, int *add)
|
||||
{
|
||||
tty_log_dir = name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("tty_log_dir=", set_tty_log_dir,
|
||||
"tty_log_dir=<directory>\n"
|
||||
" This is used to specify the directory where the logs of all pty\n"
|
||||
" data from this UML machine will be written.\n\n"
|
||||
);
|
||||
|
||||
static int __init set_tty_log_fd(char *name, int *add)
|
||||
{
|
||||
char *end;
|
||||
|
||||
tty_log_fd = strtoul(name, &end, 0);
|
||||
if((*end != '\0') || (end == name)){
|
||||
printf("set_tty_log_fd - strtoul failed on '%s'\n", name);
|
||||
tty_log_fd = -1;
|
||||
}
|
||||
|
||||
*add = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("tty_log_fd=", set_tty_log_fd,
|
||||
"tty_log_fd=<fd>\n"
|
||||
" This is used to specify a preconfigured file descriptor to which all\n"
|
||||
" tty data will be written. Preconfigure the descriptor with something\n"
|
||||
" like '10>tty_log tty_log_fd=10'.\n\n"
|
||||
);
|
||||
32
arch/um/os-Linux/uaccess.c
Normal file
32
arch/um/os-Linux/uaccess.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk)
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "longjmp.h"
|
||||
|
||||
unsigned long __do_user_copy(void *to, const void *from, int n,
|
||||
void **fault_addr, void **fault_catcher,
|
||||
void (*op)(void *to, const void *from,
|
||||
int n), int *faulted_out)
|
||||
{
|
||||
unsigned long *faddrp = (unsigned long *) fault_addr, ret;
|
||||
|
||||
jmp_buf jbuf;
|
||||
*fault_catcher = &jbuf;
|
||||
if(UML_SETJMP(&jbuf) == 0){
|
||||
(*op)(to, from, n);
|
||||
ret = 0;
|
||||
*faulted_out = 0;
|
||||
}
|
||||
else {
|
||||
ret = *faddrp;
|
||||
*faulted_out = 1;
|
||||
}
|
||||
*fault_addr = NULL;
|
||||
*fault_catcher = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
385
arch/um/os-Linux/umid.c
Normal file
385
arch/um/os-Linux/umid.c
Normal file
@@ -0,0 +1,385 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include "init.h"
|
||||
#include "os.h"
|
||||
#include "user.h"
|
||||
#include "mode.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("make_uml_dir : no value in environment for "
|
||||
"$HOME\n");
|
||||
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) {
|
||||
printf("make_uml_dir : malloc failed, errno = %d\n", errno);
|
||||
goto err;
|
||||
}
|
||||
strcpy(uml_dir, dir);
|
||||
|
||||
if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){
|
||||
printf("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("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("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("is_umdir_used : couldn't read pid file '%s', "
|
||||
"err = %d\n", file, errno);
|
||||
goto out_close;
|
||||
} else if(n == 0){
|
||||
printk("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("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("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("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("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("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;
|
||||
|
||||
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("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("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("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("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'){
|
||||
printf("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){
|
||||
printf("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)
|
||||
printf("remove_umid_dir - remove_files_and_dir failed with "
|
||||
"err = %d\n", err);
|
||||
}
|
||||
|
||||
__uml_exitcall(remove_umid_dir);
|
||||
115
arch/um/os-Linux/user_syms.c
Normal file
115
arch/um/os-Linux/user_syms.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#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 *memcpy(void *, const void *, size_t);
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
extern void *memset(void *, int, size_t);
|
||||
extern int printf(const char *, ...);
|
||||
|
||||
/* If they're not defined, the export is included in lib/string.c.*/
|
||||
#ifdef __HAVE_ARCH_STRLEN
|
||||
EXPORT_SYMBOL(strlen);
|
||||
#endif
|
||||
#ifdef __HAVE_ARCH_STRSTR
|
||||
EXPORT_SYMBOL(strstr);
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
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 SUBARCH_i386
|
||||
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(lseek);
|
||||
EXPORT_SYMBOL_PROTO(lseek64);
|
||||
EXPORT_SYMBOL_PROTO(chown);
|
||||
EXPORT_SYMBOL_PROTO(truncate);
|
||||
EXPORT_SYMBOL_PROTO(utime);
|
||||
EXPORT_SYMBOL_PROTO(chmod);
|
||||
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 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);
|
||||
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
123
arch/um/os-Linux/util.c
Normal file
123
arch/um/os-Linux/util.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include "asm/types.h"
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <wait.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <sched.h>
|
||||
#include <termios.h>
|
||||
#include <string.h>
|
||||
#include "user_util.h"
|
||||
#include "kern_util.h"
|
||||
#include "user.h"
|
||||
#include "mem_user.h"
|
||||
#include "init.h"
|
||||
#include "ptrace_user.h"
|
||||
#include "uml-config.h"
|
||||
#include "os.h"
|
||||
#include "longjmp.h"
|
||||
|
||||
void stack_protections(unsigned long address)
|
||||
{
|
||||
int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
|
||||
if(mprotect((void *) address, page_size(), prot) < 0)
|
||||
panic("protecting stack failed, errno = %d", errno);
|
||||
}
|
||||
|
||||
void task_protections(unsigned long address)
|
||||
{
|
||||
unsigned long guard = address + page_size();
|
||||
unsigned long stack = guard + page_size();
|
||||
int prot = 0, pages;
|
||||
|
||||
#ifdef notdef
|
||||
if(mprotect((void *) stack, page_size(), prot) < 0)
|
||||
panic("protecting guard page failed, errno = %d", errno);
|
||||
#endif
|
||||
pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER) - 2;
|
||||
prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
if(mprotect((void *) stack, pages * page_size(), prot) < 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);
|
||||
}
|
||||
|
||||
char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1];
|
||||
|
||||
void setup_hostinfo(void)
|
||||
{
|
||||
struct utsname host;
|
||||
|
||||
uname(&host);
|
||||
sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename,
|
||||
host.release, host.version, host.machine);
|
||||
}
|
||||
|
||||
int setjmp_wrapper(void (*proc)(void *, void *), ...)
|
||||
{
|
||||
va_list args;
|
||||
jmp_buf buf;
|
||||
int n;
|
||||
|
||||
n = UML_SETJMP(&buf);
|
||||
if(n == 0){
|
||||
va_start(args, proc);
|
||||
(*proc)(&buf, &args);
|
||||
}
|
||||
va_end(args);
|
||||
return n;
|
||||
}
|
||||
Reference in New Issue
Block a user