diff options
Diffstat (limited to 'arch/um')
96 files changed, 3024 insertions, 2183 deletions
diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 1eb21de9d1b..5982fe2753e 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -22,10 +22,6 @@ config SBUS config PCI bool -config UID16 - bool - default y - config GENERIC_CALIBRATE_DELAY bool default y @@ -39,12 +35,12 @@ menu "UML-specific options" config MODE_TT bool "Tracing thread support" - default y + default n help This option controls whether tracing thread support is compiled - into UML. Normally, this should be set to Y. If you intend to - use only skas mode (and the host has the skas patch applied to it), - then it is OK to say N here. + into UML. This option is largely obsolete, given that skas0 provides + skas security and performance without needing to patch the host. + It is safe to say 'N' here. config STATIC_LINK bool "Force a static link" @@ -83,7 +79,7 @@ config KERNEL_HALF_GIGS of physical memory. config MODE_SKAS - bool "Separate Kernel Address Space support" + bool "Separate Kernel Address Space support" if MODE_TT default y help This option controls whether skas (separate kernel address space) @@ -147,6 +143,7 @@ config HOSTFS config HPPFS tristate "HoneyPot ProcFS (EXPERIMENTAL)" + depends on EXPERIMENTAL help hppfs (HoneyPot ProcFS) is a filesystem which allows UML /proc entries to be overridden, removed, or fabricated from the host. @@ -159,10 +156,6 @@ config HPPFS You only need this if you are setting up a UML honeypot. Otherwise, it is safe to say 'N' here. - If you are actively using it, please report any problems, since it's - getting fixed. In this moment, it is experimental on 2.6 (it works on - 2.4). - config MCONSOLE bool "Management console" default y @@ -247,8 +240,16 @@ config NEST_LEVEL Only change this if you are running nested UMLs. config HIGHMEM - bool "Highmem support" - depends on !64BIT + bool "Highmem support (EXPERIMENTAL)" + depends on !64BIT && EXPERIMENTAL + default n + help + This was used to allow UML to run with big amounts of memory. + Currently it is unstable, so if unsure say N. + + To use big amounts of memory, it is recommended to disable TT mode (i.e. + CONFIG_MODE_TT) and enable static linking (i.e. CONFIG_STATIC_LINK) - + this should allow the guest to use up to 2.75G of memory. config KERNEL_STACK_ORDER int "Kernel stack size order" @@ -273,17 +274,13 @@ endmenu source "init/Kconfig" -source "net/Kconfig" - -source "drivers/base/Kconfig" +source "drivers/block/Kconfig" source "arch/um/Kconfig.char" -source "drivers/block/Kconfig" +source "drivers/base/Kconfig" -config NETDEVICES - bool - default NET +source "net/Kconfig" source "arch/um/Kconfig.net" diff --git a/arch/um/Kconfig.i386 b/arch/um/Kconfig.i386 index c71b39a677a..ef79ed25aec 100644 --- a/arch/um/Kconfig.i386 +++ b/arch/um/Kconfig.i386 @@ -22,13 +22,17 @@ config TOP_ADDR default 0x80000000 if HOST_2G_2G config 3_LEVEL_PGTABLES - bool "Three-level pagetables" + bool "Three-level pagetables (EXPERIMENTAL)" default n + depends on EXPERIMENTAL help Three-level pagetables will let UML have more than 4G of physical memory. All the memory that can't be mapped directly will be treated as high memory. + However, this it experimental on 32-bit architectures, so if unsure say + N (on x86-64 it's automatically enabled, instead, as it's safe there). + config STUB_CODE hex default 0xbfffe000 diff --git a/arch/um/Makefile b/arch/um/Makefile index 1b12feeba36..6430a638385 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -32,7 +32,7 @@ um-modes-$(CONFIG_MODE_TT) += tt um-modes-$(CONFIG_MODE_SKAS) += skas MODE_INCLUDE += $(foreach mode,$(um-modes-y),\ - -I$(srctree)/$(ARCH_DIR)/kernel/$(mode)/include) + -I$(srctree)/$(ARCH_DIR)/include/$(mode)) MAKEFILES-INCL += $(foreach mode,$(um-modes-y),\ $(srctree)/$(ARCH_DIR)/Makefile-$(mode)) @@ -67,7 +67,8 @@ USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) $(ARCH_INCLUDE) \ # in CFLAGS. Otherwise, it would cause ld to complain about the two different # errnos. -CFLAGS += -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask +CFLAGS += -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask \ + -Dmktime=kernel_mktime CFLAGS += $(call cc-option,-fno-unit-at-a-time,) include $(srctree)/$(ARCH_DIR)/Makefile-$(SUBARCH) @@ -189,6 +190,12 @@ define filechk_umlconfig sed 's/ CONFIG/ UML_CONFIG/' endef +$(ARCH_DIR)/include/uml-config.h : include/linux/autoconf.h + $(call filechk,umlconfig) + +$(ARCH_DIR)/user-offsets.s: $(ARCH_DIR)/sys-$(SUBARCH)/user-offsets.c + $(CC) $(USER_CFLAGS) -S -o $@ $< + define filechk_gen-asm-offsets (set -e; \ echo "/*"; \ @@ -202,24 +209,13 @@ define filechk_gen-asm-offsets echo ""; ) endef -$(ARCH_DIR)/include/uml-config.h : include/linux/autoconf.h - $(call filechk,umlconfig) - -$(ARCH_DIR)/user-offsets.s: $(ARCH_DIR)/sys-$(SUBARCH)/user-offsets.c - $(CC) $(USER_CFLAGS) -S -o $@ $< - $(ARCH_DIR)/include/user_constants.h: $(ARCH_DIR)/user-offsets.s $(call filechk,gen-asm-offsets) CLEAN_FILES += $(ARCH_DIR)/user-offsets.s -$(ARCH_DIR)/kernel-offsets.s: $(ARCH_DIR)/sys-$(SUBARCH)/kernel-offsets.c \ - archprepare - $(CC) $(CFLAGS) $(NOSTDINC_FLAGS) $(CPPFLAGS) -S -o $@ $< - -$(ARCH_DIR)/include/kern_constants.h: $(ARCH_DIR)/kernel-offsets.s - $(call filechk,gen-asm-offsets) - -CLEAN_FILES += $(ARCH_DIR)/kernel-offsets.s +$(ARCH_DIR)/include/kern_constants.h: + @echo ' SYMLINK $@' + $(Q) ln -sf ../../../include/asm-um/asm-offsets.h $@ export SUBARCH USER_CFLAGS OS diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 5b58fad4529..ab0d0b17081 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -58,7 +58,7 @@ static void *not_configged_init(char *str, int device, struct chan_opts *opts) { my_puts("Using a channel type which is configured out of " "UML\n"); - return(NULL); + return NULL; } static int not_configged_open(int input, int output, int primary, void *data, @@ -66,7 +66,7 @@ static int not_configged_open(int input, int output, int primary, void *data, { my_puts("Using a channel type which is configured out of " "UML\n"); - return(-ENODEV); + return -ENODEV; } static void not_configged_close(int fd, void *data) @@ -79,21 +79,21 @@ static int not_configged_read(int fd, char *c_out, void *data) { my_puts("Using a channel type which is configured out of " "UML\n"); - return(-EIO); + return -EIO; } static int not_configged_write(int fd, const char *buf, int len, void *data) { my_puts("Using a channel type which is configured out of " "UML\n"); - return(-EIO); + return -EIO; } static int not_configged_console_write(int fd, const char *buf, int len) { my_puts("Using a channel type which is configured out of " "UML\n"); - return(-EIO); + return -EIO; } static int not_configged_window_size(int fd, void *data, unsigned short *rows, @@ -101,7 +101,7 @@ static int not_configged_window_size(int fd, void *data, unsigned short *rows, { my_puts("Using a channel type which is configured out of " "UML\n"); - return(-ENODEV); + return -ENODEV; } static void not_configged_free(void *data) @@ -135,17 +135,17 @@ int generic_read(int fd, char *c_out, void *unused) n = os_read_file(fd, c_out, sizeof(*c_out)); if(n == -EAGAIN) - return(0); + return 0; else if(n == 0) - return(-EIO); - return(n); + return -EIO; + return n; } /* XXX Trivial wrapper around os_write_file */ int generic_write(int fd, const char *buf, int n, void *unused) { - return(os_write_file(fd, buf, n)); + return os_write_file(fd, buf, n); } int generic_window_size(int fd, void *unused, unsigned short *rows_out, @@ -156,14 +156,14 @@ int generic_window_size(int fd, void *unused, unsigned short *rows_out, ret = os_window_size(fd, &rows, &cols); if(ret < 0) - return(ret); + return ret; ret = ((*rows_out != rows) || (*cols_out != cols)); *rows_out = rows; *cols_out = cols; - return(ret); + return ret; } void generic_free(void *data) @@ -186,25 +186,26 @@ static void tty_receive_char(struct tty_struct *tty, char ch) } } - if((tty->flip.flag_buf_ptr == NULL) || - (tty->flip.char_buf_ptr == NULL)) - return; tty_insert_flip_char(tty, ch, TTY_NORMAL); } -static int open_one_chan(struct chan *chan, int input, int output, int primary) +static int open_one_chan(struct chan *chan) { int fd; - if(chan->opened) return(0); - if(chan->ops->open == NULL) fd = 0; - else fd = (*chan->ops->open)(input, output, primary, chan->data, - &chan->dev); - if(fd < 0) return(fd); + if(chan->opened) + return 0; + + if(chan->ops->open == NULL) + fd = 0; + else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary, + chan->data, &chan->dev); + if(fd < 0) + return fd; chan->fd = fd; chan->opened = 1; - return(0); + return 0; } int open_chan(struct list_head *chans) @@ -215,11 +216,11 @@ int open_chan(struct list_head *chans) list_for_each(ele, chans){ chan = list_entry(ele, struct chan, list); - ret = open_one_chan(chan, chan->input, chan->output, - chan->primary); - if(chan->primary) err = ret; + ret = open_one_chan(chan); + if(chan->primary) + err = ret; } - return(err); + return err; } void chan_enable_winch(struct list_head *chans, struct tty_struct *tty) @@ -236,20 +237,65 @@ void chan_enable_winch(struct list_head *chans, struct tty_struct *tty) } } -void enable_chan(struct list_head *chans, struct tty_struct *tty) +void enable_chan(struct line *line) { struct list_head *ele; struct chan *chan; - list_for_each(ele, chans){ + list_for_each(ele, &line->chan_list){ chan = list_entry(ele, struct chan, list); - if(!chan->opened) continue; + if(open_one_chan(chan)) + continue; - line_setup_irq(chan->fd, chan->input, chan->output, tty); + if(chan->enabled) + continue; + line_setup_irq(chan->fd, chan->input, chan->output, line, + chan); + chan->enabled = 1; } } -void close_chan(struct list_head *chans) +static LIST_HEAD(irqs_to_free); + +void free_irqs(void) +{ + struct chan *chan; + + while(!list_empty(&irqs_to_free)){ + chan = list_entry(irqs_to_free.next, struct chan, free_list); + list_del(&chan->free_list); + + if(chan->input) + free_irq(chan->line->driver->read_irq, chan); + if(chan->output) + free_irq(chan->line->driver->write_irq, chan); + chan->enabled = 0; + } +} + +static void close_one_chan(struct chan *chan, int delay_free_irq) +{ + if(!chan->opened) + return; + + if(delay_free_irq){ + list_add(&chan->free_list, &irqs_to_free); + } + else { + if(chan->input) + free_irq(chan->line->driver->read_irq, chan); + if(chan->output) + free_irq(chan->line->driver->write_irq, chan); + chan->enabled = 0; + } + if(chan->ops->close != NULL) + (*chan->ops->close)(chan->fd, chan->data); + + chan->opened = 0; + chan->fd = -1; +} + +void close_chan(struct list_head *chans, int delay_free_irq) { struct chan *chan; @@ -259,15 +305,37 @@ void close_chan(struct list_head *chans) * so it must be the last closed. */ list_for_each_entry_reverse(chan, chans, list) { - if(!chan->opened) continue; - if(chan->ops->close != NULL) - (*chan->ops->close)(chan->fd, chan->data); - chan->opened = 0; - chan->fd = -1; + close_one_chan(chan, delay_free_irq); } } -int write_chan(struct list_head *chans, const char *buf, int len, +void deactivate_chan(struct list_head *chans, int irq) +{ + struct list_head *ele; + + struct chan *chan; + list_for_each(ele, chans) { + chan = list_entry(ele, struct chan, list); + + if(chan->enabled && chan->input) + deactivate_fd(chan->fd, irq); + } +} + +void reactivate_chan(struct list_head *chans, int irq) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans) { + chan = list_entry(ele, struct chan, list); + + if(chan->enabled && chan->input) + reactivate_fd(chan->fd, irq); + } +} + +int write_chan(struct list_head *chans, const char *buf, int len, int write_irq) { struct list_head *ele; @@ -285,7 +353,7 @@ int write_chan(struct list_head *chans, const char *buf, int len, reactivate_fd(chan->fd, write_irq); } } - return(ret); + return ret; } int console_write_chan(struct list_head *chans, const char *buf, int len) @@ -301,19 +369,18 @@ int console_write_chan(struct list_head *chans, const char *buf, int len) n = chan->ops->console_write(chan->fd, buf, len); if(chan->primary) ret = n; } - return(ret); + return ret; } -int console_open_chan(struct line *line, struct console *co, struct chan_opts *opts) +int console_open_chan(struct line *line, struct console *co, + struct chan_opts *opts) { - if (!list_empty(&line->chan_list)) - return 0; + int err; + + err = open_chan(&line->chan_list); + if(err) + return err; - if (0 != parse_chan_pair(line->init_str, &line->chan_list, - line->init_pri, co->index, opts)) - return -1; - if (0 != open_chan(&line->chan_list)) - return -1; printk("Console initialized on /dev/%s%d\n",co->name,co->index); return 0; } @@ -327,32 +394,36 @@ int chan_window_size(struct list_head *chans, unsigned short *rows_out, list_for_each(ele, chans){ chan = list_entry(ele, struct chan, list); if(chan->primary){ - if(chan->ops->window_size == NULL) return(0); - return(chan->ops->window_size(chan->fd, chan->data, - rows_out, cols_out)); + if(chan->ops->window_size == NULL) + return 0; + return chan->ops->window_size(chan->fd, chan->data, + rows_out, cols_out); } } - return(0); + return 0; } -void free_one_chan(struct chan *chan) +void free_one_chan(struct chan *chan, int delay_free_irq) { list_del(&chan->list); + + close_one_chan(chan, delay_free_irq); + if(chan->ops->free != NULL) (*chan->ops->free)(chan->data); - free_irq_by_fd(chan->fd); + if(chan->primary && chan->output) ignore_sigio_fd(chan->fd); kfree(chan); } -void free_chan(struct list_head *chans) +void free_chan(struct list_head *chans, int delay_free_irq) { struct list_head *ele, *next; struct chan *chan; list_for_each_safe(ele, next, chans){ chan = list_entry(ele, struct chan, list); - free_one_chan(chan); + free_one_chan(chan, delay_free_irq); } } @@ -363,23 +434,23 @@ static int one_chan_config_string(struct chan *chan, char *str, int size, if(chan == NULL){ CONFIG_CHUNK(str, size, n, "none", 1); - return(n); + return n; } CONFIG_CHUNK(str, size, n, chan->ops->type, 0); if(chan->dev == NULL){ CONFIG_CHUNK(str, size, n, "", 1); - return(n); + return n; } CONFIG_CHUNK(str, size, n, ":", 0); CONFIG_CHUNK(str, size, n, chan->dev, 0); - return(n); + return n; } -static int chan_pair_config_string(struct chan *in, struct chan *out, +static int chan_pair_config_string(struct chan *in, struct chan *out, char *str, int size, char **error_out) { int n; @@ -390,7 +461,7 @@ static int chan_pair_config_string(struct chan *in, struct chan *out, if(in == out){ CONFIG_CHUNK(str, size, n, "", 1); - return(n); + return n; } CONFIG_CHUNK(str, size, n, ",", 1); @@ -399,10 +470,10 @@ static int chan_pair_config_string(struct chan *in, struct chan *out, size -= n; CONFIG_CHUNK(str, size, n, "", 1); - return(n); + return n; } -int chan_config_string(struct list_head *chans, char *str, int size, +int chan_config_string(struct list_head *chans, char *str, int size, char **error_out) { struct list_head *ele; @@ -418,7 +489,7 @@ int chan_config_string(struct list_head *chans, char *str, int size, out = chan; } - return(chan_pair_config_string(in, out, str, size, error_out)); + return chan_pair_config_string(in, out, str, size, error_out); } struct chan_type { @@ -462,7 +533,7 @@ struct chan_type chan_table[] = { #endif }; -static struct chan *parse_chan(char *str, int pri, int device, +static struct chan *parse_chan(struct line *line, char *str, int device, struct chan_opts *opts) { struct chan_type *entry; @@ -484,36 +555,42 @@ static struct chan *parse_chan(char *str, int pri, int device, if(ops == NULL){ my_printf("parse_chan couldn't parse \"%s\"\n", str); - return(NULL); + return NULL; } - if(ops->init == NULL) return(NULL); + if(ops->init == NULL) + return NULL; data = (*ops->init)(str, device, opts); - if(data == NULL) return(NULL); + if(data == NULL) + return NULL; chan = kmalloc(sizeof(*chan), GFP_ATOMIC); - if(chan == NULL) return(NULL); + if(chan == NULL) + return NULL; *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), + .free_list = + LIST_HEAD_INIT(chan->free_list), + .line = line, .primary = 1, .input = 0, .output = 0, .opened = 0, + .enabled = 0, .fd = -1, - .pri = pri, .ops = ops, .data = data }); - return(chan); + return chan; } -int parse_chan_pair(char *str, struct list_head *chans, int pri, int device, +int parse_chan_pair(char *str, struct line *line, int device, struct chan_opts *opts) { + struct list_head *chans = &line->chan_list; struct chan *new, *chan; char *in, *out; if(!list_empty(chans)){ chan = list_entry(chans->next, struct chan, list); - if(chan->pri >= pri) return(0); - free_chan(chans); + free_chan(chans, 0); INIT_LIST_HEAD(chans); } @@ -522,24 +599,30 @@ int parse_chan_pair(char *str, struct list_head *chans, int pri, int device, in = str; *out = '\0'; out++; - new = parse_chan(in, pri, device, opts); - if(new == NULL) return(-1); + new = parse_chan(line, in, device, opts); + if(new == NULL) + return -1; + new->input = 1; list_add(&new->list, chans); - new = parse_chan(out, pri, device, opts); - if(new == NULL) return(-1); + new = parse_chan(line, out, device, opts); + if(new == NULL) + return -1; + list_add(&new->list, chans); new->output = 1; } else { - new = parse_chan(str, pri, device, opts); - if(new == NULL) return(-1); + new = parse_chan(line, str, device, opts); + if(new == NULL) + return -1; + list_add(&new->list, chans); new->input = 1; new->output = 1; } - return(0); + return 0; } int chan_out_fd(struct list_head *chans) @@ -550,9 +633,9 @@ int chan_out_fd(struct list_head *chans) list_for_each(ele, chans){ chan = list_entry(ele, struct chan, list); if(chan->primary && chan->output) - return(chan->fd); + return chan->fd; } - return(-1); + return -1; } void chan_interrupt(struct list_head *chans, struct work_struct *task, @@ -567,9 +650,8 @@ void chan_interrupt(struct list_head *chans, struct work_struct *task, chan = list_entry(ele, struct chan, list); if(!chan->input || (chan->ops->read == NULL)) continue; do { - if((tty != NULL) && - (tty->flip.count >= TTY_FLIPBUF_SIZE)){ - schedule_work(task); + if (tty && !tty_buffer_request_room(tty, 1)) { + schedule_delayed_work(task, 1); goto out; } err = chan->ops->read(chan->fd, &c, chan->data); @@ -582,29 +664,12 @@ void chan_interrupt(struct list_head *chans, struct work_struct *task, if(chan->primary){ if(tty != NULL) tty_hangup(tty); - line_disable(tty, irq); - close_chan(chans); - free_chan(chans); + close_chan(chans, 1); return; } - else { - if(chan->ops->close != NULL) - chan->ops->close(chan->fd, chan->data); - free_one_chan(chan); - } + else close_one_chan(chan, 1); } } out: if(tty) tty_flip_buffer_push(tty); } - -/* - * 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: - */ diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c index 30d285b266a..507e3cbac9d 100644 --- a/arch/um/drivers/daemon_kern.c +++ b/arch/um/drivers/daemon_kern.c @@ -31,6 +31,10 @@ void daemon_init(struct net_device *dev, void *data) dpri->fd = -1; dpri->control = -1; dpri->dev = dev; + /* We will free this pointer. If it contains crap we're burned. */ + dpri->ctl_addr = NULL; + dpri->data_addr = NULL; + dpri->local_addr = NULL; printk("daemon backend (uml_switch version %d) - %s:%s", SWITCH_VERSION, dpri->sock_type, dpri->ctl_sock); diff --git a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c index 1bb085b2824..c944265955e 100644 --- a/arch/um/drivers/daemon_user.c +++ b/arch/um/drivers/daemon_user.c @@ -158,10 +158,16 @@ static void daemon_remove(void *data) struct daemon_data *pri = data; os_close_file(pri->fd); + pri->fd = -1; os_close_file(pri->control); + pri->control = -1; + kfree(pri->data_addr); + pri->data_addr = NULL; kfree(pri->ctl_addr); + pri->ctl_addr = NULL; kfree(pri->local_addr); + pri->local_addr = NULL; } int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri) diff --git a/arch/um/drivers/fd.c b/arch/um/drivers/fd.c index 3296e86a03a..c41f75e4acb 100644 --- a/arch/um/drivers/fd.c +++ b/arch/um/drivers/fd.c @@ -11,6 +11,7 @@ #include "user.h" #include "user_util.h" #include "chan_user.h" +#include "os.h" struct fd_chan { int fd; diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index e0fdffa2d54..46ceb25a995 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -23,8 +23,9 @@ static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) { - struct tty_struct *tty = data; - struct line *line = tty->driver_data; + struct chan *chan = data; + struct line *line = chan->line; + struct tty_struct *tty = line->tty; if (line) chan_interrupt(&line->chan_list, &line->task, tty, irq); @@ -33,10 +34,11 @@ static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) static void line_timer_cb(void *arg) { - struct tty_struct *tty = arg; - struct line *line = tty->driver_data; + struct line *line = arg; - line_interrupt(line->driver->read_irq, arg, NULL); + if(!line->throttled) + chan_interrupt(&line->chan_list, &line->task, line->tty, + line->driver->read_irq); } /* Returns the free space inside the ring buffer of this line. @@ -124,7 +126,8 @@ static int buffer_data(struct line *line, const char *buf, int len) if (len < end){ memcpy(line->tail, buf, len); line->tail += len; - } else { + } + else { /* The circular buffer is wrapping */ memcpy(line->tail, buf, end); buf += end; @@ -170,7 +173,7 @@ static int flush_buffer(struct line *line) } count = line->tail - line->head; - n = write_chan(&line->chan_list, line->head, count, + n = write_chan(&line->chan_list, line->head, count, line->driver->write_irq); if(n < 0) @@ -227,7 +230,7 @@ int line_write(struct tty_struct *tty, const unsigned char *buf, int len) if (err <= 0 && (err != -EAGAIN || !ret)) ret = err; } else { - n = write_chan(&line->chan_list, buf, len, + n = write_chan(&line->chan_list, buf, len, line->driver->write_irq); if (n < 0) { ret = n; @@ -338,11 +341,36 @@ int line_ioctl(struct tty_struct *tty, struct file * file, return ret; } +void line_throttle(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + + deactivate_chan(&line->chan_list, line->driver->read_irq); + line->throttled = 1; +} + +void line_unthrottle(struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + + line->throttled = 0; + chan_interrupt(&line->chan_list, &line->task, tty, + line->driver->read_irq); + + /* Maybe there is enough stuff pending that calling the interrupt + * throttles us again. In this case, line->throttled will be 1 + * again and we shouldn't turn the interrupt back on. + */ + if(!line->throttled) + reactivate_chan(&line->chan_list, line->driver->read_irq); +} + static irqreturn_t line_write_interrupt(int irq, void *data, struct pt_regs *unused) { - struct tty_struct *tty = data; - struct line *line = tty->driver_data; + struct chan *chan = data; + struct line *line = chan->line; + struct tty_struct *tty = line->tty; int err; /* Interrupts are enabled here because we registered the interrupt with @@ -364,7 +392,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data, if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && (tty->ldisc.write_wakeup != NULL)) (tty->ldisc.write_wakeup)(tty); - + /* BLOCKING mode * In blocking mode, everything sleeps on tty->write_wait. * Sleeping in the console driver would break non-blocking @@ -376,53 +404,29 @@ static irqreturn_t line_write_interrupt(int irq, void *data, return IRQ_HANDLED; } -int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) +int line_setup_irq(int fd, int input, int output, struct line *line, void *data) { - struct line *line = tty->driver_data; struct line_driver *driver = line->driver; int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM; if (input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, - line_interrupt, flags, - driver->read_irq_name, tty); + line_interrupt, flags, + driver->read_irq_name, data); if (err) return err; if (output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, - line_write_interrupt, flags, - driver->write_irq_name, tty); + line_write_interrupt, flags, + driver->write_irq_name, data); line->have_irq = 1; return err; } -void line_disable(struct tty_struct *tty, int current_irq) -{ - struct line *line = tty->driver_data; - - if(!line->have_irq) - return; - - if(line->driver->read_irq == current_irq) - free_irq_later(line->driver->read_irq, tty); - else { - free_irq(line->driver->read_irq, tty); - } - - if(line->driver->write_irq == current_irq) - free_irq_later(line->driver->write_irq, tty); - else { - free_irq(line->driver->write_irq, tty); - } - - line->have_irq = 0; -} - -int line_open(struct line *lines, struct tty_struct *tty, - struct chan_opts *opts) +int line_open(struct line *lines, struct tty_struct *tty) { struct line *line; - int err = 0; + int err = -ENODEV; line = &lines[tty->index]; tty->driver_data = line; @@ -430,31 +434,29 @@ int line_open(struct line *lines, struct tty_struct *tty, /* The IRQ which takes this lock is not yet enabled and won't be run * before the end, so we don't need to use spin_lock_irq.*/ spin_lock(&line->lock); - if (tty->count == 1) { - if (!line->valid) { - err = -ENODEV; - goto out; - } - if (list_empty(&line->chan_list)) { - err = parse_chan_pair(line->init_str, &line->chan_list, - line->init_pri, tty->index, opts); - if(err) goto out; - err = open_chan(&line->chan_list); - if(err) goto out; + + tty->driver_data = line; + line->tty = tty; + if(!line->valid) + goto out; + + if(tty->count == 1){ + /* Here the device is opened, if necessary, and interrupt + * is registered. + */ + enable_chan(line); + INIT_WORK(&line->task, line_timer_cb, line); + + if(!line->sigio){ + chan_enable_winch(&line->chan_list, tty); + line->sigio = 1; } - /* Here the interrupt is registered.*/ - enable_chan(&line->chan_list, tty); - INIT_WORK(&line->task, line_timer_cb, tty); - } - if(!line->sigio){ - chan_enable_winch(&line->chan_list, tty); - line->sigio = 1; + chan_window_size(&line->chan_list, &tty->winsize.ws_row, + &tty->winsize.ws_col); } - chan_window_size(&line->chan_list, &tty->winsize.ws_row, - &tty->winsize.ws_col); - line->count++; + err = 0; out: spin_unlock(&line->lock); return err; @@ -474,15 +476,14 @@ void line_close(struct tty_struct *tty, struct file * filp) /* We ignore the error anyway! */ flush_buffer(line); - line->count--; - if (tty->count == 1) { - line_disable(tty, -1); + if(tty->count == 1){ + line->tty = NULL; tty->driver_data = NULL; - } - if((line->count == 0) && line->sigio){ - unregister_winch(tty); - line->sigio = 0; + if(line->sigio){ + unregister_winch(tty); + line->sigio = 0; + } } spin_unlock_irq(&line->lock); @@ -493,17 +494,15 @@ void close_lines(struct line *lines, int nlines) int i; for(i = 0; i < nlines; i++) - close_chan(&lines[i].chan_list); + close_chan(&lines[i].chan_list, 0); } /* Common setup code for both startup command line and mconsole initialization. * @lines contains the the array (of size @num) to modify; * @init is the setup string; - * @all_allowed is a boolean saying if we can setup the whole @lines - * at once. For instance, it will be usually true for startup init. (where we - * can use con=xterm) and false for mconsole.*/ + */ -int line_setup(struct line *lines, unsigned int num, char *init, int all_allowed) +int line_setup(struct line *lines, unsigned int num, char *init) { int i, n; char *end; @@ -512,10 +511,11 @@ int line_setup(struct line *lines, unsigned int num, char *init, int all_allowed /* We said con=/ssl= instead of con#=, so we are configuring all * consoles at once.*/ n = -1; - } else { + } + else { n = simple_strtoul(init, &end, 0); if(*end != '='){ - printk(KERN_ERR "line_setup failed to parse \"%s\"\n", + printk(KERN_ERR "line_setup failed to parse \"%s\"\n", init); return 0; } @@ -527,8 +527,9 @@ int line_setup(struct line *lines, unsigned int num, char *init, int all_allowed printk("line_setup - %d out of range ((0 ... %d) allowed)\n", n, num - 1); return 0; - } else if (n >= 0){ - if (lines[n].count > 0) { + } + else if (n >= 0){ + if (lines[n].tty != NULL) { printk("line_setup - device %d is open\n", n); return 0; } @@ -539,13 +540,10 @@ int line_setup(struct line *lines, unsigned int num, char *init, int all_allowed else { lines[n].init_str = init; lines[n].valid = 1; - } + } } - } else if(!all_allowed){ - printk("line_setup - can't configure all devices from " - "mconsole\n"); - return 0; - } else { + } + else { for(i = 0; i < num; i++){ if(lines[i].init_pri <= INIT_ALL){ lines[i].init_pri = INIT_ALL; @@ -557,18 +555,33 @@ int line_setup(struct line *lines, unsigned int num, char *init, int all_allowed } } } - return 1; + return n == -1 ? num : n; } -int line_config(struct line *lines, unsigned int num, char *str) +int line_config(struct line *lines, unsigned int num, char *str, + struct chan_opts *opts) { - char *new = uml_strdup(str); + struct line *line; + char *new; + int n; + if(*str == '='){ + printk("line_config - can't configure all devices from " + "mconsole\n"); + return 1; + } + + new = kstrdup(str, GFP_KERNEL); if(new == NULL){ - printk("line_config - uml_strdup failed\n"); - return -ENOMEM; + printk("line_config - kstrdup failed\n"); + return 1; } - return !line_setup(lines, num, new, 0); + n = line_setup(lines, num, new); + if(n < 0) + return 1; + + line = &lines[n]; + return parse_chan_pair(line->init_str, line, n, opts); } int line_get_config(char *name, struct line *lines, unsigned int num, char *str, @@ -594,7 +607,7 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str, spin_lock(&line->lock); if(!line->valid) CONFIG_CHUNK(str, size, n, "none", 1); - else if(line->count == 0) + else if(line->tty == NULL) CONFIG_CHUNK(str, size, n, line->init_str, 1); else n = chan_config_string(&line->chan_list, str, size, error_out); spin_unlock(&line->lock); @@ -619,14 +632,18 @@ int line_id(char **str, int *start_out, int *end_out) int line_remove(struct line *lines, unsigned int num, int n) { + int err; char config[sizeof("conxxxx=none\0")]; sprintf(config, "%d=none", n); - return !line_setup(lines, num, config, 0); + err = line_setup(lines, num, config); + if(err >= 0) + err = 0; + return err; } struct tty_driver *line_register_devfs(struct lines *set, - struct line_driver *line_driver, + struct line_driver *line_driver, struct tty_operations *ops, struct line *lines, int nlines) { @@ -655,7 +672,7 @@ struct tty_driver *line_register_devfs(struct lines *set, } for(i = 0; i < nlines; i++){ - if(!lines[i].valid) + if(!lines[i].valid) tty_unregister_device(driver, i); } @@ -663,24 +680,28 @@ struct tty_driver *line_register_devfs(struct lines *set, return driver; } -static spinlock_t winch_handler_lock; -LIST_HEAD(winch_handlers); +static DEFINE_SPINLOCK(winch_handler_lock); +static LIST_HEAD(winch_handlers); -void lines_init(struct line *lines, int nlines) +void lines_init(struct line *lines, int nlines, struct chan_opts *opts) { struct line *line; int i; - spin_lock_init(&winch_handler_lock); for(i = 0; i < nlines; i++){ line = &lines[i]; INIT_LIST_HEAD(&line->chan_list); - spin_lock_init(&line->lock); - if(line->init_str != NULL){ - line->init_str = uml_strdup(line->init_str); - if(line->init_str == NULL) - printk("lines_init - uml_strdup returned " - "NULL\n"); + + if(line->init_str == NULL) + continue; + + line->init_str = kstrdup(line->init_str, GFP_KERNEL); + if(line->init_str == NULL) + printk("lines_init - kstrdup returned NULL\n"); + + if(parse_chan_pair(line->init_str, line, i, opts)){ + printk("parse_chan_pair failed for device %d\n", i); + line->valid = 0; } } } @@ -717,8 +738,7 @@ irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) tty = winch->tty; if (tty != NULL) { line = tty->driver_data; - chan_window_size(&line->chan_list, - &tty->winsize.ws_row, + chan_window_size(&line->chan_list, &tty->winsize.ws_row, &tty->winsize.ws_col); kill_pg(tty->pgrp, SIGWINCH, 1); } @@ -749,60 +769,54 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) spin_unlock(&winch_handler_lock); if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, - SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "winch", winch) < 0) printk("register_winch_irq - failed to register IRQ\n"); } +static void free_winch(struct winch *winch) +{ + list_del(&winch->list); + + if(winch->pid != -1) + os_kill_process(winch->pid, 1); + if(winch->fd != -1) + os_close_file(winch->fd); + + free_irq(WINCH_IRQ, winch); + kfree(winch); +} + static void unregister_winch(struct tty_struct *tty) { struct list_head *ele; - struct winch *winch, *found = NULL; + struct winch *winch; spin_lock(&winch_handler_lock); + list_for_each(ele, &winch_handlers){ winch = list_entry(ele, struct winch, list); if(winch->tty == tty){ - found = winch; - break; + free_winch(winch); + break; } } - if(found == NULL) - goto err; - - list_del(&winch->list); - spin_unlock(&winch_handler_lock); - - if(winch->pid != -1) - os_kill_process(winch->pid, 1); - - free_irq(WINCH_IRQ, winch); - kfree(winch); - - return; -err: spin_unlock(&winch_handler_lock); } -/* XXX: No lock as it's an exitcall... is this valid? Depending on cleanup - * order... are we sure that nothing else is done on the list? */ static void winch_cleanup(void) { - struct list_head *ele; + struct list_head *ele, *next; struct winch *winch; - list_for_each(ele, &winch_handlers){ + spin_lock(&winch_handler_lock); + + list_for_each_safe(ele, next, &winch_handlers){ winch = list_entry(ele, struct winch, list); - if(winch->fd != -1){ - /* Why is this different from the above free_irq(), - * which deactivates SIGIO? This searches the FD - * somewhere else and removes it from the list... */ - deactivate_fd(winch->fd, WINCH_IRQ); - os_close_file(winch->fd); - } - if(winch->pid != -1) - os_kill_process(winch->pid, 1); + free_winch(winch); } + + spin_unlock(&winch_handler_lock); } __uml_exitcall(winch_cleanup); @@ -811,10 +825,10 @@ char *add_xterm_umid(char *base) char *umid, *title; int len; - umid = get_umid(1); - if(umid == NULL) + umid = get_umid(); + if(*umid == '\0') return base; - + len = strlen(base) + strlen(" ()") + strlen(umid) + 1; title = kmalloc(len, GFP_KERNEL); if(title == NULL){ diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 12c95368124..e3d57656717 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -20,6 +20,7 @@ #include "linux/namei.h" #include "linux/proc_fs.h" #include "linux/syscalls.h" +#include "linux/console.h" #include "asm/irq.h" #include "asm/uaccess.h" #include "user_util.h" @@ -34,7 +35,7 @@ #include "irq_kern.h" #include "choose-mode.h" -static int do_unlink_socket(struct notifier_block *notifier, +static int do_unlink_socket(struct notifier_block *notifier, unsigned long what, void *data) { return(mconsole_unlink_socket()); @@ -46,12 +47,12 @@ static struct notifier_block reboot_notifier = { .priority = 0, }; -/* Safe without explicit locking for now. Tasklets provide their own +/* Safe without explicit locking for now. Tasklets provide their own * locking, and the interrupt handler is safe because it can't interrupt * itself and it can only happen on CPU 0. */ -LIST_HEAD(mc_requests); +static LIST_HEAD(mc_requests); static void mc_work_proc(void *unused) { @@ -60,7 +61,7 @@ static void mc_work_proc(void *unused) while(!list_empty(&mc_requests)){ local_save_flags(flags); - req = list_entry(mc_requests.next, struct mconsole_entry, + req = list_entry(mc_requests.next, struct mconsole_entry, list); list_del(&req->list); local_irq_restore(flags); @@ -69,7 +70,7 @@ static void mc_work_proc(void *unused) } } -DECLARE_WORK(mconsole_work, mc_work_proc, NULL); +static DECLARE_WORK(mconsole_work, mc_work_proc, NULL); static irqreturn_t mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs) @@ -103,8 +104,8 @@ void mconsole_version(struct mc_request *req) { char version[256]; - sprintf(version, "%s %s %s %s %s", system_utsname.sysname, - system_utsname.nodename, system_utsname.release, + sprintf(version, "%s %s %s %s %s", system_utsname.sysname, + system_utsname.nodename, system_utsname.release, system_utsname.version, system_utsname.machine); mconsole_reply(req, version, 0, 0); } @@ -348,7 +349,7 @@ static struct mc_device *mconsole_find_dev(char *name) #define CONFIG_BUF_SIZE 64 -static void mconsole_get_config(int (*get_config)(char *, char *, int, +static void mconsole_get_config(int (*get_config)(char *, char *, int, char **), struct mc_request *req, char *name) { @@ -389,7 +390,6 @@ static void mconsole_get_config(int (*get_config)(char *, char *, int, out: if(buf != default_buf) kfree(buf); - } void mconsole_config(struct mc_request *req) @@ -420,9 +420,9 @@ void mconsole_config(struct mc_request *req) void mconsole_remove(struct mc_request *req) { - struct mc_device *dev; + struct mc_device *dev; char *ptr = req->request.data, *err_msg = ""; - char error[256]; + char error[256]; int err, start, end, n; ptr += strlen("remove"); @@ -433,37 +433,112 @@ void mconsole_remove(struct mc_request *req) return; } - ptr = &ptr[strlen(dev->name)]; - - err = 1; - n = (*dev->id)(&ptr, &start, &end); - if(n < 0){ - err_msg = "Couldn't parse device number"; - goto out; - } - else if((n < start) || (n > end)){ - sprintf(error, "Invalid device number - must be between " - "%d and %d", start, end); - err_msg = error; - goto out; - } + ptr = &ptr[strlen(dev->name)]; + + err = 1; + n = (*dev->id)(&ptr, &start, &end); + if(n < 0){ + err_msg = "Couldn't parse device number"; + goto out; + } + else if((n < start) || (n > end)){ + sprintf(error, "Invalid device number - must be between " + "%d and %d", start, end); + err_msg = error; + goto out; + } err = (*dev->remove)(n); - switch(err){ - case -ENODEV: - err_msg = "Device doesn't exist"; - break; - case -EBUSY: - err_msg = "Device is currently open"; - break; - default: - break; - } - out: + switch(err){ + case -ENODEV: + err_msg = "Device doesn't exist"; + break; + case -EBUSY: + err_msg = "Device is currently open"; + break; + default: + break; + } +out: mconsole_reply(req, err_msg, err, 0); } +static DEFINE_SPINLOCK(console_lock); +static LIST_HEAD(clients); +static char console_buf[MCONSOLE_MAX_DATA]; +static int console_index = 0; + +static void console_write(struct console *console, const char *string, + unsigned len) +{ + struct list_head *ele; + int n; + + if(list_empty(&clients)) + return; + + while(1){ + n = min(len, ARRAY_SIZE(console_buf) - console_index); + strncpy(&console_buf[console_index], string, n); + console_index += n; + string += n; + len -= n; + if(len == 0) + return; + + list_for_each(ele, &clients){ + struct mconsole_entry *entry; + + entry = list_entry(ele, struct mconsole_entry, list); + mconsole_reply_len(&entry->request, console_buf, + console_index, 0, 1); + } + + console_index = 0; + } +} + +static struct console mc_console = { .name = "mc", + .write = console_write, + .flags = CON_ENABLED, + .index = -1 }; + +static int mc_add_console(void) +{ + register_console(&mc_console); + return 0; +} + +late_initcall(mc_add_console); + +static void with_console(struct mc_request *req, void (*proc)(void *), + void *arg) +{ + struct mconsole_entry entry; + unsigned long flags; + + INIT_LIST_HEAD(&entry.list); + entry.request = *req; + list_add(&entry.list, &clients); + spin_lock_irqsave(&console_lock, flags); + + (*proc)(arg); + + mconsole_reply_len(req, console_buf, console_index, 0, 0); + console_index = 0; + + spin_unlock_irqrestore(&console_lock, flags); + list_del(&entry.list); +} + #ifdef CONFIG_MAGIC_SYSRQ +static void sysrq_proc(void *arg) +{ + char *op = arg; + + handle_sysrq(*op, ¤t->thread.regs, NULL); +} + void mconsole_sysrq(struct mc_request *req) { char *ptr = req->request.data; @@ -471,8 +546,13 @@ void mconsole_sysrq(struct mc_request *req) ptr += strlen("sysrq"); while(isspace(*ptr)) ptr++; - mconsole_reply(req, "", 0, 0); - handle_sysrq(*ptr, ¤t->thread.regs, NULL); + /* With 'b', the system will shut down without a chance to reply, + * so in this case, we reply first. + */ + if(*ptr == 'b') + mconsole_reply(req, "", 0, 0); + + with_console(req, sysrq_proc, ptr); } #else void mconsole_sysrq(struct mc_request *req) @@ -481,6 +561,14 @@ void mconsole_sysrq(struct mc_request *req) } #endif +static void stack_proc(void *arg) +{ + struct task_struct *from = current, *to = arg; + + to->thread.saved_task = from; + switch_to(from, to, from); +} + /* Mconsole stack trace * Added by Allan Graves, Jeff Dike * Dumps a stacks registers to the linux console. @@ -488,37 +576,34 @@ void mconsole_sysrq(struct mc_request *req) */ void do_stack(struct mc_request *req) { - char *ptr = req->request.data; - int pid_requested= -1; - struct task_struct *from = NULL; + char *ptr = req->request.data; + int pid_requested= -1; + struct task_struct *from = NULL; struct task_struct *to = NULL; - /* Would be nice: - * 1) Send showregs output to mconsole. + /* Would be nice: + * 1) Send showregs output to mconsole. * 2) Add a way to stack dump all pids. */ - ptr += strlen("stack"); - while(isspace(*ptr)) ptr++; - - /* Should really check for multiple pids or reject bad args here */ - /* What do the arguments in mconsole_reply mean? */ - if(sscanf(ptr, "%d", &pid_requested) == 0){ - mconsole_reply(req, "Please specify a pid", 1, 0); - return; - } + ptr += strlen("stack"); + while(isspace(*ptr)) ptr++; - from = current; - to = find_task_by_pid(pid_requested); + /* Should really check for multiple pids or reject bad args here */ + /* What do the arguments in mconsole_reply mean? */ + if(sscanf(ptr, "%d", &pid_requested) == 0){ + mconsole_reply(req, "Please specify a pid", 1, 0); + return; + } - if((to == NULL) || (pid_requested == 0)) { - mconsole_reply(req, "Couldn't find that pid", 1, 0); - return; - } - to->thread.saved_task = current; + from = current; - switch_to(from, to, from); - mconsole_reply(req, "Stack Dumped to console and message log", 0, 0); + to = find_task_by_pid(pid_requested); + if((to == NULL) || (pid_requested == 0)) { + mconsole_reply(req, "Couldn't find that pid", 1, 0); + return; + } + with_console(req, stack_proc, to); } void mconsole_stack(struct mc_request *req) @@ -534,9 +619,9 @@ void mconsole_stack(struct mc_request *req) /* Changed by mconsole_setup, which is __setup, and called before SMP is * active. */ -static char *notify_socket = NULL; +static char *notify_socket = NULL; -int mconsole_init(void) +static int mconsole_init(void) { /* long to avoid size mismatch warnings from gcc */ long sock; @@ -563,16 +648,16 @@ int mconsole_init(void) } if(notify_socket != NULL){ - notify_socket = uml_strdup(notify_socket); + notify_socket = kstrdup(notify_socket, GFP_KERNEL); if(notify_socket != NULL) mconsole_notify(notify_socket, MCONSOLE_SOCKET, - mconsole_socket_name, + mconsole_socket_name, strlen(mconsole_socket_name) + 1); else printk(KERN_ERR "mconsole_setup failed to strdup " "string\n"); } - printk("mconsole (version %d) initialized on %s\n", + printk("mconsole (version %d) initialized on %s\n", MCONSOLE_VERSION, mconsole_socket_name); return(0); } @@ -585,7 +670,7 @@ static int write_proc_mconsole(struct file *file, const char __user *buffer, char *buf; buf = kmalloc(count + 1, GFP_KERNEL); - if(buf == NULL) + if(buf == NULL) return(-ENOMEM); if(copy_from_user(buf, buffer, count)){ @@ -661,7 +746,7 @@ static int notify_panic(struct notifier_block *self, unsigned long unused1, if(notify_socket == NULL) return(0); - mconsole_notify(notify_socket, MCONSOLE_PANIC, message, + mconsole_notify(notify_socket, MCONSOLE_PANIC, message, strlen(message) + 1); return(0); } @@ -686,14 +771,3 @@ char *mconsole_notify_socket(void) } EXPORT_SYMBOL(mconsole_notify_socket); - -/* - * 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: - */ diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c index 310c1f823f2..4b109fe7fff 100644 --- a/arch/um/drivers/mconsole_user.c +++ b/arch/um/drivers/mconsole_user.c @@ -122,12 +122,12 @@ int mconsole_get_request(int fd, struct mc_request *req) return(1); } -int mconsole_reply(struct mc_request *req, char *str, int err, int more) +int mconsole_reply_len(struct mc_request *req, const char *str, int total, + int err, int more) { struct mconsole_reply reply; - int total, len, n; + int len, n; - total = strlen(str); do { reply.err = err; @@ -155,6 +155,12 @@ int mconsole_reply(struct mc_request *req, char *str, int err, int more) return(0); } +int mconsole_reply(struct mc_request *req, const char *str, int err, int more) +{ + return mconsole_reply_len(req, str, strlen(str), err, more); +} + + int mconsole_unlink_socket(void) { unlink(mconsole_socket_name); diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 84c73a300ac..8ebb2241ad4 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -34,7 +34,7 @@ #define DRIVER_NAME "uml-netdev" static DEFINE_SPINLOCK(opened_lock); -LIST_HEAD(opened); +static LIST_HEAD(opened); static int uml_net_rx(struct net_device *dev) { @@ -68,6 +68,11 @@ static int uml_net_rx(struct net_device *dev) return pkt_len; } +static void uml_dev_close(void* dev) +{ + dev_close( (struct net_device *) dev); +} + irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; @@ -80,15 +85,21 @@ irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_lock(&lp->lock); while((err = uml_net_rx(dev)) > 0) ; if(err < 0) { + DECLARE_WORK(close_work, uml_dev_close, dev); printk(KERN_ERR "Device '%s' read returned %d, shutting it down\n", dev->name, err); - dev_close(dev); + /* dev_close can't be called in interrupt context, and takes + * again lp->lock. + * And dev_close() can be safely called multiple times on the + * same device, since it tests for (dev->flags & IFF_UP). So + * there's no harm in delaying the device shutdown. */ + schedule_work(&close_work); goto out; } reactivate_fd(lp->fd, UM_ETH_IRQ); - out: +out: spin_unlock(&lp->lock); return(IRQ_HANDLED); } @@ -150,6 +161,7 @@ static int uml_net_close(struct net_device *dev) if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user); lp->fd = -1; + list_del(&lp->list); spin_unlock(&lp->lock); return 0; @@ -266,7 +278,7 @@ void uml_net_user_timer_expire(unsigned long _conn) } static DEFINE_SPINLOCK(devices_lock); -static struct list_head devices = LIST_HEAD_INIT(devices); +static LIST_HEAD(devices); static struct platform_driver uml_net_driver = { .driver = { @@ -316,6 +328,11 @@ static int eth_configure(int n, void *init, char *mac, return 1; } + lp = dev->priv; + /* This points to the transport private data. It's still clear, but we + * must memset it to 0 *now*. Let's help the drivers. */ + memset(lp, 0, size); + /* sysfs register */ if (!driver_registered) { platform_driver_register(¨_net_driver); @@ -357,7 +374,6 @@ static int eth_configure(int n, void *init, char *mac, free_netdev(dev); return 1; } - lp = dev->priv; /* lp.user is the first four bytes of the transport data, which * has already been initialized. This structure assignment will @@ -586,7 +602,7 @@ static int net_config(char *str) err = eth_parse(str, &n, &str); if(err) return(err); - str = uml_strdup(str); + str = kstrdup(str, GFP_KERNEL); if(str == NULL){ printk(KERN_ERR "net_config failed to strdup string\n"); return(-1); @@ -715,6 +731,7 @@ static void close_devices(void) list_for_each(ele, &opened){ lp = list_entry(ele, struct uml_net_private, list); + free_irq(lp->dev->irq, lp->dev); if((lp->close != NULL) && (lp->fd >= 0)) (*lp->close)(lp->fd, &lp->user); if(lp->remove != NULL) (*lp->remove)(&lp->user); diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c index 62e04ecfada..a32ef55cb24 100644 --- a/arch/um/drivers/ssl.c +++ b/arch/um/drivers/ssl.c @@ -69,7 +69,7 @@ static struct line_driver driver = { .name = "ssl", .config = ssl_config, .get_config = ssl_get_config, - .id = line_id, + .id = line_id, .remove = ssl_remove, }, }; @@ -84,26 +84,23 @@ static struct lines lines = LINES_INIT(NR_PORTS); static int ssl_config(char *str) { - return(line_config(serial_lines, - sizeof(serial_lines)/sizeof(serial_lines[0]), str)); + return line_config(serial_lines, ARRAY_SIZE(serial_lines), str, &opts); } static int ssl_get_config(char *dev, char *str, int size, char **error_out) { - return(line_get_config(dev, serial_lines, - sizeof(serial_lines)/sizeof(serial_lines[0]), - str, size, error_out)); + return line_get_config(dev, serial_lines, ARRAY_SIZE(serial_lines), str, + size, error_out); } static int ssl_remove(int n) { - return line_remove(serial_lines, - sizeof(serial_lines)/sizeof(serial_lines[0]), n); + return line_remove(serial_lines, ARRAY_SIZE(serial_lines), n); } int ssl_open(struct tty_struct *tty, struct file *filp) { - return line_open(serial_lines, tty, &opts); + return line_open(serial_lines, tty); } #if 0 @@ -112,16 +109,6 @@ static void ssl_flush_buffer(struct tty_struct *tty) return; } -static void ssl_throttle(struct tty_struct * tty) -{ - printk(KERN_ERR "Someone should implement ssl_throttle\n"); -} - -static void ssl_unthrottle(struct tty_struct * tty) -{ - printk(KERN_ERR "Someone should implement ssl_unthrottle\n"); -} - static void ssl_stop(struct tty_struct *tty) { printk(KERN_ERR "Someone should implement ssl_stop\n"); @@ -148,9 +135,9 @@ static struct tty_operations ssl_ops = { .flush_chars = line_flush_chars, .set_termios = line_set_termios, .ioctl = line_ioctl, + .throttle = line_throttle, + .unthrottle = line_unthrottle, #if 0 - .throttle = ssl_throttle, - .unthrottle = ssl_unthrottle, .stop = ssl_stop, .start = ssl_start, .hangup = ssl_hangup, @@ -183,7 +170,7 @@ static int ssl_console_setup(struct console *co, char *options) { struct line *line = &serial_lines[co->index]; - return console_open_chan(line,co,&opts); + return console_open_chan(line, co, &opts); } static struct console ssl_cons = { @@ -199,12 +186,13 @@ int ssl_init(void) { char *new_title; - printk(KERN_INFO "Initializing software serial port version %d\n", + printk(KERN_INFO "Initializing software serial port version %d\n", ssl_version); ssl_driver = line_register_devfs(&lines, &driver, &ssl_ops, - serial_lines, ARRAY_SIZE(serial_lines)); + serial_lines, + ARRAY_SIZE(serial_lines)); - lines_init(serial_lines, sizeof(serial_lines)/sizeof(serial_lines[0])); + lines_init(serial_lines, ARRAY_SIZE(serial_lines), &opts); new_title = add_xterm_umid(opts.xterm_title); if (new_title != NULL) @@ -212,7 +200,7 @@ int ssl_init(void) ssl_init_done = 1; register_console(&ssl_cons); - return(0); + return 0; } late_initcall(ssl_init); @@ -220,16 +208,13 @@ static void ssl_exit(void) { if (!ssl_init_done) return; - close_lines(serial_lines, - sizeof(serial_lines)/sizeof(serial_lines[0])); + close_lines(serial_lines, ARRAY_SIZE(serial_lines)); } __uml_exitcall(ssl_exit); static int ssl_chan_setup(char *str) { - return(line_setup(serial_lines, - sizeof(serial_lines)/sizeof(serial_lines[0]), - str, 1)); + return line_setup(serial_lines, ARRAY_SIZE(serial_lines), str); } __setup("ssl", ssl_chan_setup); diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c index 005aa6333b6..61db8b2fc83 100644 --- a/arch/um/drivers/stdio_console.c +++ b/arch/um/drivers/stdio_console.c @@ -75,7 +75,7 @@ static struct line_driver driver = { .name = "con", .config = con_config, .get_config = con_get_config, - .id = line_id, + .id = line_id, .remove = con_remove, }, }; @@ -86,28 +86,27 @@ static struct lines console_lines = LINES_INIT(MAX_TTYS); * individual elements are protected by individual semaphores. */ struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver), - [ 1 ... MAX_TTYS - 1 ] = + [ 1 ... MAX_TTYS - 1 ] = LINE_INIT(CONFIG_CON_CHAN, &driver) }; static int con_config(char *str) { - return(line_config(vts, sizeof(vts)/sizeof(vts[0]), str)); + return line_config(vts, ARRAY_SIZE(vts), str, &opts); } static int con_get_config(char *dev, char *str, int size, char **error_out) { - return(line_get_config(dev, vts, sizeof(vts)/sizeof(vts[0]), str, - size, error_out)); + return line_get_config(dev, vts, ARRAY_SIZE(vts), str, size, error_out); } static int con_remove(int n) { - return line_remove(vts, sizeof(vts)/sizeof(vts[0]), n); + return line_remove(vts, ARRAY_SIZE(vts), n); } static int con_open(struct tty_struct *tty, struct file *filp) { - return line_open(vts, tty, &opts); + return line_open(vts, tty); } static int con_init_done = 0; @@ -117,16 +116,18 @@ static struct tty_operations console_ops = { .close = line_close, .write = line_write, .put_char = line_put_char, - .write_room = line_write_room, + .write_room = line_write_room, .chars_in_buffer = line_chars_in_buffer, .flush_buffer = line_flush_buffer, .flush_chars = line_flush_chars, .set_termios = line_set_termios, .ioctl = line_ioctl, + .throttle = line_throttle, + .unthrottle = line_unthrottle, }; static void uml_console_write(struct console *console, const char *string, - unsigned len) + unsigned len) { struct line *line = &vts[console->index]; unsigned long flags; @@ -146,7 +147,7 @@ static int uml_console_setup(struct console *co, char *options) { struct line *line = &vts[co->index]; - return console_open_chan(line,co,&opts); + return console_open_chan(line, co, &opts); } static struct console stdiocons = { @@ -156,7 +157,7 @@ static struct console stdiocons = { .setup = uml_console_setup, .flags = CON_PRINTBUFFER, .index = -1, - .data = &vts, + .data = &vts, }; int stdio_init(void) @@ -166,11 +167,11 @@ int stdio_init(void) console_driver = line_register_devfs(&console_lines, &driver, &console_ops, vts, ARRAY_SIZE(vts)); - if (NULL == console_driver) + if (console_driver == NULL) return -1; printk(KERN_INFO "Initialized stdio console driver\n"); - lines_init(vts, sizeof(vts)/sizeof(vts[0])); + lines_init(vts, ARRAY_SIZE(vts), &opts); new_title = add_xterm_umid(opts.xterm_title); if(new_title != NULL) @@ -178,7 +179,7 @@ int stdio_init(void) con_init_done = 1; register_console(&stdiocons); - return(0); + return 0; } late_initcall(stdio_init); @@ -186,13 +187,13 @@ static void console_exit(void) { if (!con_init_done) return; - close_lines(vts, sizeof(vts)/sizeof(vts[0])); + close_lines(vts, ARRAY_SIZE(vts)); } __uml_exitcall(console_exit); static int console_chan_setup(char *str) { - return(line_setup(vts, sizeof(vts)/sizeof(vts[0]), str, 1)); + return line_setup(vts, ARRAY_SIZE(vts), str); } __setup("con", console_chan_setup); __channel_help(console_chan_setup, "con"); diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 93898917cbe..101efd26d46 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -117,6 +117,7 @@ static int ubd_open(struct inode * inode, struct file * filp); static int ubd_release(struct inode * inode, struct file * file); static int ubd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); +static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo); #define MAX_DEV (8) @@ -125,6 +126,7 @@ static struct block_device_operations ubd_blops = { .open = ubd_open, .release = ubd_release, .ioctl = ubd_ioctl, + .getgeo = ubd_getgeo, }; /* Protected by the queue_lock */ @@ -706,7 +708,7 @@ static int ubd_config(char *str) { int n, err; - str = uml_strdup(str); + str = kstrdup(str, GFP_KERNEL); if(str == NULL){ printk(KERN_ERR "ubd_config failed to strdup string\n"); return(1); @@ -1058,10 +1060,19 @@ static void do_ubd_request(request_queue_t *q) } } +static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct ubd *dev = bdev->bd_disk->private_data; + + geo->heads = 128; + geo->sectors = 32; + geo->cylinders = dev->size / (128 * 32 * 512); + return 0; +} + static int ubd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { - struct hd_geometry __user *loc = (struct hd_geometry __user *) arg; struct ubd *dev = inode->i_bdev->bd_disk->private_data; struct hd_driveid ubd_id = { .cyls = 0, @@ -1070,16 +1081,7 @@ static int ubd_ioctl(struct inode * inode, struct file * file, }; switch (cmd) { - struct hd_geometry g; struct cdrom_volctrl volume; - case HDIO_GETGEO: - if(!loc) return(-EINVAL); - g.heads = 128; - g.sectors = 32; - g.cylinders = dev->size / (128 * 32 * 512); - g.start = get_start_sect(inode->i_bdev); - return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0); - case HDIO_GET_IDENTITY: ubd_id.cyls = dev->size / (128 * 32 * 512); if(copy_to_user((char __user *) arg, (char *) &ubd_id, @@ -1101,31 +1103,33 @@ static int ubd_ioctl(struct inode * inode, struct file * file, return(-EINVAL); } -static int same_backing_files(char *from_cmdline, char *from_cow, char *cow) +static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow) { struct uml_stat buf1, buf2; int err; - if(from_cmdline == NULL) return(1); - if(!strcmp(from_cmdline, from_cow)) return(1); + if(from_cmdline == NULL) + return 0; + if(!strcmp(from_cmdline, from_cow)) + return 0; err = os_stat_file(from_cmdline, &buf1); if(err < 0){ printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err); - return(1); + return 0; } err = os_stat_file(from_cow, &buf2); if(err < 0){ printk("Couldn't stat '%s', err = %d\n", from_cow, -err); - return(1); + return 1; } if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino)) - return(1); + return 0; printk("Backing file mismatch - \"%s\" requested,\n" "\"%s\" specified in COW header of \"%s\"\n", from_cmdline, from_cow, cow); - return(0); + return 1; } static int backing_file_mismatch(char *file, __u64 size, time_t mtime) @@ -1187,18 +1191,19 @@ int open_ubd_file(char *file, struct openflags *openflags, unsigned long long size; __u32 version, align; char *backing_file; - int fd, err, sectorsize, same, mode = 0644; + int fd, err, sectorsize, asked_switch, mode = 0644; fd = os_open_file(file, *openflags, mode); - if(fd < 0){ - if((fd == -ENOENT) && (create_cow_out != NULL)) + if (fd < 0) { + if ((fd == -ENOENT) && (create_cow_out != NULL)) *create_cow_out = 1; - if(!openflags->w || - ((fd != -EROFS) && (fd != -EACCES))) return(fd); + if (!openflags->w || + ((fd != -EROFS) && (fd != -EACCES))) + return fd; openflags->w = 0; fd = os_open_file(file, *openflags, mode); - if(fd < 0) - return(fd); + if (fd < 0) + return fd; } err = os_lock_file(fd, openflags->w); @@ -1207,7 +1212,9 @@ int open_ubd_file(char *file, struct openflags *openflags, goto out_close; } - if(backing_file_out == NULL) return(fd); + /* Succesful return case! */ + if(backing_file_out == NULL) + return(fd); err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime, &size, §orsize, &align, bitmap_offset_out); @@ -1216,34 +1223,34 @@ int open_ubd_file(char *file, struct openflags *openflags, "errno = %d\n", file, -err); goto out_close; } - if(err) return(fd); - - if(backing_file_out == NULL) return(fd); + if(err) + return(fd); - same = same_backing_files(*backing_file_out, backing_file, file); + asked_switch = path_requires_switch(*backing_file_out, backing_file, file); - if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){ + /* Allow switching only if no mismatch. */ + if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) { printk("Switching backing file to '%s'\n", *backing_file_out); err = write_cow_header(file, fd, *backing_file_out, sectorsize, align, &size); - if(err){ + if (err) { printk("Switch failed, errno = %d\n", -err); - return(err); + goto out_close; } - } - else { + } else { *backing_file_out = backing_file; err = backing_file_mismatch(*backing_file_out, size, mtime); - if(err) goto out_close; + if (err) + goto out_close; } cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, bitmap_len_out, data_offset_out); - return(fd); + return fd; out_close: os_close_file(fd); - return(err); + return err; } int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, @@ -1387,15 +1394,6 @@ int io_thread(void *arg) printk("io_thread - write failed, fd = %d, err = %d\n", kernel_fd, -n); } -} -/* - * 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: - */ + return 0; +} diff --git a/arch/um/include/chan_kern.h b/arch/um/include/chan_kern.h index da9a6717e7a..1bb5e9d9427 100644 --- a/arch/um/include/chan_kern.h +++ b/arch/um/include/chan_kern.h @@ -14,21 +14,23 @@ struct chan { struct list_head list; + struct list_head free_list; + struct line *line; char *dev; unsigned int primary:1; unsigned int input:1; unsigned int output:1; unsigned int opened:1; + unsigned int enabled:1; int fd; - enum chan_init_pri pri; struct chan_ops *ops; void *data; }; extern void chan_interrupt(struct list_head *chans, struct work_struct *task, struct tty_struct *tty, int irq); -extern int parse_chan_pair(char *str, struct list_head *chans, int pri, - int device, struct chan_opts *opts); +extern int parse_chan_pair(char *str, struct line *line, int device, + struct chan_opts *opts); extern int open_chan(struct list_head *chans); extern int write_chan(struct list_head *chans, const char *buf, int len, int write_irq); @@ -36,9 +38,11 @@ extern int console_write_chan(struct list_head *chans, const char *buf, int len); extern int console_open_chan(struct line *line, struct console *co, struct chan_opts *opts); -extern void close_chan(struct list_head *chans); +extern void deactivate_chan(struct list_head *chans, int irq); +extern void reactivate_chan(struct list_head *chans, int irq); extern void chan_enable_winch(struct list_head *chans, struct tty_struct *tty); -extern void enable_chan(struct list_head *chans, struct tty_struct *tty); +extern void enable_chan(struct line *line); +extern void close_chan(struct list_head *chans, int delay_free_irq); extern int chan_window_size(struct list_head *chans, unsigned short *rows_out, unsigned short *cols_out); @@ -47,14 +51,3 @@ extern int chan_config_string(struct list_head *chans, char *str, int size, char **error_out); #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: - */ diff --git a/arch/um/include/choose-mode.h b/arch/um/include/choose-mode.h index f25fa83a5da..b87b36a87d9 100644 --- a/arch/um/include/choose-mode.h +++ b/arch/um/include/choose-mode.h @@ -23,6 +23,9 @@ static inline void *__choose_mode(void *tt, void *skas) { #elif defined(UML_CONFIG_MODE_TT) #define CHOOSE_MODE(tt, skas) (tt) + +#else +#error CONFIG_MODE_SKAS and CONFIG_MODE_TT are both disabled #endif #define CHOOSE_MODE_PROC(tt, skas, args...) \ diff --git a/arch/um/include/irq_user.h b/arch/um/include/irq_user.h index f724b717213..b61deb8b362 100644 --- a/arch/um/include/irq_user.h +++ b/arch/um/include/irq_user.h @@ -18,19 +18,8 @@ extern int deactivate_all_fds(void); extern void forward_interrupts(int pid); extern void init_irq_signals(int on_sigstack); extern void forward_ipi(int fd, int pid); -extern void free_irq_later(int irq, void *dev_id); extern int activate_ipi(int fd, int pid); extern unsigned long irq_lock(void); extern void irq_unlock(unsigned long flags); -#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: - */ +#endif diff --git a/arch/um/include/kern.h b/arch/um/include/kern.h index 1e3170768b5..7d223beccbc 100644 --- a/arch/um/include/kern.h +++ b/arch/um/include/kern.h @@ -17,7 +17,7 @@ extern int errno; extern int clone(int (*proc)(void *), void *sp, int flags, void *data); extern int sleep(int); -extern int printf(char *fmt, ...); +extern int printf(const char *fmt, ...); extern char *strerror(int errnum); extern char *ptsname(int __fd); extern int munmap(void *, int); @@ -35,15 +35,6 @@ extern int read(unsigned int, char *, int); extern int pipe(int *); extern int sched_yield(void); extern int ptrace(int op, int pid, long addr, long data); + #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: - */ diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h index e5fec557019..c649108a9e9 100644 --- a/arch/um/include/kern_util.h +++ b/arch/um/include/kern_util.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -10,6 +10,19 @@ #include "sysdep/ptrace.h" #include "sysdep/faultinfo.h" +typedef void (*kern_hndl)(int, union uml_pt_regs *); + +struct kern_handlers { + kern_hndl relay_signal; + kern_hndl winch; + kern_hndl bus_handler; + kern_hndl page_fault; + kern_hndl sigio_handler; + kern_hndl timer_handler; +}; + +extern struct kern_handlers handlinfo_kern; + extern int ncpus; extern char *linux_prog; extern char *gdb_init; @@ -51,8 +64,6 @@ extern void timer_handler(int sig, union uml_pt_regs *regs); extern int set_signals(int enable); extern void force_sigbus(void); extern int pid_to_processor_id(int pid); -extern void block_signals(void); -extern void unblock_signals(void); extern void deliver_signals(void *t); extern int next_syscall_index(int max); extern int next_trap_index(int max); @@ -109,9 +120,13 @@ extern void machine_halt(void); extern int is_syscall(unsigned long addr); extern void arch_switch(void); extern void free_irq(unsigned int, void *); -extern int um_in_interrupt(void); extern int cpu(void); +/* Are we disallowed to sleep? Used to choose between GFP_KERNEL and GFP_ATOMIC. */ +extern int __cant_sleep(void); +extern void segv_handler(int sig, union uml_pt_regs *regs); +extern void sigio_handler(int sig, union uml_pt_regs *regs); + #endif /* diff --git a/arch/um/include/line.h b/arch/um/include/line.h index 5323d22a6ca..6f4d680dc1d 100644 --- a/arch/um/include/line.h +++ b/arch/um/include/line.h @@ -32,11 +32,13 @@ struct line_driver { }; struct line { + struct tty_struct *tty; char *init_str; int init_pri; struct list_head chan_list; int valid; int count; + int throttled; /*This lock is actually, mostly, local to*/ spinlock_t lock; @@ -58,14 +60,15 @@ struct line { #define LINE_INIT(str, d) \ { init_str : str, \ init_pri : INIT_STATIC, \ - chan_list : { }, \ valid : 1, \ + throttled : 0, \ + lock : SPIN_LOCK_UNLOCKED, \ buffer : NULL, \ head : NULL, \ tail : NULL, \ sigio : 0, \ - driver : d, \ - have_irq : 0 } + driver : d, \ + have_irq : 0 } struct lines { int num; @@ -74,11 +77,11 @@ struct lines { #define LINES_INIT(n) { num : n } extern void line_close(struct tty_struct *tty, struct file * filp); -extern int line_open(struct line *lines, struct tty_struct *tty, - struct chan_opts *opts); -extern int line_setup(struct line *lines, unsigned int sizeof_lines, char *init, - int all_allowed); -extern int line_write(struct tty_struct *tty, const unsigned char *buf, int len); +extern int line_open(struct line *lines, struct tty_struct *tty); +extern int line_setup(struct line *lines, unsigned int sizeof_lines, + char *init); +extern int line_write(struct tty_struct *tty, const unsigned char *buf, + int len); extern void line_put_char(struct tty_struct *tty, unsigned char ch); extern void line_set_termios(struct tty_struct *tty, struct termios * old); extern int line_chars_in_buffer(struct tty_struct *tty); @@ -87,23 +90,27 @@ extern void line_flush_chars(struct tty_struct *tty); extern int line_write_room(struct tty_struct *tty); extern int line_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +extern void line_throttle(struct tty_struct *tty); +extern void line_unthrottle(struct tty_struct *tty); extern char *add_xterm_umid(char *base); -extern int line_setup_irq(int fd, int input, int output, struct tty_struct *tty); +extern int line_setup_irq(int fd, int input, int output, struct line *line, + void *data); extern void line_close_chan(struct line *line); -extern void line_disable(struct tty_struct *tty, int current_irq); -extern struct tty_driver * line_register_devfs(struct lines *set, - struct line_driver *line_driver, +extern struct tty_driver * line_register_devfs(struct lines *set, + struct line_driver *line_driver, struct tty_operations *driver, struct line *lines, int nlines); -extern void lines_init(struct line *lines, int nlines); +extern void lines_init(struct line *lines, int nlines, struct chan_opts *opts); extern void close_lines(struct line *lines, int nlines); -extern int line_config(struct line *lines, unsigned int sizeof_lines, char *str); +extern int line_config(struct line *lines, unsigned int sizeof_lines, + char *str, struct chan_opts *opts); extern int line_id(char **str, int *start_out, int *end_out); extern int line_remove(struct line *lines, unsigned int sizeof_lines, int n); -extern int line_get_config(char *dev, struct line *lines, unsigned int sizeof_lines, char *str, +extern int line_get_config(char *dev, struct line *lines, + unsigned int sizeof_lines, char *str, int size, char **error_out); #endif diff --git a/arch/um/include/longjmp.h b/arch/um/include/longjmp.h new file mode 100644 index 00000000000..018b3819ab0 --- /dev/null +++ b/arch/um/include/longjmp.h @@ -0,0 +1,19 @@ +#ifndef __UML_LONGJMP_H +#define __UML_LONGJMP_H + +#include <setjmp.h> +#include "os.h" + +#define UML_SIGLONGJMP(buf, val) do { \ + longjmp(*buf, val); \ +} while(0) + +#define UML_SIGSETJMP(buf, enable) ({ \ + int n; \ + enable = get_signals(); \ + n = setjmp(*buf); \ + if(n != 0) \ + set_signals(enable); \ + n; }) + +#endif diff --git a/arch/um/include/mconsole.h b/arch/um/include/mconsole.h index b1b512f4703..58f67d39110 100644 --- a/arch/um/include/mconsole.h +++ b/arch/um/include/mconsole.h @@ -32,7 +32,7 @@ struct mconsole_reply { struct mconsole_notify { u32 magic; - u32 version; + u32 version; enum { MCONSOLE_SOCKET, MCONSOLE_PANIC, MCONSOLE_HANG, MCONSOLE_USER_NOTIFY } type; u32 len; @@ -66,7 +66,9 @@ struct mc_request extern char mconsole_socket_name[]; extern int mconsole_unlink_socket(void); -extern int mconsole_reply(struct mc_request *req, char *reply, int err, +extern int mconsole_reply_len(struct mc_request *req, const char *reply, + int len, int err, int more); +extern int mconsole_reply(struct mc_request *req, const char *str, int err, int more); extern void mconsole_version(struct mc_request *req); @@ -84,7 +86,7 @@ extern void mconsole_proc(struct mc_request *req); extern void mconsole_stack(struct mc_request *req); extern int mconsole_get_request(int fd, struct mc_request *req); -extern int mconsole_notify(char *sock_name, int type, const void *data, +extern int mconsole_notify(char *sock_name, int type, const void *data, int len); extern char *mconsole_notify_socket(void); extern void lock_notify(void); diff --git a/arch/um/include/mode_kern.h b/arch/um/include/mode_kern.h index 2d88afd0cf1..e7539a8451e 100644 --- a/arch/um/include/mode_kern.h +++ b/arch/um/include/mode_kern.h @@ -9,22 +9,11 @@ #include "linux/config.h" #ifdef CONFIG_MODE_TT -#include "mode_kern-tt.h" +#include "mode_kern_tt.h" #endif #ifdef CONFIG_MODE_SKAS -#include "mode_kern-skas.h" +#include "mode_kern_skas.h" #endif #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: - */ diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 2cccfa5b8ab..eb1710b8125 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -9,6 +9,9 @@ #include "uml-config.h" #include "asm/types.h" #include "../os/include/file.h" +#include "sysdep/ptrace.h" +#include "kern_util.h" +#include "skas/mm_id.h" #define OS_TYPE_FILE 1 #define OS_TYPE_DIR 2 @@ -188,11 +191,12 @@ extern int os_protect_memory(void *addr, unsigned long len, int r, int w, int x); extern int os_unmap_memory(void *addr, int len); extern void os_flush_stdout(void); -extern unsigned long long os_usecs(void); /* tt.c * for tt mode only (will be deleted in future...) */ +extern void stop(void); +extern int wait_for_stop(int pid, int sig, int cont_type, void *relay); extern int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, int must_succeed); extern void forward_pending_sigio(int target); @@ -213,15 +217,78 @@ extern int run_helper_thread(int (*proc)(void *), void *arg, int stack_order); extern int helper_wait(int pid); -#endif +/* umid.c */ -/* - * 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: - */ +extern int umid_file_name(char *name, char *buf, int len); +extern int set_umid(char *name); +extern char *get_umid(void); + +/* signal.c */ +extern void set_sigstack(void *sig_stack, int size); +extern void remove_sigstack(void); +extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern int change_sig(int signal, int on); +extern void block_signals(void); +extern void unblock_signals(void); +extern int get_signals(void); +extern int set_signals(int enable); +extern void os_usr1_signal(int on); + +/* trap.c */ +extern void os_fill_handlinfo(struct kern_handlers h); +extern void do_longjmp(void *p, int val); + +/* util.c */ +extern void stack_protections(unsigned long address); +extern void task_protections(unsigned long address); +extern int raw(int fd); +extern void setup_machinename(char *machine_out); +extern void setup_hostinfo(void); +extern int setjmp_wrapper(void (*proc)(void *, void *), ...); + +/* time.c */ +#define BILLION (1000 * 1000 * 1000) + +extern void switch_timers(int to_real); +extern void idle_sleep(int secs); +extern void enable_timer(void); +extern void disable_timer(void); +extern void user_time_init(void); +extern void uml_idle_timer(void); +extern unsigned long long os_nsecs(void); + +/* skas/mem.c */ +extern long run_syscall_stub(struct mm_id * mm_idp, + int syscall, unsigned long *args, long expected, + void **addr, int done); +extern long syscall_stub_data(struct mm_id * mm_idp, + unsigned long *data, int data_count, + void **addr, void **stub_addr); +extern 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); +extern int unmap(struct mm_id * mm_idp, void *addr, unsigned long len, + int done, void **data); +extern int protect(struct mm_id * mm_idp, unsigned long addr, + unsigned long len, int r, int w, int x, int done, + void **data); + +/* skas/process.c */ +extern int is_skas_winch(int pid, int fd, void *data); +extern int start_userspace(unsigned long stub_stack); +extern int copy_context_skas0(unsigned long stack, int pid); +extern void userspace(union uml_pt_regs *regs); +extern void map_stub_pages(int fd, unsigned long code, + unsigned long data, unsigned long stack); +extern void new_thread(void *stack, void **switch_buf_ptr, + void **fork_buf_ptr, void (*handler)(int)); +extern void thread_wait(void *sw, void *fb); +extern void switch_threads(void *me, void *next); +extern int start_idle_thread(void *stack, void *switch_buf_ptr, + void **fork_buf_ptr); +extern void initial_thread_cb_skas(void (*proc)(void *), + void *arg); +extern void halt_skas(void); +extern void reboot_skas(void); + +#endif diff --git a/arch/um/include/signal_user.h b/arch/um/include/signal_user.h deleted file mode 100644 index b075e543d86..00000000000 --- a/arch/um/include/signal_user.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SIGNAL_USER_H__ -#define __SIGNAL_USER_H__ - -extern int signal_stack_size; - -extern int change_sig(int signal, int on); -extern void set_sigstack(void *stack, int size); -extern void set_handler(int sig, void (*handler)(int), int flags, ...); -extern int set_signals(int enable); -extern int get_signals(void); - -#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: - */ diff --git a/arch/um/kernel/skas/include/mm_id.h b/arch/um/include/skas/mm_id.h index 48dd0989dda..48dd0989dda 100644 --- a/arch/um/kernel/skas/include/mm_id.h +++ b/arch/um/include/skas/mm_id.h diff --git a/arch/um/include/skas/mmu-skas.h b/arch/um/include/skas/mmu-skas.h new file mode 100644 index 00000000000..d8869a6ef1b --- /dev/null +++ b/arch/um/include/skas/mmu-skas.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_MMU_H +#define __SKAS_MMU_H + +#include "linux/config.h" +#include "mm_id.h" +#include "asm/ldt.h" + +struct mmu_context_skas { + struct mm_id id; + unsigned long last_page_table; +#ifdef CONFIG_3_LEVEL_PGTABLES + unsigned long last_pmd; +#endif + uml_ldt_t ldt; +}; + +extern void switch_mm_skas(struct mm_id * mm_idp); + +#endif diff --git a/arch/um/include/skas/mode-skas.h b/arch/um/include/skas/mode-skas.h new file mode 100644 index 00000000000..260065cfeef --- /dev/null +++ b/arch/um/include/skas/mode-skas.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MODE_SKAS_H__ +#define __MODE_SKAS_H__ + +#include <sysdep/ptrace.h> + +extern unsigned long exec_regs[]; +extern unsigned long exec_fp_regs[]; +extern unsigned long exec_fpx_regs[]; +extern int have_fpx_regs; + +extern void sig_handler_common_skas(int sig, void *sc_ptr); +extern void kill_off_processes_skas(void); + +#endif diff --git a/arch/um/kernel/skas/include/mode_kern-skas.h b/arch/um/include/skas/mode_kern_skas.h index c97a80dfe37..63c58739bde 100644 --- a/arch/um/kernel/skas/include/mode_kern-skas.h +++ b/arch/um/include/skas/mode_kern_skas.h @@ -18,7 +18,6 @@ extern int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp, unsigned long stack_top, struct task_struct *p, struct pt_regs *regs); extern void release_thread_skas(struct task_struct *task); -extern void initial_thread_cb_skas(void (*proc)(void *), void *arg); extern void init_idle_skas(void); extern void flush_tlb_kernel_range_skas(unsigned long start, unsigned long end); @@ -39,14 +38,3 @@ extern int thread_pid_skas(struct task_struct *task); #define kmem_end_skas (host_task_size - 1024 * 1024) #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: - */ diff --git a/arch/um/kernel/skas/include/proc_mm.h b/arch/um/include/skas/proc_mm.h index cce61a67905..90280920960 100644 --- a/arch/um/kernel/skas/include/proc_mm.h +++ b/arch/um/include/skas/proc_mm.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -22,13 +22,13 @@ struct mm_mmap { struct mm_munmap { unsigned long addr; - unsigned long len; + unsigned long len; }; struct mm_mprotect { unsigned long addr; unsigned long len; - unsigned int prot; + unsigned int prot; }; struct proc_mm_op { @@ -42,14 +42,3 @@ struct proc_mm_op { }; #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: - */ diff --git a/arch/um/include/skas/skas.h b/arch/um/include/skas/skas.h new file mode 100644 index 00000000000..86357282d68 --- /dev/null +++ b/arch/um/include/skas/skas.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_H +#define __SKAS_H + +#include "mm_id.h" +#include "sysdep/ptrace.h" + +extern int userspace_pid[]; +extern int proc_mm, ptrace_faultinfo, ptrace_ldt; +extern int skas_needs_stub; + +extern int user_thread(unsigned long stack, int flags); +extern void new_thread_proc(void *stack, void (*handler)(int sig)); +extern void new_thread_handler(int sig); +extern void handle_syscall(union uml_pt_regs *regs); +extern void user_signal(int sig, union uml_pt_regs *regs, int pid); +extern int new_mm(unsigned long stack); +extern void get_skas_faultinfo(int pid, struct faultinfo * fi); +extern long execute_syscall_skas(void *r); +extern unsigned long current_stub_stack(void); + +#endif diff --git a/arch/um/kernel/skas/include/stub-data.h b/arch/um/include/skas/stub-data.h index f6ed92c3727..f6ed92c3727 100644 --- a/arch/um/kernel/skas/include/stub-data.h +++ b/arch/um/include/skas/stub-data.h diff --git a/arch/um/kernel/skas/include/uaccess-skas.h b/arch/um/include/skas/uaccess-skas.h index 64516c556cd..224a75f4c02 100644 --- a/arch/um/kernel/skas/include/uaccess-skas.h +++ b/arch/um/include/skas/uaccess-skas.h @@ -19,14 +19,3 @@ extern int clear_user_skas(void __user *mem, int len); extern int strnlen_user_skas(const void __user *str, int len); #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: - */ diff --git a/arch/um/include/sysdep-i386/checksum.h b/arch/um/include/sysdep-i386/checksum.h index 764ba4db478..7d3d202d7ff 100644 --- a/arch/um/include/sysdep-i386/checksum.h +++ b/arch/um/include/sysdep-i386/checksum.h @@ -36,7 +36,7 @@ unsigned int csum_partial_copy_nocheck(const unsigned char *src, unsigned char * int len, int sum) { memcpy(dst, src, len); - return(csum_partial(dst, len, sum)); + return csum_partial(dst, len, sum); } /* @@ -104,7 +104,7 @@ static inline unsigned short ip_fast_csum(unsigned char * iph, : "=r" (sum), "=r" (iph), "=r" (ihl) : "1" (iph), "2" (ihl) : "memory"); - return(sum); + return sum; } /* diff --git a/arch/um/sys-i386/kernel-offsets.c b/arch/um/include/sysdep-i386/kernel-offsets.h index 35db8505750..82f96c57414 100644 --- a/arch/um/sys-i386/kernel-offsets.c +++ b/arch/um/include/sysdep-i386/kernel-offsets.h @@ -1,12 +1,9 @@ -#include <linux/config.h> #include <linux/stddef.h> #include <linux/sched.h> -#include <linux/time.h> #include <linux/elf.h> -#include <asm/page.h> #define DEFINE(sym, val) \ - asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) #define STR(x) #x #define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " STR(val) " " #val: : ) diff --git a/arch/um/sys-x86_64/kernel-offsets.c b/arch/um/include/sysdep-x86_64/kernel-offsets.h index bfcb104b846..5ce93abd0b5 100644 --- a/arch/um/sys-x86_64/kernel-offsets.c +++ b/arch/um/include/sysdep-x86_64/kernel-offsets.h @@ -6,7 +6,7 @@ #include <asm/page.h> #define DEFINE(sym, val) \ - asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) #define DEFINE_STR1(x) #x #define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " DEFINE_STR1(val) " " #val: : ) diff --git a/arch/um/include/time_user.h b/arch/um/include/time_user.h deleted file mode 100644 index 17d7ef2141f..00000000000 --- a/arch/um/include/time_user.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __TIME_USER_H__ -#define __TIME_USER_H__ - -extern void timer(void); -extern void switch_timers(int to_real); -extern void idle_sleep(int secs); -extern void enable_timer(void); -extern void prepare_timer(void * ptr); -extern void disable_timer(void); -extern unsigned long time_lock(void); -extern void time_unlock(unsigned long); -extern void user_time_init(void); - -#endif diff --git a/arch/um/kernel/tt/include/debug.h b/arch/um/include/tt/debug.h index 738435461e1..9778fa83829 100644 --- a/arch/um/kernel/tt/include/debug.h +++ b/arch/um/include/tt/debug.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) and * Lars Brinkhoff. * Licensed under the GPL diff --git a/arch/um/include/tt/mmu-tt.h b/arch/um/include/tt/mmu-tt.h new file mode 100644 index 00000000000..572a78b2258 --- /dev/null +++ b/arch/um/include/tt/mmu-tt.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TT_MMU_H +#define __TT_MMU_H + +struct mmu_context_tt { +}; + +#endif diff --git a/arch/um/include/tt/mode-tt.h b/arch/um/include/tt/mode-tt.h new file mode 100644 index 00000000000..2823cd56eea --- /dev/null +++ b/arch/um/include/tt/mode-tt.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MODE_TT_H__ +#define __MODE_TT_H__ + +#include "sysdep/ptrace.h" + +enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB }; + +extern int tracing_pid; + +extern int tracer(int (*init_proc)(void *), void *sp); +extern void sig_handler_common_tt(int sig, void *sc); +extern void syscall_handler_tt(int sig, union uml_pt_regs *regs); +extern void reboot_tt(void); +extern void halt_tt(void); +extern int is_tracer_winch(int pid, int fd, void *data); +extern void kill_off_processes_tt(void); + +#endif diff --git a/arch/um/include/tt/mode_kern_tt.h b/arch/um/include/tt/mode_kern_tt.h new file mode 100644 index 00000000000..efa0012550d --- /dev/null +++ b/arch/um/include/tt/mode_kern_tt.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TT_MODE_KERN_H__ +#define __TT_MODE_KERN_H__ + +#include "linux/sched.h" +#include "asm/page.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" + +extern void switch_to_tt(void *prev, void *next); +extern void flush_thread_tt(void); +extern void start_thread_tt(struct pt_regs *regs, unsigned long eip, + unsigned long esp); +extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp, + unsigned long stack_top, struct task_struct *p, + struct pt_regs *regs); +extern void release_thread_tt(struct task_struct *task); +extern void initial_thread_cb_tt(void (*proc)(void *), void *arg); +extern void init_idle_tt(void); +extern void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end); +extern void flush_tlb_kernel_vm_tt(void); +extern void __flush_tlb_one_tt(unsigned long addr); +extern void flush_tlb_range_tt(struct vm_area_struct *vma, + unsigned long start, unsigned long end); +extern void flush_tlb_mm_tt(struct mm_struct *mm); +extern void force_flush_all_tt(void); +extern long execute_syscall_tt(void *r); +extern void before_mem_tt(unsigned long brk_start); +extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out, + unsigned long *task_size_out); +extern int start_uml_tt(void); +extern int external_pid_tt(struct task_struct *task); +extern int thread_pid_tt(struct task_struct *task); + +#define kmem_end_tt (host_task_size - ABOVE_KMEM) + +#endif diff --git a/arch/um/kernel/tt/include/tt.h b/arch/um/include/tt/tt.h index c667b67af40..80852198018 100644 --- a/arch/um/kernel/tt/include/tt.h +++ b/arch/um/include/tt/tt.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -34,13 +34,3 @@ extern long execute_syscall_tt(void *r); #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: - */ diff --git a/arch/um/kernel/tt/include/uaccess-tt.h b/arch/um/include/tt/uaccess-tt.h index b9bfe9c481c..b19645f32f2 100644 --- a/arch/um/kernel/tt/include/uaccess-tt.h +++ b/arch/um/include/tt/uaccess-tt.h @@ -46,14 +46,3 @@ extern int clear_user_tt(void __user *mem, int len); extern int strnlen_user_tt(const void __user *str, int len); #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: - */ diff --git a/arch/um/include/user.h b/arch/um/include/user.h index 0f865ef4691..91b0ac4ad88 100644 --- a/arch/um/include/user.h +++ b/arch/um/include/user.h @@ -18,6 +18,7 @@ extern int open_gdb_chan(void); extern unsigned long strlcpy(char *, const char *, unsigned long); extern unsigned long strlcat(char *, const char *, unsigned long); extern void *um_vmalloc(int size); +extern void *um_vmalloc_atomic(int size); extern void vfree(void *ptr); #endif diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h index bb505e01d99..a6f1f176cf8 100644 --- a/arch/um/include/user_util.h +++ b/arch/um/include/user_util.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -23,12 +23,7 @@ struct cpu_task { extern struct cpu_task cpu_tasks[]; -struct signal_info { - void (*handler)(int, union uml_pt_regs *); - int is_irq; -}; - -extern struct signal_info sig_info[]; +extern void (*sig_info[])(int, union uml_pt_regs *); extern unsigned long low_physmem; extern unsigned long high_physmem; @@ -49,10 +44,6 @@ extern unsigned long brk_start; extern int pty_output_sigio; extern int pty_close_sigio; -extern void stop(void); -extern void stack_protections(unsigned long address); -extern void task_protections(unsigned long address); -extern int wait_for_stop(int pid, int sig, int cont_type, void *relay); extern void *add_signal_handler(int sig, void (*handler)(int)); extern int linux_main(int argc, char **argv); extern void set_cmdline(char *cmd); @@ -60,12 +51,8 @@ extern void input_cb(void (*proc)(void *), void *arg, int arg_len); extern int get_pty(void); extern void *um_kmalloc(int size); extern int switcheroo(int fd, int prot, void *from, void *to, int size); -extern void setup_machinename(char *machine_out); -extern void setup_hostinfo(void); extern void do_exec(int old_pid, int new_pid); extern void tracer_panic(char *msg, ...); -extern char *get_umid(int only_if_set); -extern void do_longjmp(void *p, int val); extern int detach(int pid, int sig); extern int attach(int pid); extern void kill_child_dead(int pid); @@ -77,18 +64,6 @@ extern int cpu_feature(char *what, char *buf, int len); extern int arch_handle_signal(int sig, union uml_pt_regs *regs); extern int arch_fixup(unsigned long address, void *sc_ptr); extern void arch_init_thread(void); -extern int setjmp_wrapper(void (*proc)(void *, void *), ...); extern int raw(int fd); #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: - */ diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index 3de9d21e36b..693018ba80f 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -9,9 +9,8 @@ clean-files := obj-y = config.o exec_kern.o exitcode.o \ init_task.o irq.o irq_user.o ksyms.o mem.o physmem.o \ process_kern.o ptrace.o reboot.o resource.o sigio_user.o sigio_kern.o \ - signal_kern.o signal_user.o smp.o syscall_kern.o sysrq.o time.o \ - time_kern.o tlb.o trap_kern.o trap_user.o uaccess.o um_arch.o \ - umid.o user_util.o + signal_kern.o smp.o syscall_kern.o sysrq.o \ + time_kern.o tlb.o trap_kern.o uaccess.o um_arch.o umid.o obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o obj-$(CONFIG_GPROF) += gprof_syms.o @@ -24,7 +23,7 @@ obj-$(CONFIG_MODE_SKAS) += skas/ user-objs-$(CONFIG_TTY_LOG) += tty_log.o -USER_OBJS := $(user-objs-y) config.o time.o tty_log.o umid.o user_util.o +USER_OBJS := $(user-objs-y) config.o tty_log.o include arch/um/scripts/Makefile.rules diff --git a/arch/um/kernel/asm-offsets.c b/arch/um/kernel/asm-offsets.c index c13a64a288f..91ea538e161 100644 --- a/arch/um/kernel/asm-offsets.c +++ b/arch/um/kernel/asm-offsets.c @@ -1 +1 @@ -/* Dummy file to make kbuild happy - unused! */ +#include "sysdep/kernel-offsets.h" diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c index efd222ffe20..569fe8b9b05 100644 --- a/arch/um/kernel/exec_kern.c +++ b/arch/um/kernel/exec_kern.c @@ -17,7 +17,6 @@ #include "irq_user.h" #include "tlb.h" #include "os.h" -#include "time_user.h" #include "choose-mode.h" #include "mode_kern.h" diff --git a/arch/um/kernel/irq_user.c b/arch/um/kernel/irq_user.c index c3ccaf24f3e..0e32f5f4a88 100644 --- a/arch/um/kernel/irq_user.c +++ b/arch/um/kernel/irq_user.c @@ -15,7 +15,6 @@ #include "kern_util.h" #include "user.h" #include "process.h" -#include "signal_user.h" #include "sigio.h" #include "irq_user.h" #include "os.h" @@ -29,7 +28,6 @@ struct irq_fd { int pid; int events; int current_events; - int freed; }; static struct irq_fd *active_fds = NULL; @@ -41,9 +39,11 @@ static int pollfds_size = 0; extern int io_count, intr_count; +extern void free_irqs(void); + void sigio_handler(int sig, union uml_pt_regs *regs) { - struct irq_fd *irq_fd, *next; + struct irq_fd *irq_fd; int i, n; if(smp_sigio_handler()) return; @@ -66,29 +66,15 @@ void sigio_handler(int sig, union uml_pt_regs *regs) irq_fd = irq_fd->next; } - for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){ - next = irq_fd->next; + for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ if(irq_fd->current_events != 0){ irq_fd->current_events = 0; do_IRQ(irq_fd->irq, regs); - - /* This is here because the next irq may be - * freed in the handler. If a console goes - * away, both the read and write irqs will be - * freed. After do_IRQ, ->next will point to - * a good IRQ. - * Irqs can't be freed inside their handlers, - * so the next best thing is to have them - * marked as needing freeing, so that they - * can be freed here. - */ - next = irq_fd->next; - if(irq_fd->freed){ - free_irq(irq_fd->irq, irq_fd->id); - } } } } + + free_irqs(); } int activate_ipi(int fd, int pid) @@ -136,8 +122,7 @@ int activate_fd(int irq, int fd, int type, void *dev_id) .irq = irq, .pid = pid, .events = events, - .current_events = 0, - .freed = 0 } ); + .current_events = 0 } ); /* Critical section - locked by a spinlock because this stuff can * be changed from interrupt handlers. The stuff above is done @@ -313,26 +298,6 @@ static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) return(irq); } -void free_irq_later(int irq, void *dev_id) -{ - struct irq_fd *irq_fd; - unsigned long flags; - - flags = irq_lock(); - for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ - if((irq_fd->irq == irq) && (irq_fd->id == dev_id)) - break; - } - if(irq_fd == NULL){ - printk("free_irq_later found no irq, irq = %d, " - "dev_id = 0x%p\n", irq, dev_id); - goto out; - } - irq_fd->freed = 1; - out: - irq_unlock(flags); -} - void reactivate_fd(int fd, int irqnum) { struct irq_fd *irq; diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c index 34b54a3e213..3113cab8675 100644 --- a/arch/um/kernel/process_kern.c +++ b/arch/um/kernel/process_kern.c @@ -36,11 +36,9 @@ #include "kern_util.h" #include "kern.h" #include "signal_kern.h" -#include "signal_user.h" #include "init.h" #include "irq_user.h" #include "mem_user.h" -#include "time_user.h" #include "tlb.h" #include "frame_kern.h" #include "sigcontext.h" @@ -108,7 +106,7 @@ void set_current(void *t) { struct task_struct *task = t; - cpu_tasks[task->thread_info->cpu] = ((struct cpu_task) + cpu_tasks[task_thread_info(task)->cpu] = ((struct cpu_task) { external_pid(task), task }); } @@ -289,17 +287,27 @@ EXPORT_SYMBOL(disable_hlt); void *um_kmalloc(int size) { - return(kmalloc(size, GFP_KERNEL)); + return kmalloc(size, GFP_KERNEL); } void *um_kmalloc_atomic(int size) { - return(kmalloc(size, GFP_ATOMIC)); + return kmalloc(size, GFP_ATOMIC); } void *um_vmalloc(int size) { - return(vmalloc(size)); + return vmalloc(size); +} + +void *um_vmalloc_atomic(int size) +{ + return __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM, PAGE_KERNEL); +} + +int __cant_sleep(void) { + return in_atomic() || irqs_disabled() || in_interrupt(); + /* Is in_interrupt() really needed? */ } unsigned long get_fault_addr(void) @@ -324,10 +332,6 @@ int user_context(unsigned long sp) return(stack != (unsigned long) current_thread); } -extern void remove_umid_dir(void); - -__uml_exitcall(remove_umid_dir); - extern exitcall_t __uml_exitcall_begin, __uml_exitcall_end; void do_uml_exitcalls(void) @@ -375,11 +379,6 @@ int smp_sigio_handler(void) return(0); } -int um_in_interrupt(void) -{ - return(in_interrupt()); -} - int cpu(void) { return(current_thread->cpu); diff --git a/arch/um/kernel/reboot.c b/arch/um/kernel/reboot.c index a637e885c58..6f1a3a28811 100644 --- a/arch/um/kernel/reboot.c +++ b/arch/um/kernel/reboot.c @@ -12,6 +12,8 @@ #include "mode.h" #include "choose-mode.h" +void (*pm_power_off)(void); + #ifdef CONFIG_SMP static void kill_idlers(int me) { diff --git a/arch/um/kernel/sigio_user.c b/arch/um/kernel/sigio_user.c index 48b1f644b9a..f7b18e157d3 100644 --- a/arch/um/kernel/sigio_user.c +++ b/arch/um/kernel/sigio_user.c @@ -216,6 +216,8 @@ static int write_sigio_thread(void *unused) "err = %d\n", -n); } } + + return 0; } static int need_poll(int n) @@ -335,70 +337,103 @@ int ignore_sigio_fd(int fd) return(err); } -static int setup_initial_poll(int fd) +static struct pollfd* setup_initial_poll(int fd) { struct pollfd *p; - p = um_kmalloc_atomic(sizeof(struct pollfd)); - if(p == NULL){ + p = um_kmalloc(sizeof(struct pollfd)); + if (p == NULL) { printk("setup_initial_poll : failed to allocate poll\n"); - return(-1); + return NULL; } *p = ((struct pollfd) { .fd = fd, .events = POLLIN, .revents = 0 }); - current_poll = ((struct pollfds) { .poll = p, - .used = 1, - .size = 1 }); - return(0); + return p; } 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(); - if(write_sigio_pid != -1) - goto out; + l_write_sigio_pid = write_sigio_pid; + sigio_unlock(); + + if (l_write_sigio_pid != -1) + return; - err = os_pipe(write_sigio_fds, 1, 1); + err = os_pipe(l_write_sigio_fds, 1, 1); if(err < 0){ printk("write_sigio_workaround - os_pipe 1 failed, " "err = %d\n", -err); - goto out; + return; } - err = os_pipe(sigio_private, 1, 1); + err = os_pipe(l_sigio_private, 1, 1); if(err < 0){ - printk("write_sigio_workaround - os_pipe 2 failed, " + printk("write_sigio_workaround - os_pipe 1 failed, " "err = %d\n", -err); goto out_close1; } - if(setup_initial_poll(sigio_private[1])) + + p = setup_initial_poll(l_sigio_private[1]); + if(!p) goto out_close2; - write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, + 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_unlock; + + write_sigio_pid = run_helper_thread(write_sigio_thread, NULL, CLONE_FILES | CLONE_VM, &stack, 0); - if(write_sigio_pid < 0) goto out_close2; + if (write_sigio_pid < 0) + goto out_clear; - if(write_sigio_irq(write_sigio_fds[0])) + if (write_sigio_irq(l_write_sigio_fds[0])) goto out_kill; - out: + /* Success, finally. */ + memcpy(write_sigio_fds, l_write_sigio_fds, sizeof(l_write_sigio_fds)); + memcpy(sigio_private, l_sigio_private, sizeof(l_sigio_private)); + + current_poll = ((struct pollfds) { .poll = p, + .used = 1, + .size = 1 }); + sigio_unlock(); return; out_kill: - os_kill_process(write_sigio_pid, 1); + l_write_sigio_pid = write_sigio_pid; + write_sigio_pid = -1; + sigio_unlock(); + /* Going to call waitpid, avoid holding the lock. */ + os_kill_process(l_write_sigio_pid, 1); + goto out_free; + + out_clear: write_sigio_pid = -1; + out_unlock: + sigio_unlock(); + out_free: + kfree(p); out_close2: - os_close_file(sigio_private[0]); - os_close_file(sigio_private[1]); + os_close_file(l_sigio_private[0]); + os_close_file(l_sigio_private[1]); out_close1: - os_close_file(write_sigio_fds[0]); - os_close_file(write_sigio_fds[1]); - sigio_unlock(); + os_close_file(l_write_sigio_fds[0]); + os_close_file(l_write_sigio_fds[1]); + return; } int read_sigio_fd(int fd) diff --git a/arch/um/kernel/signal_kern.c b/arch/um/kernel/signal_kern.c index 03618bd13d5..da17b7541e0 100644 --- a/arch/um/kernel/signal_kern.c +++ b/arch/um/kernel/signal_kern.c @@ -22,7 +22,6 @@ #include "asm/ucontext.h" #include "kern_util.h" #include "signal_kern.h" -#include "signal_user.h" #include "kern.h" #include "frame_kern.h" #include "sigcontext.h" @@ -100,31 +99,46 @@ static int handle_signal(struct pt_regs *regs, unsigned long signr, return err; } -static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset) +static int kern_do_signal(struct pt_regs *regs) { struct k_sigaction ka_copy; siginfo_t info; + sigset_t *oldset; int sig, handled_sig = 0; + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else + oldset = ¤t->blocked; + while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){ handled_sig = 1; /* Whee! Actually deliver the signal. */ - if(!handle_signal(regs, sig, &ka_copy, &info, oldset)) + if(!handle_signal(regs, sig, &ka_copy, &info, oldset)){ + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); break; + } } /* Did we come from a system call? */ if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){ /* Restart the system call - no handlers present */ - if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND || - PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS || - PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){ + switch(PT_REGS_SYSCALL_RET(regs)){ + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); PT_REGS_RESTART_SYSCALL(regs); - } - else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){ + break; + case -ERESTART_RESTARTBLOCK: PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall; PT_REGS_RESTART_SYSCALL(regs); + break; } } @@ -138,12 +152,19 @@ static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset) if(current->ptrace & PT_DTRACE) current->thread.singlestep_syscall = is_syscall(PT_REGS_IP(¤t->thread.regs)); + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } return(handled_sig); } int do_signal(void) { - return(kern_do_signal(¤t->thread.regs, ¤t->blocked)); + return(kern_do_signal(¤t->thread.regs)); } /* @@ -151,63 +172,20 @@ int do_signal(void) */ long sys_sigsuspend(int history0, int history1, old_sigset_t mask) { - sigset_t saveset; - mask &= _BLOCKABLE; spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if(kern_do_signal(¤t->thread.regs, &saveset)) - return(-EINTR); - } -} - -long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); - - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR; - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (kern_do_signal(¤t->thread.regs, &saveset)) - return(-EINTR); - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) { return(do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs))); } - -/* - * 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: - */ diff --git a/arch/um/kernel/signal_user.c b/arch/um/kernel/signal_user.c deleted file mode 100644 index 62f457835fb..00000000000 --- a/arch/um/kernel/signal_user.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <signal.h> -#include <errno.h> -#include <stdarg.h> -#include <string.h> -#include <sys/mman.h> -#include "user_util.h" -#include "kern_util.h" -#include "user.h" -#include "signal_user.h" -#include "signal_kern.h" -#include "sysdep/sigcontext.h" -#include "sigcontext.h" - -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 set_handler(int sig, void (*handler)(int), int flags, ...) -{ - struct sigaction action; - va_list ap; - int mask; - - va_start(ap, flags); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - 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"); -} - -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)); -} - -/* Both here and in set/get_signal we don't touch SIGPROF, because we must not - * disable profiling; it's safe because the profiling code does not interact - * with the kernel code at all.*/ - -static void change_signals(int type) -{ - sigset_t mask; - - sigemptyset(&mask); - sigaddset(&mask, SIGVTALRM); - sigaddset(&mask, SIGALRM); - sigaddset(&mask, SIGIO); - if(sigprocmask(type, &mask, NULL) < 0) - panic("Failed to change signal mask - errno = %d", errno); -} - -void block_signals(void) -{ - change_signals(SIG_BLOCK); -} - -void unblock_signals(void) -{ - change_signals(SIG_UNBLOCK); -} - -/* 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 SIGVTALRM_BIT 1 - -static int enable_mask(sigset_t *mask) -{ - int sigs; - - sigs = sigismember(mask, SIGIO) ? 0 : 1 << SIGIO_BIT; - sigs |= sigismember(mask, SIGVTALRM) ? 0 : 1 << SIGVTALRM_BIT; - sigs |= sigismember(mask, SIGALRM) ? 0 : 1 << SIGVTALRM_BIT; - return(sigs); -} - -int get_signals(void) -{ - sigset_t mask; - - if(sigprocmask(SIG_SETMASK, NULL, &mask) < 0) - panic("Failed to get signal mask"); - return(enable_mask(&mask)); -} - -int set_signals(int enable) -{ - sigset_t mask; - int ret; - - sigemptyset(&mask); - if(enable & (1 << SIGIO_BIT)) - sigaddset(&mask, SIGIO); - if(enable & (1 << SIGVTALRM_BIT)){ - sigaddset(&mask, SIGVTALRM); - sigaddset(&mask, SIGALRM); - } - - /* This is safe - sigprocmask is guaranteed to copy locally the - * value of new_set, do his work and then, at the end, write to - * old_set. - */ - if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) - panic("Failed to enable signals"); - ret = enable_mask(&mask); - sigemptyset(&mask); - if((enable & (1 << SIGIO_BIT)) == 0) - sigaddset(&mask, SIGIO); - if((enable & (1 << SIGVTALRM_BIT)) == 0){ - sigaddset(&mask, SIGVTALRM); - sigaddset(&mask, SIGALRM); - } - if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) - panic("Failed to block signals"); - - return(ret); -} - -/* - * 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: - */ diff --git a/arch/um/kernel/skas/Makefile b/arch/um/kernel/skas/Makefile index 8de471b59c1..57181a920d4 100644 --- a/arch/um/kernel/skas/Makefile +++ b/arch/um/kernel/skas/Makefile @@ -1,12 +1,12 @@ -# +# # Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com) # Licensed under the GPL # -obj-y := clone.o exec_kern.o mem.o mem_user.o mmu.o process.o process_kern.o \ - syscall.o tlb.o trap_user.o uaccess.o +obj-y := clone.o exec_kern.o mem.o mmu.o process_kern.o \ + syscall.o tlb.o uaccess.o -USER_OBJS := process.o clone.o +USER_OBJS := clone.o include arch/um/scripts/Makefile.rules diff --git a/arch/um/kernel/skas/include/mmu-skas.h b/arch/um/kernel/skas/include/mmu-skas.h deleted file mode 100644 index 44110c521e4..00000000000 --- a/arch/um/kernel/skas/include/mmu-skas.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SKAS_MMU_H -#define __SKAS_MMU_H - -#include "linux/config.h" -#include "mm_id.h" -#include "asm/ldt.h" - -struct mmu_context_skas { - struct mm_id id; - unsigned long last_page_table; -#ifdef CONFIG_3_LEVEL_PGTABLES - unsigned long last_pmd; -#endif - uml_ldt_t ldt; -}; - -extern void switch_mm_skas(struct mm_id * mm_idp); - -#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: - */ diff --git a/arch/um/kernel/skas/include/mode-skas.h b/arch/um/kernel/skas/include/mode-skas.h deleted file mode 100644 index bcd26a6a388..00000000000 --- a/arch/um/kernel/skas/include/mode-skas.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __MODE_SKAS_H__ -#define __MODE_SKAS_H__ - -#include <sysdep/ptrace.h> - -extern unsigned long exec_regs[]; -extern unsigned long exec_fp_regs[]; -extern unsigned long exec_fpx_regs[]; -extern int have_fpx_regs; - -extern void sig_handler_common_skas(int sig, void *sc_ptr); -extern void halt_skas(void); -extern void reboot_skas(void); -extern void kill_off_processes_skas(void); -extern int is_skas_winch(int pid, int fd, void *data); - -#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: - */ diff --git a/arch/um/kernel/skas/include/skas.h b/arch/um/kernel/skas/include/skas.h deleted file mode 100644 index daa2f85b684..00000000000 --- a/arch/um/kernel/skas/include/skas.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SKAS_H -#define __SKAS_H - -#include "mm_id.h" -#include "sysdep/ptrace.h" - -extern int userspace_pid[]; -extern int proc_mm, ptrace_faultinfo, ptrace_ldt; -extern int skas_needs_stub; - -extern void switch_threads(void *me, void *next); -extern void thread_wait(void *sw, void *fb); -extern void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, - void (*handler)(int)); -extern int start_idle_thread(void *stack, void *switch_buf_ptr, - void **fork_buf_ptr); -extern int user_thread(unsigned long stack, int flags); -extern void userspace(union uml_pt_regs *regs); -extern void new_thread_proc(void *stack, void (*handler)(int sig)); -extern void remove_sigstack(void); -extern void new_thread_handler(int sig); -extern void handle_syscall(union uml_pt_regs *regs); -extern 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); -extern int unmap(struct mm_id * mm_idp, void *addr, unsigned long len, - int done, void **data); -extern int protect(struct mm_id * mm_idp, unsigned long addr, - unsigned long len, int r, int w, int x, int done, - void **data); -extern void user_signal(int sig, union uml_pt_regs *regs, int pid); -extern int new_mm(int from, unsigned long stack); -extern int start_userspace(unsigned long stub_stack); -extern int copy_context_skas0(unsigned long stack, int pid); -extern void get_skas_faultinfo(int pid, struct faultinfo * fi); -extern long execute_syscall_skas(void *r); -extern unsigned long current_stub_stack(void); -extern long run_syscall_stub(struct mm_id * mm_idp, - int syscall, unsigned long *args, long expected, - void **addr, int done); -extern long syscall_stub_data(struct mm_id * mm_idp, - unsigned long *data, int data_count, - void **addr, void **stub_addr); - -#endif diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 677871f1b37..c5c9885a829 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -78,7 +78,7 @@ int init_new_context_skas(struct task_struct *task, struct mm_struct *mm) struct mmu_context_skas *from_mm = NULL; struct mmu_context_skas *to_mm = &mm->context.skas; unsigned long stack = 0; - int from_fd, ret = -ENOMEM; + int ret = -ENOMEM; if(skas_needs_stub){ stack = get_zeroed_page(GFP_KERNEL); @@ -108,11 +108,7 @@ int init_new_context_skas(struct task_struct *task, struct mm_struct *mm) from_mm = ¤t->mm->context.skas; if(proc_mm){ - if(from_mm) - from_fd = from_mm->id.u.mm_fd; - else from_fd = -1; - - ret = new_mm(from_fd, stack); + ret = new_mm(stack); if(ret < 0){ printk("init_new_context_skas - new_mm failed, " "errno = %d\n", ret); diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index 599d679bd4f..eea1c9c4bb0 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -18,7 +18,6 @@ #include <asm/types.h> #include "user.h" #include "ptrace_user.h" -#include "time_user.h" #include "sysdep/ptrace.h" #include "user_util.h" #include "kern_util.h" @@ -31,7 +30,6 @@ #include "proc_mm.h" #include "skas_ptrace.h" #include "chan_user.h" -#include "signal_user.h" #include "registers.h" #include "mem.h" #include "uml-config.h" @@ -69,7 +67,7 @@ void wait_stub_done(int pid, int sig, char * fname) if((n < 0) || !WIFSTOPPED(status) || (WSTOPSIG(status) != SIGUSR1 && WSTOPSIG(status) != SIGTRAP)){ - unsigned long regs[FRAME_SIZE]; + unsigned long regs[HOST_FRAME_SIZE]; if(ptrace(PTRACE_GETREGS, pid, 0, regs) < 0) printk("Failed to get registers from stub, " "errno = %d\n", errno); @@ -77,7 +75,7 @@ void wait_stub_done(int pid, int sig, char * fname) int i; printk("Stub registers -\n"); - for(i = 0; i < FRAME_SIZE; i++) + for(i = 0; i < HOST_FRAME_SIZE; i++) printk("\t%d - %lx\n", i, regs[i]); } panic("%s : failed to wait for SIGUSR1/SIGTRAP, " @@ -514,16 +512,6 @@ int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) siglongjmp(**switch_buf, 1); } -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 initial_thread_cb_skas(void (*proc)(void *), void *arg) { sigjmp_buf here; diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c index 9c990253966..3f70a2e12f0 100644 --- a/arch/um/kernel/skas/process_kern.c +++ b/arch/um/kernel/skas/process_kern.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -13,15 +13,12 @@ #include "asm/uaccess.h" #include "asm/atomic.h" #include "kern_util.h" -#include "time_user.h" -#include "signal_user.h" #include "skas.h" #include "os.h" #include "user_util.h" #include "tlb.h" #include "kern.h" #include "mode.h" -#include "proc_mm.h" #include "registers.h" void switch_to_skas(void *prev, void *next) @@ -35,7 +32,7 @@ void switch_to_skas(void *prev, void *next) if(current->pid == 0) switch_timers(0); - switch_threads(&from->thread.mode.skas.switch_buf, + switch_threads(&from->thread.mode.skas.switch_buf, to->thread.mode.skas.switch_buf); if(current->pid == 0) @@ -51,8 +48,8 @@ void new_thread_handler(int sig) fn = current->thread.request.u.thread.proc; arg = current->thread.request.u.thread.arg; - change_sig(SIGUSR1, 1); - thread_wait(¤t->thread.mode.skas.switch_buf, + os_usr1_signal(1); + thread_wait(¤t->thread.mode.skas.switch_buf, current->thread.mode.skas.fork_buf); if(current->thread.prev_sched != NULL) @@ -83,8 +80,8 @@ void release_thread_skas(struct task_struct *task) void fork_handler(int sig) { - change_sig(SIGUSR1, 1); - thread_wait(¤t->thread.mode.skas.switch_buf, + os_usr1_signal(1); + thread_wait(¤t->thread.mode.skas.switch_buf, current->thread.mode.skas.fork_buf); force_flush_all(); @@ -94,13 +91,13 @@ void fork_handler(int sig) schedule_tail(current->thread.prev_sched); current->thread.prev_sched = NULL; - /* Handle any immediate reschedules or signals */ +/* Handle any immediate reschedules or signals */ interrupt_end(); userspace(¤t->thread.regs.regs); } int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp, - unsigned long stack_top, struct task_struct * p, + unsigned long stack_top, struct task_struct * p, struct pt_regs *regs) { void (*handler)(int); @@ -119,32 +116,19 @@ int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp, handler = new_thread_handler; } - new_thread(p->thread_info, &p->thread.mode.skas.switch_buf, + new_thread(task_stack_page(p), &p->thread.mode.skas.switch_buf, &p->thread.mode.skas.fork_buf, handler); return(0); } -extern void map_stub_pages(int fd, unsigned long code, - unsigned long data, unsigned long stack); -int new_mm(int from, unsigned long stack) +int new_mm(unsigned long stack) { - struct proc_mm_op copy; - int n, fd; + int fd; fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0); if(fd < 0) return(fd); - if(from != -1){ - copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS, - .u = - { .copy_segments = from } } ); - n = os_write_file(fd, ©, sizeof(copy)); - if(n != sizeof(copy)) - printk("new_mm : /proc/mm copy_segments failed, " - "err = %d\n", -n); - } - if(skas_needs_stub) map_stub_pages(fd, CONFIG_STUB_CODE, CONFIG_STUB_DATA, stack); @@ -186,7 +170,7 @@ int start_uml_skas(void) init_task.thread.request.u.thread.proc = start_kernel_proc; init_task.thread.request.u.thread.arg = NULL; - return(start_idle_thread(init_task.thread_info, + return(start_idle_thread(task_stack_page(&init_task), &init_task.thread.mode.skas.switch_buf, &init_task.thread.mode.skas.fork_buf)); } diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c index a5a47528dec..5992c325716 100644 --- a/arch/um/kernel/skas/uaccess.c +++ b/arch/um/kernel/skas/uaccess.c @@ -13,7 +13,7 @@ #include "asm/pgtable.h" #include "asm/uaccess.h" #include "kern_util.h" -#include "user_util.h" +#include "os.h" extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, pte_t *pte_out); diff --git a/arch/um/kernel/syscall.c b/arch/um/kernel/syscall.c index 1429c131879..1731d90e685 100644 --- a/arch/um/kernel/syscall.c +++ b/arch/um/kernel/syscall.c @@ -25,12 +25,12 @@ int record_syscall_start(int syscall) syscall_record[index].syscall = syscall; syscall_record[index].pid = current_pid(); syscall_record[index].result = 0xdeadbeef; - syscall_record[index].start = os_usecs(); + syscall_record[index].start = os_nsecs(); return(index); } void record_syscall_end(int index, long result) { syscall_record[index].result = result; - syscall_record[index].end = os_usecs(); + syscall_record[index].end = os_nsecs(); } diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index c40b611e3d9..8fa2ae7f302 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -14,9 +14,9 @@ #include "kern_util.h" #include "user.h" #include "process.h" -#include "signal_user.h" #include "time_user.h" #include "kern_constants.h" +#include "os.h" /* XXX This really needs to be declared and initialized in a kernel file since * it's in <linux/time.h> @@ -99,7 +99,8 @@ void uml_idle_timer(void) set_interval(ITIMER_REAL); } -extern int do_posix_clock_monotonic_gettime(struct timespec *tp); +extern void ktime_get_ts(struct timespec *ts); +#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) void time_init(void) { @@ -114,8 +115,8 @@ void time_init(void) wall_to_monotonic.tv_nsec = -now.tv_nsec; } -/* Declared in linux/time.h, which can't be included here */ -extern void clock_was_set(void); +/* Defined in linux/ktimer.h, which can't be included here */ +#define clock_was_set() do { } while (0) void do_gettimeofday(struct timeval *tv) { diff --git a/arch/um/kernel/time_kern.c b/arch/um/kernel/time_kern.c index 020ca79b8d3..3c7626cdba4 100644 --- a/arch/um/kernel/time_kern.c +++ b/arch/um/kernel/time_kern.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -13,12 +13,12 @@ #include "linux/interrupt.h" #include "linux/init.h" #include "linux/delay.h" +#include "linux/hrtimer.h" #include "asm/irq.h" #include "asm/param.h" #include "asm/current.h" #include "kern_util.h" #include "user_util.h" -#include "time_user.h" #include "mode.h" #include "os.h" @@ -39,7 +39,7 @@ unsigned long long sched_clock(void) int timer_irq_inited = 0; static int first_tick; -static unsigned long long prev_usecs; +static unsigned long long prev_nsecs; #ifdef CONFIG_UML_REAL_TIME_CLOCK static long long delta; /* Deviation per interval */ #endif @@ -58,23 +58,23 @@ void timer_irq(union uml_pt_regs *regs) if(first_tick){ #ifdef CONFIG_UML_REAL_TIME_CLOCK /* We've had 1 tick */ - unsigned long long usecs = os_usecs(); + unsigned long long nsecs = os_nsecs(); - delta += usecs - prev_usecs; - prev_usecs = usecs; + delta += nsecs - prev_nsecs; + prev_nsecs = nsecs; /* Protect against the host clock being set backwards */ if(delta < 0) delta = 0; - ticks += (delta * HZ) / MILLION; - delta -= (ticks * MILLION) / HZ; + ticks += (delta * HZ) / BILLION; + delta -= (ticks * BILLION) / HZ; #else ticks = 1; #endif } else { - prev_usecs = os_usecs(); + prev_nsecs = os_nsecs(); first_tick = 1; } @@ -84,49 +84,102 @@ void timer_irq(union uml_pt_regs *regs) } } -void boot_timer_handler(int sig) +void do_boot_timer_handler(struct sigcontext * sc) { struct pt_regs regs; - CHOOSE_MODE((void) - (UPT_SC(®s.regs) = (struct sigcontext *) (&sig + 1)), + CHOOSE_MODE((void) (UPT_SC(®s.regs) = sc), (void) (regs.regs.skas.is_user = 0)); do_timer(®s); } +static DEFINE_SPINLOCK(timer_spinlock); + +static unsigned long long local_offset = 0; + +static inline unsigned long long get_time(void) +{ + unsigned long long nsecs; + unsigned long flags; + + spin_lock_irqsave(&timer_spinlock, flags); + nsecs = os_nsecs(); + nsecs += local_offset; + spin_unlock_irqrestore(&timer_spinlock, flags); + + return nsecs; +} + irqreturn_t um_timer(int irq, void *dev, struct pt_regs *regs) { + unsigned long long nsecs; unsigned long flags; do_timer(regs); + write_seqlock_irqsave(&xtime_lock, flags); - timer(); + nsecs = get_time() + local_offset; + xtime.tv_sec = nsecs / NSEC_PER_SEC; + xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC; write_sequnlock_irqrestore(&xtime_lock, flags); + return(IRQ_HANDLED); } long um_time(int __user *tloc) { - struct timeval now; + long ret = get_time() / NSEC_PER_SEC; - do_gettimeofday(&now); - if (tloc) { - if (put_user(now.tv_sec, tloc)) - now.tv_sec = -EFAULT; - } - return now.tv_sec; + if((tloc != NULL) && put_user(ret, tloc)) + return -EFAULT; + + return ret; +} + +void do_gettimeofday(struct timeval *tv) +{ + unsigned long long nsecs = get_time(); + + tv->tv_sec = nsecs / NSEC_PER_SEC; + /* Careful about calculations here - this was originally done as + * (nsecs - tv->tv_sec * NSEC_PER_SEC) / NSEC_PER_USEC + * which gave bogus (> 1000000) values. Dunno why, suspect gcc + * (4.0.0) miscompiled it, or there's a subtle 64/32-bit conversion + * problem that I missed. + */ + nsecs -= tv->tv_sec * NSEC_PER_SEC; + tv->tv_usec = (unsigned long) nsecs / NSEC_PER_USEC; +} + +static inline void set_time(unsigned long long nsecs) +{ + unsigned long long now; + unsigned long flags; + + spin_lock_irqsave(&timer_spinlock, flags); + now = os_nsecs(); + local_offset = nsecs - now; + spin_unlock_irqrestore(&timer_spinlock, flags); + + clock_was_set(); } long um_stime(int __user *tptr) { int value; - struct timespec new; if (get_user(value, tptr)) return -EFAULT; - new.tv_sec = value; - new.tv_nsec = 0; - do_settimeofday(&new); + + set_time((unsigned long long) value * NSEC_PER_SEC); + + return 0; +} + +int do_settimeofday(struct timespec *tv) +{ + set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec); + return 0; } @@ -134,29 +187,15 @@ void timer_handler(int sig, union uml_pt_regs *regs) { local_irq_disable(); irq_enter(); - update_process_times(CHOOSE_MODE(user_context(UPT_SP(regs)), - (regs)->skas.is_user)); + update_process_times(CHOOSE_MODE( + (UPT_SC(regs) && user_context(UPT_SP(regs))), + (regs)->skas.is_user)); irq_exit(); local_irq_enable(); if(current_thread->cpu == 0) timer_irq(regs); } -static DEFINE_SPINLOCK(timer_spinlock); - -unsigned long time_lock(void) -{ - unsigned long flags; - - spin_lock_irqsave(&timer_spinlock, flags); - return(flags); -} - -void time_unlock(unsigned long flags) -{ - spin_unlock_irqrestore(&timer_spinlock, flags); -} - int __init timer_init(void) { int err; @@ -171,14 +210,3 @@ int __init timer_init(void) } __initcall(timer_init); - -/* - * 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: - */ diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c index 0d4c10a7360..d56046c2aba 100644 --- a/arch/um/kernel/trap_kern.c +++ b/arch/um/kernel/trap_kern.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -26,9 +26,13 @@ #include "mconsole_kern.h" #include "mem.h" #include "mem_kern.h" +#include "sysdep/sigcontext.h" +#include "sysdep/ptrace.h" +#include "os.h" #ifdef CONFIG_MODE_SKAS #include "skas.h" #endif +#include "os.h" /* Note this is constrained to return 0, -EFAULT, -EACCESS, -ENOMEM by segv(). */ int handle_page_fault(unsigned long address, unsigned long ip, @@ -125,6 +129,25 @@ out_of_memory: goto out; } +void segv_handler(int sig, union uml_pt_regs *regs) +{ + struct faultinfo * fi = UPT_FAULTINFO(regs); + + if(UPT_IS_USER(regs) && !SEGV_IS_FIXABLE(fi)){ + bad_segv(*fi, UPT_IP(regs)); + return; + } + segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs); +} + +struct kern_handlers handlinfo_kern = { + .relay_signal = relay_signal, + .winch = winch, + .bus_handler = relay_signal, + .page_fault = segv_handler, + .sigio_handler = sigio_handler, + .timer_handler = timer_handler +}; /* * We give a *copy* of the faultinfo in the regs to segv. * This must be done, since nesting SEGVs could overwrite diff --git a/arch/um/kernel/trap_user.c b/arch/um/kernel/trap_user.c deleted file mode 100644 index e9ccd6b8d3c..00000000000 --- a/arch/um/kernel/trap_user.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#include <stdlib.h> -#include <errno.h> -#include <setjmp.h> -#include <signal.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <asm/page.h> -#include <asm/unistd.h> -#include <asm/ptrace.h> -#include "init.h" -#include "sysdep/ptrace.h" -#include "sigcontext.h" -#include "sysdep/sigcontext.h" -#include "irq_user.h" -#include "signal_user.h" -#include "time_user.h" -#include "task.h" -#include "mode.h" -#include "choose-mode.h" -#include "kern_util.h" -#include "user_util.h" -#include "os.h" - -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 segv_handler(int sig, union uml_pt_regs *regs) -{ - struct faultinfo * fi = UPT_FAULTINFO(regs); - - if(UPT_IS_USER(regs) && !SEGV_IS_FIXABLE(fi)){ - bad_segv(*fi, UPT_IP(regs)); - return; - } - segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs); -} - -void usr2_handler(int sig, union uml_pt_regs *regs) -{ - CHOOSE_MODE(syscall_handler_tt(sig, regs), (void) 0); -} - -struct signal_info sig_info[] = { - [ SIGTRAP ] { .handler = relay_signal, - .is_irq = 0 }, - [ SIGFPE ] { .handler = relay_signal, - .is_irq = 0 }, - [ SIGILL ] { .handler = relay_signal, - .is_irq = 0 }, - [ SIGWINCH ] { .handler = winch, - .is_irq = 1 }, - [ SIGBUS ] { .handler = bus_handler, - .is_irq = 0 }, - [ SIGSEGV] { .handler = segv_handler, - .is_irq = 0 }, - [ SIGIO ] { .handler = sigio_handler, - .is_irq = 1 }, - [ SIGVTALRM ] { .handler = timer_handler, - .is_irq = 1 }, - [ SIGALRM ] { .handler = timer_handler, - .is_irq = 1 }, - [ SIGUSR2 ] { .handler = usr2_handler, - .is_irq = 0 }, -}; - -void do_longjmp(void *b, int val) -{ - sigjmp_buf *buf = b; - - siglongjmp(*buf, val); -} - -/* - * 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: - */ diff --git a/arch/um/kernel/tt/exec_kern.c b/arch/um/kernel/tt/exec_kern.c index 065b504a653..5c1e4cc1c04 100644 --- a/arch/um/kernel/tt/exec_kern.c +++ b/arch/um/kernel/tt/exec_kern.c @@ -13,8 +13,6 @@ #include "user_util.h" #include "kern_util.h" #include "irq_user.h" -#include "time_user.h" -#include "signal_user.h" #include "mem_user.h" #include "os.h" #include "tlb.h" @@ -40,7 +38,7 @@ void flush_thread_tt(void) do_exit(SIGKILL); } - new_pid = start_fork_tramp(current->thread_info, stack, 0, exec_tramp); + new_pid = start_fork_tramp(task_stack_page(current), stack, 0, exec_tramp); if(new_pid < 0){ printk(KERN_ERR "flush_thread : new thread failed, errno = %d\n", diff --git a/arch/um/kernel/tt/gdb.c b/arch/um/kernel/tt/gdb.c index 37e22d71a0d..786e4edd86c 100644 --- a/arch/um/kernel/tt/gdb.c +++ b/arch/um/kernel/tt/gdb.c @@ -20,6 +20,7 @@ #include "user_util.h" #include "tt.h" #include "sysdep/thread.h" +#include "os.h" extern int debugger_pid; extern int debugger_fd; diff --git a/arch/um/kernel/tt/include/mmu-tt.h b/arch/um/kernel/tt/include/mmu-tt.h deleted file mode 100644 index 0440510ab3f..00000000000 --- a/arch/um/kernel/tt/include/mmu-tt.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __TT_MMU_H -#define __TT_MMU_H - -struct mmu_context_tt { -}; - -#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: - */ diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c index cfaa373a6e7..295c1ac817b 100644 --- a/arch/um/kernel/tt/process_kern.c +++ b/arch/um/kernel/tt/process_kern.c @@ -13,13 +13,11 @@ #include "asm/ptrace.h" #include "asm/tlbflush.h" #include "irq_user.h" -#include "signal_user.h" #include "kern_util.h" #include "user_util.h" #include "os.h" #include "kern.h" #include "sigcontext.h" -#include "time_user.h" #include "mem_user.h" #include "tlb.h" #include "mode.h" @@ -37,7 +35,7 @@ void switch_to_tt(void *prev, void *next) from = prev; to = next; - cpu = from->thread_info->cpu; + cpu = task_thread_info(from)->cpu; if(cpu == 0) forward_interrupts(to->thread.mode.tt.extern_pid); #ifdef CONFIG_SMP @@ -254,7 +252,7 @@ int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp, clone_flags &= CLONE_VM; p->thread.temp_stack = stack; - new_pid = start_fork_tramp(p->thread_info, stack, clone_flags, tramp); + new_pid = start_fork_tramp(task_stack_page(p), stack, clone_flags, tramp); if(new_pid < 0){ printk(KERN_ERR "copy_thread : clone failed - errno = %d\n", -new_pid); @@ -344,7 +342,7 @@ int do_proc_op(void *t, int proc_id) pid = thread->request.u.exec.pid; do_exec(thread->mode.tt.extern_pid, pid); thread->mode.tt.extern_pid = pid; - cpu_tasks[task->thread_info->cpu].pid = pid; + cpu_tasks[task_thread_info(task)->cpu].pid = pid; break; case OP_FORK: attach_process(thread->request.u.fork.pid); @@ -426,7 +424,7 @@ int start_uml_tt(void) int pages; pages = (1 << CONFIG_KERNEL_STACK_ORDER); - sp = (void *) ((unsigned long) init_task.thread_info) + + sp = task_stack_page(&init_task) + pages * PAGE_SIZE - sizeof(unsigned long); return(tracer(start_kernel_proc, sp)); } diff --git a/arch/um/kernel/tt/ptproxy/ptrace.c b/arch/um/kernel/tt/ptproxy/ptrace.c index 528a5fc8d88..03774427d46 100644 --- a/arch/um/kernel/tt/ptproxy/ptrace.c +++ b/arch/um/kernel/tt/ptproxy/ptrace.c @@ -20,6 +20,7 @@ Jeff Dike (jdike@karaya.com) : Modified for integration into uml #include "kern_util.h" #include "ptrace_user.h" #include "tt.h" +#include "os.h" long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2, long arg3, long arg4, pid_t child, int *ret) diff --git a/arch/um/kernel/tt/ptproxy/sysdep.c b/arch/um/kernel/tt/ptproxy/sysdep.c index a5f0e01e214..99f178319d0 100644 --- a/arch/um/kernel/tt/ptproxy/sysdep.c +++ b/arch/um/kernel/tt/ptproxy/sysdep.c @@ -15,6 +15,7 @@ terms and conditions. #include "ptrace_user.h" #include "user_util.h" #include "user.h" +#include "os.h" int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4, long *arg5) diff --git a/arch/um/kernel/tt/tracer.c b/arch/um/kernel/tt/tracer.c index d11e7399d7a..71daae24e48 100644 --- a/arch/um/kernel/tt/tracer.c +++ b/arch/um/kernel/tt/tracer.c @@ -19,7 +19,6 @@ #include "sigcontext.h" #include "sysdep/sigcontext.h" #include "os.h" -#include "signal_user.h" #include "user_util.h" #include "mem_user.h" #include "process.h" diff --git a/arch/um/kernel/tt/trap_user.c b/arch/um/kernel/tt/trap_user.c index fc108615bea..b5d9d64d91e 100644 --- a/arch/um/kernel/tt/trap_user.c +++ b/arch/um/kernel/tt/trap_user.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -8,18 +8,18 @@ #include <signal.h> #include "sysdep/ptrace.h" #include "sysdep/sigcontext.h" -#include "signal_user.h" #include "user_util.h" #include "kern_util.h" #include "task.h" #include "tt.h" +#include "os.h" void sig_handler_common_tt(int sig, void *sc_ptr) { struct sigcontext *sc = sc_ptr; struct tt_regs save_regs, *r; - struct signal_info *info; - int save_errno = errno, is_user; + int save_errno = errno, is_user = 0; + void (*handler)(int, union uml_pt_regs *); /* 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, @@ -35,15 +35,20 @@ void sig_handler_common_tt(int sig, void *sc_ptr) GET_FAULTINFO_FROM_SC(r->faultinfo, sc); } save_regs = *r; - is_user = user_context(SC_SP(sc)); + if (sc) + is_user = user_context(SC_SP(sc)); r->sc = sc; if(sig != SIGUSR2) r->syscall = -1; - info = &sig_info[sig]; - if(!info->is_irq) unblock_signals(); + 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(); - (*info->handler)(sig, (union uml_pt_regs *) r); + handler(sig, (union uml_pt_regs *) r); if(is_user){ interrupt_end(); diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 142a9493912..e2d3ca445ef 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -146,8 +146,8 @@ void set_cmdline(char *cmd) if(CHOOSE_MODE(honeypot, 0)) return; - umid = get_umid(1); - if(umid != NULL){ + umid = get_umid(); + if(*umid != '\0'){ snprintf(argv1_begin, (argv1_end - argv1_begin) * sizeof(*ptr), "(%s) ", umid); @@ -243,10 +243,6 @@ static int __init mode_tt_setup(char *line, int *add) return(0); } -#else - -#error Either CONFIG_MODE_TT or CONFIG_MODE_SKAS must be enabled - #endif #endif #endif @@ -363,6 +359,11 @@ int linux_main(int argc, char **argv) uml_start = CHOOSE_MODE_PROC(set_task_sizes_tt, set_task_sizes_skas, 0, &host_task_size, &task_size); + /* + * Setting up handlers to 'sig_info' struct + */ + os_fill_handlinfo(handlinfo_kern); + brk_start = (unsigned long) sbrk(0); CHOOSE_MODE_PROC(before_mem_tt, before_mem_skas, brk_start); /* Increase physical memory size for exec-shield users diff --git a/arch/um/kernel/umid.c b/arch/um/kernel/umid.c index 0b21d59ba0c..4eaee823bfd 100644 --- a/arch/um/kernel/umid.c +++ b/arch/um/kernel/umid.c @@ -3,61 +3,30 @@ * Licensed under the GPL */ -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <dirent.h> -#include <signal.h> -#include <sys/stat.h> -#include <sys/param.h> -#include "user.h" -#include "umid.h" +#include "asm/errno.h" #include "init.h" #include "os.h" -#include "user_util.h" -#include "choose-mode.h" +#include "kern.h" +#include "linux/kernel.h" -#define UMID_LEN 64 -#define UML_DIR "~/.uml/" - -/* Changed by set_umid and make_umid, which are 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; - -/* Changed by set_umid */ -static int umid_is_random = 1; +/* Changed by set_umid_arg */ static int umid_inited = 0; -/* Have we created the files? Should we remove them? */ -static int umid_owned = 0; -static int make_umid(int (*printer)(const char *fmt, ...)); - -static int __init set_umid(char *name, int is_random, - int (*printer)(const char *fmt, ...)) +static int __init set_umid_arg(char *name, int *add) { - if(umid_inited){ - (*printer)("Unique machine name can't be set twice\n"); - return(-1); - } + int err; - if(strlen(name) > UMID_LEN - 1) - (*printer)("Unique machine name is being truncated to %d " - "characters\n", UMID_LEN); - strlcpy(umid, name, sizeof(umid)); + if(umid_inited) + return 0; - umid_is_random = is_random; - umid_inited = 1; - return 0; -} - -static int __init set_umid_arg(char *name, int *add) -{ *add = 0; - return(set_umid(name, 0, printf)); + err = set_umid(name); + if(err == -EEXIST) + printf("umid '%s' already in use\n", name); + else if(!err) + umid_inited = 1; + + return 0; } __uml_setup("umid=", set_umid_arg, @@ -66,265 +35,3 @@ __uml_setup("umid=", set_umid_arg, " is used for naming the pid file and management console socket.\n\n" ); -int __init umid_file_name(char *name, char *buf, int len) -{ - int n; - - if(!umid_inited && make_umid(printk)) return(-1); - - n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1; - if(n > len){ - printk("umid_file_name : buffer too short\n"); - return(-1); - } - - sprintf(buf, "%s%s/%s", uml_dir, umid, name); - return(0); -} - -extern int tracing_pid; - -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 = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))), - 0644); - if(fd < 0){ - printf("Open of machine pid file \"%s\" failed: %s\n", - file, strerror(-fd)); - return; - } - - sprintf(pid, "%d\n", os_getpid()); - n = os_write_file(fd, pid, strlen(pid)); - if(n != strlen(pid)) - printf("Write of pid file failed - err = %d\n", -n); - os_close_file(fd); -} - -static int actually_do_remove(char *dir) -{ - DIR *directory; - struct dirent *ent; - int len; - char file[256]; - - directory = opendir(dir); - if(directory == NULL){ - printk("actually_do_remove : couldn't open directory '%s', " - "errno = %d\n", dir, errno); - return(1); - } - 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)){ - printk("Not deleting '%s' from '%s' - name too long\n", - ent->d_name, dir); - continue; - } - sprintf(file, "%s/%s", dir, ent->d_name); - if(unlink(file) < 0){ - printk("actually_do_remove : couldn't remove '%s' " - "from '%s', errno = %d\n", ent->d_name, dir, - errno); - return(1); - } - } - if(rmdir(dir) < 0){ - printk("actually_do_remove : couldn't rmdir '%s', " - "errno = %d\n", dir, errno); - return(1); - } - return(0); -} - -void remove_umid_dir(void) -{ - char dir[strlen(uml_dir) + UMID_LEN + 1]; - if (!umid_owned) - return; - - sprintf(dir, "%s%s", uml_dir, umid); - actually_do_remove(dir); -} - -char *get_umid(int only_if_set) -{ - if(only_if_set && umid_is_random) - return NULL; - return umid; -} - -static int not_dead_yet(char *dir) -{ - char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; - char pid[sizeof("nnnnn\0")], *end; - int dead, fd, p, n; - - sprintf(file, "%s/pid", dir); - dead = 0; - fd = os_open_file(file, of_read(OPENFLAGS()), 0); - if(fd < 0){ - if(fd != -ENOENT){ - printk("not_dead_yet : couldn't open pid file '%s', " - "err = %d\n", file, -fd); - return(1); - } - dead = 1; - } - if(fd > 0){ - n = os_read_file(fd, pid, sizeof(pid)); - if(n < 0){ - printk("not_dead_yet : couldn't read pid file '%s', " - "err = %d\n", file, -n); - return(1); - } - p = strtoul(pid, &end, 0); - if(end == pid){ - printk("not_dead_yet : couldn't parse pid file '%s', " - "errno = %d\n", file, errno); - dead = 1; - } - if(((kill(p, 0) < 0) && (errno == ESRCH)) || - (p == CHOOSE_MODE(tracing_pid, os_getpid()))) - dead = 1; - } - if(!dead) - return(1); - return(actually_do_remove(dir)); -} - -static int __init set_uml_dir(char *name, int *add) -{ - if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){ - uml_dir = malloc(strlen(name) + 2); - if(uml_dir == NULL){ - printf("Failed to malloc uml_dir - error = %d\n", - errno); - uml_dir = name; - /* Return 0 here because do_initcalls doesn't look at - * the return value. - */ - return(0); - } - sprintf(uml_dir, "%s/", name); - } - else uml_dir = name; - return(0); -} - -static int __init make_uml_dir(void) -{ - char dir[MAXPATHLEN + 1] = { '\0' }; - int len; - - if(*uml_dir == '~'){ - char *home = getenv("HOME"); - - if(home == NULL){ - printf("make_uml_dir : no value in environment for " - "$HOME\n"); - exit(1); - } - 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)); - - uml_dir = malloc(strlen(dir) + 1); - if (uml_dir == NULL) { - printf("make_uml_dir : malloc failed, errno = %d\n", errno); - exit(1); - } - strcpy(uml_dir, dir); - - if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){ - printf("Failed to mkdir %s: %s\n", uml_dir, strerror(errno)); - return(-1); - } - return 0; -} - -static int __init make_umid(int (*printer)(const char *fmt, ...)) -{ - int fd, err; - char tmp[strlen(uml_dir) + UMID_LEN + 1]; - - strlcpy(tmp, uml_dir, sizeof(tmp)); - - if(!umid_inited){ - strcat(tmp, "XXXXXX"); - fd = mkstemp(tmp); - if(fd < 0){ - (*printer)("make_umid - mkstemp(%s) failed: %s\n", - tmp,strerror(errno)); - return(1); - } - - os_close_file(fd); - /* 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. - */ - unlink(tmp); - set_umid(&tmp[strlen(uml_dir)], 1, printer); - } - - sprintf(tmp, "%s%s", uml_dir, umid); - - err = mkdir(tmp, 0777); - if(err < 0){ - if(errno == EEXIST){ - if(not_dead_yet(tmp)){ - (*printer)("umid '%s' is in use\n", umid); - umid_owned = 0; - return(-1); - } - err = mkdir(tmp, 0777); - } - } - if(err < 0){ - (*printer)("Failed to create %s - errno = %d\n", umid, errno); - return(-1); - } - - umid_owned = 1; - 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 int __init make_umid_setup(void) -{ - /* one function with the ordering we need ... */ - make_uml_dir(); - make_umid(printf); - create_pid_file(); - return 0; -} -__uml_postsetup(make_umid_setup); - -/* - * 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: - */ diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile index b83ac8e21c3..08a4e628b24 100644 --- a/arch/um/os-Linux/Makefile +++ b/arch/um/os-Linux/Makefile @@ -4,11 +4,13 @@ # obj-y = aio.o elf_aux.o file.o helper.o main.o mem.o process.o signal.o \ - start_up.o time.o tt.o tty.o uaccess.o user_syms.o drivers/ \ - sys-$(SUBARCH)/ + start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o user_syms.o \ + util.o drivers/ sys-$(SUBARCH)/ + +obj-$(CONFIG_MODE_SKAS) += skas/ USER_OBJS := aio.o elf_aux.o file.o helper.o main.o mem.o process.o signal.o \ - start_up.o time.o tt.o tty.o uaccess.o + start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o util.o elf_aux.o: $(ARCH_DIR)/kernel-offsets.h CFLAGS_elf_aux.o += -I$(objtree)/arch/um diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c index ffa759addd3..f897140cc4a 100644 --- a/arch/um/os-Linux/aio.c +++ b/arch/um/os-Linux/aio.c @@ -16,12 +16,12 @@ #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; + enum aio_type type; + int io_fd; + unsigned long long offset; + char *buf; + int len; + struct aio_context *aio; }; static int aio_req_fd_r = -1; @@ -38,18 +38,18 @@ static int aio_req_fd_w = -1; static long io_setup(int n, aio_context_t *ctxp) { - return syscall(__NR_io_setup, n, 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); + 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) + struct io_event *events, struct timespec *timeout) { - return syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, timeout); + return syscall(__NR_io_getevents, ctx_id, min_nr, nr, events, timeout); } #endif @@ -66,243 +66,245 @@ static long io_getevents(aio_context_t ctx_id, long min_nr, long nr, */ 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) + 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; + 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; + return err; } 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 }); + 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)) + if(err != sizeof(reply)) printk("aio_thread - write failed, fd = %d, " - "err = %d\n", aio_req_fd_r, -err); - } - } - return 0; + "err = %d\n", aio_req_fd_r, -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; + 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; } 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", aio_req_fd_r, -err); - } + 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", aio_req_fd_r, -err); + } + + return 0; } static int aio_pid = -1; 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: + 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; + return 0; } #ifdef HAVE_AIO_ABI #define DEFAULT_24_AIO 0 static int init_aio_26(void) { - unsigned long stack; - int err; + unsigned long stack; + int err; - if(io_setup(256, &ctx)){ + if(io_setup(256, &ctx)){ err = -errno; - printk("aio_thread failed to initialize context, err = %d\n", - errno); - return err; - } + 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; + err = run_helper_thread(aio_thread, NULL, + CLONE_FILES | CLONE_VM | SIGCHLD, &stack, 0); + if(err < 0) + return err; - aio_pid = err; + aio_pid = err; printk("Using 2.6 host AIO\n"); - return 0; + 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; + 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; + 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; + return -ENOSYS; } #endif @@ -310,8 +312,8 @@ static int aio_24 = DEFAULT_24_AIO; static int __init set_aio_24(char *name, int *add) { - aio_24 = 1; - return 0; + aio_24 = 1; + return 0; } __uml_setup("aio=2.4", set_aio_24, @@ -328,28 +330,27 @@ __uml_setup("aio=2.4", set_aio_24, 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; + 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 @@ -362,8 +363,8 @@ __initcall(init_aio); static void exit_aio(void) { - if(aio_pid != -1) - os_kill_process(aio_pid, 1); + if(aio_pid != -1) + os_kill_process(aio_pid, 1); } __uml_exitcall(exit_aio); @@ -371,30 +372,30 @@ __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; + 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) + 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); - } + aio->reply_fd = reply_fd; + if(aio_24) + return submit_aio_24(type, io_fd, buf, len, offset, aio); + else { + return submit_aio_26(type, io_fd, buf, len, offset, aio); + } } diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c index 36cc8475bcd..6490a4ff40a 100644 --- a/arch/um/os-Linux/helper.c +++ b/arch/um/os-Linux/helper.c @@ -60,7 +60,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, if((stack_out != NULL) && (*stack_out != 0)) stack = *stack_out; - else stack = alloc_stack(0, um_in_interrupt()); + else stack = alloc_stack(0, __cant_sleep()); if(stack == 0) return(-ENOMEM); @@ -124,7 +124,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, unsigned long stack, sp; int pid, status, err; - stack = alloc_stack(stack_order, um_in_interrupt()); + stack = alloc_stack(stack_order, __cant_sleep()); if(stack == 0) return(-ENOMEM); sp = stack + (page_size() << stack_order) - sizeof(void *); diff --git a/arch/um/os-Linux/main.c b/arch/um/os-Linux/main.c index 23da27d2256..2878e89a674 100644 --- a/arch/um/os-Linux/main.c +++ b/arch/um/os-Linux/main.c @@ -16,8 +16,6 @@ #include "user_util.h" #include "kern_util.h" #include "mem_user.h" -#include "signal_user.h" -#include "time_user.h" #include "irq_user.h" #include "user.h" #include "init.h" @@ -83,20 +81,8 @@ extern void scan_elf_aux( char **envp); int main(int argc, char **argv, char **envp) { char **new_argv; - sigset_t mask; int ret, i, err; - /* Enable all signals except SIGIO - in some environments, we can - * enter with some signals blocked - */ - - sigemptyset(&mask); - sigaddset(&mask, SIGIO); - if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){ - perror("sigprocmask"); - exit(1); - } - #ifdef UML_CONFIG_CMDLINE_ON_HOST /* Allocate memory for thread command lines */ if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index d9c52387c4a..7f5e2dac2a3 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -15,10 +15,10 @@ #include "os.h" #include "user.h" #include "user_util.h" -#include "signal_user.h" #include "process.h" #include "irq_user.h" #include "kern_util.h" +#include "longjmp.h" #define ARBITRARY_ADDR -1 #define FAILURE_PID -1 @@ -206,24 +206,13 @@ void init_new_thread_signals(int altstack) int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr) { - sigjmp_buf buf; - int n; - - *jmp_ptr = &buf; - n = sigsetjmp(buf, 1); - if(n != 0) - return(n); - (*fn)(arg); - return(0); + sigjmp_buf buf; + int n, enable; + + *jmp_ptr = &buf; + n = UML_SIGSETJMP(&buf, enable); + if(n != 0) + return(n); + (*fn)(arg); + return(0); } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index c7bfd5ee392..f11b3124a0c 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -4,27 +4,74 @@ */ #include <signal.h> -#include "time_user.h" -#include "mode.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/signal.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) + +static int signals_enabled = 1; +static int pending = 0; void sig_handler(ARCH_SIGHDLR_PARAM) { struct sigcontext *sc; + int enabled; + + /* Must be the first thing that this handler does - x86_64 stores + * the sigcontext in %rdx, and we need to save it before it has a + * chance to get trashed. + */ ARCH_GET_SIGCONTEXT(sc, sig); + + 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); } extern int timer_irq_inited; -void alarm_handler(ARCH_SIGHDLR_PARAM) +static void real_alarm_handler(int sig, struct sigcontext *sc) { - struct sigcontext *sc; - - ARCH_GET_SIGCONTEXT(sc, sig); - if(!timer_irq_inited) return; + if(!timer_irq_inited){ + signals_enabled = 1; + return; + } if(sig == SIGALRM) switch_timers(0); @@ -34,15 +81,180 @@ void alarm_handler(ARCH_SIGHDLR_PARAM) if(sig == SIGALRM) switch_timers(1); + } -/* - * 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: - */ +void alarm_handler(ARCH_SIGHDLR_PARAM) +{ + struct sigcontext *sc; + int enabled; + + ARCH_GET_SIGCONTEXT(sc, sig); + + 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); +} + +extern void do_boot_timer_handler(struct sigcontext * sc); + +void boot_timer_handler(ARCH_SIGHDLR_PARAM) +{ + struct sigcontext *sc; + int enabled; + + ARCH_GET_SIGCONTEXT(sc, sig); + + enabled = signals_enabled; + if(!enabled){ + if(sig == SIGVTALRM) + pending |= SIGVTALRM_MASK; + else pending |= SIGALRM_MASK; + return; + } + + block_signals(); + + do_boot_timer_handler(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 set_handler(int sig, void (*handler)(int), int flags, ...) +{ + struct sigaction action; + va_list ap; + sigset_t sig_mask; + int mask; + + va_start(ap, flags); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + 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; +} + +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; + + save_pending = pending; + if(save_pending == 0) + 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; +} + +void os_usr1_signal(int on) +{ + change_sig(SIGUSR1, on); +} diff --git a/arch/um/os-Linux/skas/Makefile b/arch/um/os-Linux/skas/Makefile new file mode 100644 index 00000000000..5fd8d4dad66 --- /dev/null +++ b/arch/um/os-Linux/skas/Makefile @@ -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 diff --git a/arch/um/kernel/skas/mem_user.c b/arch/um/os-Linux/skas/mem.c index 1d89640bd50..9890e9090f5 100644 --- a/arch/um/kernel/skas/mem_user.c +++ b/arch/um/os-Linux/skas/mem.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -32,7 +32,7 @@ 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){ + if(stack == NULL) { stack = (unsigned long *) mm_idp->stack + 2; *stack = 0; } @@ -45,13 +45,14 @@ int single_count = 0; int multi_count = 0; int multi_op_count = 0; -static long do_syscall_stub(struct mm_id *mm_idp, void **addr) +static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr) { unsigned long regs[MAX_REG_NR]; - unsigned long *data; - unsigned long *syscall; + int n; long ret, offset; - int n, pid = mm_idp->u.pid; + unsigned long * data; + unsigned long * syscall; + int pid = mm_idp->u.pid; if(proc_mm) #warning Need to look up userspace_pid by cpu @@ -59,10 +60,11 @@ static long do_syscall_stub(struct mm_id *mm_idp, void **addr) multi_count++; - get_safe_registers(regs); - regs[REGS_IP_INDEX] = UML_CONFIG_STUB_CODE + + get_safe_registers(regs); + regs[REGS_IP_INDEX] = UML_CONFIG_STUB_CODE + ((unsigned long) &batch_syscall_stub - - (unsigned long) &__syscall_stub_start); + (unsigned long) &__syscall_stub_start); + n = ptrace_setregs(pid, regs); if(n < 0) panic("do_syscall_stub : PTRACE_SETREGS failed, errno = %d\n", @@ -80,6 +82,8 @@ static long do_syscall_stub(struct mm_id *mm_idp, void **addr) if (offset) { data = (unsigned long *)(mm_idp->stack + offset - UML_CONFIG_STUB_DATA); + printk("do_syscall_stub : ret = %d, offset = %d, " + "data = 0x%x\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", @@ -107,32 +111,32 @@ static long do_syscall_stub(struct mm_id *mm_idp, void **addr) long run_syscall_stub(struct mm_id * mm_idp, int syscall, unsigned long *args, long expected, void **addr, - int done) + int done) { - unsigned long *stack = check_init_stack(mm_idp, *addr); + unsigned long *stack = check_init_stack(mm_idp, *addr); if(done && *addr == NULL) single_count++; - *stack += sizeof(long); + *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++ = 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++; + *stack = 0; + multi_op_count++; - if(!done && ((((unsigned long) stack) & ~PAGE_MASK) < + if(!done && ((((unsigned long) stack) & ~PAGE_MASK) < PAGE_SIZE - 10 * sizeof(long))){ *addr = stack; - return 0; - } + return 0; + } return do_syscall_stub(mm_idp, addr); } @@ -150,7 +154,7 @@ long syscall_stub_data(struct mm_id * mm_idp, 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 */ + /* in case of error, don't overwrite data on stack */ if(ret) return ret; } @@ -172,39 +176,39 @@ 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 - } } } ); + 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) }; + } + 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; } @@ -212,68 +216,66 @@ int map(struct mm_id * mm_idp, unsigned long virt, unsigned long len, 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 } } } ); + 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 }; + } + else { + unsigned long args[] = { (unsigned long) addr, len, 0, 0, 0, + 0 }; ret = run_syscall_stub(mm_idp, __NR_munmap, args, 0, data, done); - if(ret < 0) - printk("munmap stub failed, errno = %d\n", ret); - } + } - return ret; + 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; + 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) diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c new file mode 100644 index 00000000000..120a21c5883 --- /dev/null +++ b/arch/um/os-Linux/skas/process.c @@ -0,0 +1,566 @@ +/* + * 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 <setjmp.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 <asm/unistd.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[HOST_FRAME_SIZE]; + + 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 < HOST_FRAME_SIZE; 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; + + ptrace(PTRACE_TRACEME, 0, 0, 0); + + init_new_thread_signals(1); + enable_timer(); + + 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)){ + 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()); + set_handler(SIGSEGV, (void *) v, SA_ONSTACK, + SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, + SIGUSR1, -1); + } + + 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", + 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; + } + } +} +#define INIT_JMP_NEW_THREAD 0 +#define INIT_JMP_REMOVE_SIGSTACK 1 +#define INIT_JMP_CALLBACK 2 +#define INIT_JMP_HALT 3 +#define INIT_JMP_REBOOT 4 + +int copy_context_skas0(unsigned long new_stack, int pid) +{ + int err; + unsigned long regs[MAX_REG_NR]; + 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); + + /* 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, errno); + + /* 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 %d\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)) + 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, void **switch_buf_ptr, void **fork_buf_ptr, + void (*handler)(int)) +{ + unsigned long flags; + sigjmp_buf switch_buf, fork_buf; + int enable; + + *switch_buf_ptr = &switch_buf; + *fork_buf_ptr = &fork_buf; + + /* Somewhat subtle - siglongjmp restores the signal mask before doing + * the longjmp. This means that when jumping from one stack to another + * when the target stack has interrupts enabled, an interrupt may occur + * on the source stack. This is bad when starting up a process because + * it's not supposed to get timer ticks until it has been scheduled. + * So, we disable interrupts around the sigsetjmp to ensure that + * they can't happen until we get back here where they are safe. + */ + flags = get_signals(); + block_signals(); + if(UML_SIGSETJMP(&fork_buf, enable) == 0) + new_thread_proc(stack, handler); + + remove_sigstack(); + + set_signals(flags); +} + +void thread_wait(void *sw, void *fb) +{ + sigjmp_buf buf, **switch_buf = sw, *fork_buf; + int enable; + + *switch_buf = &buf; + fork_buf = fb; + if(UML_SIGSETJMP(&buf, enable) == 0) + siglongjmp(*fork_buf, INIT_JMP_REMOVE_SIGSTACK); +} + +void switch_threads(void *me, void *next) +{ + sigjmp_buf my_buf, **me_ptr = me, *next_buf = next; + int enable; + + *me_ptr = &my_buf; + if(UML_SIGSETJMP(&my_buf, enable) == 0) + UML_SIGLONGJMP(next_buf, 1); +} + +static sigjmp_buf initial_jmpbuf; + +/* XXX Make these percpu */ +static void (*cb_proc)(void *arg); +static void *cb_arg; +static sigjmp_buf *cb_back; + +int start_idle_thread(void *stack, void *switch_buf_ptr, void **fork_buf_ptr) +{ + sigjmp_buf **switch_buf = switch_buf_ptr; + int n, enable; + + set_handler(SIGWINCH, (__sighandler_t) sig_handler, + SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGALRM, + SIGVTALRM, -1); + + *fork_buf_ptr = &initial_jmpbuf; + n = UML_SIGSETJMP(&initial_jmpbuf, enable); + switch(n){ + case INIT_JMP_NEW_THREAD: + new_thread_proc((void *) stack, new_thread_handler); + break; + case INIT_JMP_REMOVE_SIGSTACK: + remove_sigstack(); + break; + case INIT_JMP_CALLBACK: + (*cb_proc)(cb_arg); + UML_SIGLONGJMP(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_SIGLONGJMP(*switch_buf, 1); +} + +void initial_thread_cb_skas(void (*proc)(void *), void *arg) +{ + sigjmp_buf here; + int enable; + + cb_proc = proc; + cb_arg = arg; + cb_back = &here; + + block_signals(); + if(UML_SIGSETJMP(&here, enable) == 0) + UML_SIGLONGJMP(&initial_jmpbuf, INIT_JMP_CALLBACK); + unblock_signals(); + + cb_proc = NULL; + cb_arg = NULL; + cb_back = NULL; +} + +void halt_skas(void) +{ + block_signals(); + UML_SIGLONGJMP(&initial_jmpbuf, INIT_JMP_HALT); +} + +void reboot_skas(void) +{ + block_signals(); + UML_SIGLONGJMP(&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; +} diff --git a/arch/um/kernel/skas/trap_user.c b/arch/um/os-Linux/skas/trap.c index 9950a6716fe..9ad5fbec459 100644 --- a/arch/um/kernel/skas/trap_user.c +++ b/arch/um/os-Linux/skas/trap.c @@ -1,11 +1,10 @@ -/* +/* * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ #include <signal.h> #include <errno.h> -#include "signal_user.h" #include "user_util.h" #include "kern_util.h" #include "task.h" @@ -14,12 +13,13 @@ #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; - struct signal_info *info; + void (*handler)(int, union uml_pt_regs *); int save_errno = errno; int save_user; @@ -34,17 +34,22 @@ void sig_handler_common_skas(int sig, void *sc_ptr) 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); - } + if ( sig == SIGFPE || sig == SIGSEGV || + sig == SIGBUS || sig == SIGILL || + sig == SIGTRAP ) { + GET_FAULTINFO_FROM_SC(r->faultinfo, sc); + } change_sig(SIGUSR1, 1); - info = &sig_info[sig]; - if(!info->is_irq) unblock_signals(); - (*info->handler)(sig, (union uml_pt_regs *) r); + 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; @@ -54,25 +59,15 @@ extern int ptrace_faultinfo; void user_signal(int sig, union uml_pt_regs *regs, int pid) { - struct signal_info *info; - int segv = ((sig == SIGFPE) || (sig == SIGSEGV) || (sig == SIGBUS) || - (sig == SIGILL) || (sig == SIGTRAP)); + 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); - info = &sig_info[sig]; - (*info->handler)(sig, regs); + + handler = sig_info[sig]; + handler(sig, (union uml_pt_regs *) regs); unblock_signals(); } - -/* - * 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: - */ diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c index 29a9e3f4376..6c5b17ed59e 100644 --- a/arch/um/os-Linux/start_up.c +++ b/arch/um/os-Linux/start_up.c @@ -24,13 +24,11 @@ #include "kern_util.h" #include "user.h" #include "signal_kern.h" -#include "signal_user.h" #include "sysdep/ptrace.h" #include "sysdep/sigcontext.h" #include "irq_user.h" #include "ptrace_user.h" #include "mem_user.h" -#include "time_user.h" #include "init.h" #include "os.h" #include "uml-config.h" diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c index cf30a39bc48..6f7626775ac 100644 --- a/arch/um/os-Linux/time.c +++ b/arch/um/os-Linux/time.c @@ -1,21 +1,128 @@ +/* + * 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" + +/* XXX This really needs to be declared and initialized in a kernel file since + * it's in <linux/time.h> + */ +extern struct timespec wall_to_monotonic; + +static void set_interval(int timer_type) +{ + int usec = 1000000/hz(); + struct itimerval interval = ((struct itimerval) { { 0, usec }, + { 0, usec } }); + + if(setitimer(timer_type, &interval, NULL) == -1) + panic("setitimer failed - errno = %d\n", errno); +} + +void enable_timer(void) +{ + set_interval(ITIMER_VIRTUAL); +} + +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 */ + set_handler(SIGALRM, SIG_IGN, 0, -1); + set_handler(SIGVTALRM, SIG_IGN, 0, -1); +} + +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); +} -unsigned long long os_usecs(void) +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(ITIMER_REAL); +} + +extern void ktime_get_ts(struct timespec *ts); +#define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) + +void time_init(void) +{ + struct timespec now; + + if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR) + panic("Couldn't set SIGVTALRM handler"); + set_interval(ITIMER_VIRTUAL); + + do_posix_clock_monotonic_gettime(&now); + wall_to_monotonic.tv_sec = -now.tv_sec; + wall_to_monotonic.tv_nsec = -now.tv_nsec; +} + +unsigned long long os_nsecs(void) { struct timeval tv; gettimeofday(&tv, NULL); - return((unsigned long long) tv.tv_sec * 1000000 + tv.tv_usec); + return((unsigned long long) tv.tv_sec * BILLION + tv.tv_usec * 1000); } -/* - * 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: - */ +void idle_sleep(int secs) +{ + struct timespec ts; + + ts.tv_sec = secs; + ts.tv_nsec = 0; + nanosleep(&ts, NULL); +} + +/* XXX This partly duplicates init_irq_signals */ + +void user_time_init(void) +{ + set_handler(SIGVTALRM, (__sighandler_t) alarm_handler, + SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, + SIGALRM, SIGUSR2, -1); + set_handler(SIGALRM, (__sighandler_t) alarm_handler, + SA_ONSTACK | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, + SIGVTALRM, SIGUSR2, -1); + set_interval(ITIMER_VIRTUAL); +} diff --git a/arch/um/os-Linux/trap.c b/arch/um/os-Linux/trap.c new file mode 100644 index 00000000000..a9f6b26f982 --- /dev/null +++ b/arch/um/os-Linux/trap.c @@ -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 <setjmp.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); +} + +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) +{ + sigjmp_buf *buf = b; + + UML_SIGLONGJMP(buf, val); +} diff --git a/arch/um/os-Linux/tt.c b/arch/um/os-Linux/tt.c index a6db8877931..919d19f1153 100644 --- a/arch/um/os-Linux/tt.c +++ b/arch/um/os-Linux/tt.c @@ -23,12 +23,10 @@ #include "kern_util.h" #include "user.h" #include "signal_kern.h" -#include "signal_user.h" #include "sysdep/ptrace.h" #include "sysdep/sigcontext.h" #include "irq_user.h" #include "ptrace_user.h" -#include "time_user.h" #include "init.h" #include "os.h" #include "uml-config.h" @@ -50,6 +48,68 @@ int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, 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); + } +} + /* *------------------------- * only for tt mode (will be deleted in future...) diff --git a/arch/um/os-Linux/uaccess.c b/arch/um/os-Linux/uaccess.c index 38d710158c3..166fb66995d 100644 --- a/arch/um/os-Linux/uaccess.c +++ b/arch/um/os-Linux/uaccess.c @@ -6,6 +6,7 @@ #include <setjmp.h> #include <string.h> +#include "longjmp.h" unsigned long __do_user_copy(void *to, const void *from, int n, void **fault_addr, void **fault_catcher, @@ -13,10 +14,11 @@ unsigned long __do_user_copy(void *to, const void *from, int n, int n), int *faulted_out) { unsigned long *faddrp = (unsigned long *) fault_addr, ret; + int enable; sigjmp_buf jbuf; *fault_catcher = &jbuf; - if(sigsetjmp(jbuf, 1) == 0){ + if(UML_SIGSETJMP(&jbuf, enable) == 0){ (*op)(to, from, n); ret = 0; *faulted_out = 0; diff --git a/arch/um/os-Linux/umid.c b/arch/um/os-Linux/umid.c new file mode 100644 index 00000000000..ecf107ae5ac --- /dev/null +++ b/arch/um/os-Linux/umid.c @@ -0,0 +1,335 @@ +#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 */ +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; +} + +static int actually_do_remove(char *dir) +{ + DIR *directory; + struct dirent *ent; + int len; + char file[256]; + + directory = opendir(dir); + if(directory == NULL) + return -errno; + + 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)) + return -E2BIG; + + sprintf(file, "%s/%s", dir, ent->d_name); + if(unlink(file) < 0) + return -errno; + } + if(rmdir(dir) < 0) + return -errno; + + return 0; +} + +/* 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. + */ + +static int not_dead_yet(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("not_dead_yet - pid filename too long\n"); + err = -E2BIG; + goto out; + } + + dead = 0; + fd = open(file, O_RDONLY); + if(fd < 0){ + if(fd != -ENOENT){ + printk("not_dead_yet : 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("not_dead_yet : couldn't read pid file '%s', " + "err = %d\n", file, -n); + goto out_close; + } + + p = strtoul(pid, &end, 0); + if(end == pid){ + printk("not_dead_yet : couldn't parse pid file '%s', " + "errno = %d\n", file, errno); + goto out_close; + } + + if((kill(p, 0) == 0) || (errno != ESRCH)) + return 1; + + err = actually_do_remove(dir); + if(err) + printk("not_dead_yet - actually_do_remove failed with " + "err = %d\n", err); + + return err; + + out_close: + close(fd); + out: + return 0; +} + +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(-fd)); + 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", -n); + + close(fd); +} + +int __init set_umid(char *name) +{ + if(strlen(name) > UMID_LEN - 1) + return -E2BIG; + + strlcpy(umid, name, sizeof(umid)); + + return 0; +} + +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(errno != EEXIST) + goto err; + + if(not_dead_yet(tmp) < 0) + goto err; + + err = mkdir(tmp, 0777); + } + if(err < 0){ + printk("Failed to create '%s' - err = %d\n", umid, err); + goto err_rmdir; + } + + umid_setup = 1; + + create_pid_file(); + + return 0; + + err_rmdir: + rmdir(tmp); + err: + return err; +} + +static int __init make_umid_init(void) +{ + 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 = actually_do_remove(dir); + if(err) + printf("remove_umid_dir - actually_do_remove failed with " + "err = %d\n", err); +} + +__uml_exitcall(remove_umid_dir); diff --git a/arch/um/kernel/user_util.c b/arch/um/os-Linux/util.c index 4c231161f25..e32065e2fdc 100644 --- a/arch/um/kernel/user_util.c +++ b/arch/um/os-Linux/util.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ @@ -29,17 +29,14 @@ #include "init.h" #include "ptrace_user.h" #include "uml-config.h" - -void stop(void) -{ - while(1) sleep(1000000); -} +#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) + if(mprotect((void *) address, page_size(), prot) < 0) panic("protecting stack failed, errno = %d", errno); } @@ -59,49 +56,6 @@ void task_protections(unsigned long address) panic("protecting stack failed, errno = %d", errno); } -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); - } -} - int raw(int fd) { struct termios tt; @@ -113,7 +67,7 @@ int raw(int fd) cfmakeraw(&tt); - CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); + CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); if(err < 0) return -errno; @@ -149,7 +103,7 @@ void setup_hostinfo(void) int setjmp_wrapper(void (*proc)(void *, void *), ...) { - va_list args; + va_list args; sigjmp_buf buf; int n; @@ -161,14 +115,3 @@ int setjmp_wrapper(void (*proc)(void *, void *), ...) va_end(args); return(n); } - -/* - * 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: - */ diff --git a/arch/um/sys-i386/ldt.c b/arch/um/sys-i386/ldt.c index 17746b4c08f..0cdfd4481d5 100644 --- a/arch/um/sys-i386/ldt.c +++ b/arch/um/sys-i386/ldt.c @@ -16,6 +16,8 @@ #include "choose-mode.h" #include "kern.h" #include "mode_kern.h" +#include "proc_mm.h" +#include "os.h" extern int modify_ldt(int func, void *ptr, unsigned long bytecount); @@ -456,13 +458,14 @@ long init_new_ldt(struct mmu_context_skas * new_mm, int i; long page, err=0; void *addr = NULL; + struct proc_mm_op copy; - memset(&desc, 0, sizeof(desc)); if(!ptrace_ldt) init_MUTEX(&new_mm->ldt.semaphore); if(!from_mm){ + memset(&desc, 0, sizeof(desc)); /* * We have to initialize a clean ldt. */ @@ -494,8 +497,26 @@ long init_new_ldt(struct mmu_context_skas * new_mm, } } new_mm->ldt.entry_count = 0; + + goto out; } - else if (!ptrace_ldt) { + + if(proc_mm){ + /* We have a valid from_mm, so we now have to copy the LDT of + * from_mm to new_mm, because using proc_mm an new mm with + * an empty/default LDT was created in new_mm() + */ + copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS, + .u = + { .copy_segments = + from_mm->id.u.mm_fd } } ); + i = os_write_file(new_mm->id.u.mm_fd, ©, sizeof(copy)); + if(i != sizeof(copy)) + printk("new_mm : /proc/mm copy_segments failed, " + "err = %d\n", -i); + } + + if(!ptrace_ldt) { /* Our local LDT is used to supply the data for * modify_ldt(READLDT), if PTRACE_LDT isn't available, * i.e., we have to use the stub for modify_ldt, which @@ -524,6 +545,7 @@ long init_new_ldt(struct mmu_context_skas * new_mm, up(&from_mm->ldt.semaphore); } + out: return err; } diff --git a/arch/um/sys-i386/signal.c b/arch/um/sys-i386/signal.c index 16bc19928b3..7cd1a82dc8c 100644 --- a/arch/um/sys-i386/signal.c +++ b/arch/um/sys-i386/signal.c @@ -10,7 +10,6 @@ #include "asm/uaccess.h" #include "asm/unistd.h" #include "frame_kern.h" -#include "signal_user.h" #include "sigcontext.h" #include "registers.h" #include "mode.h" |