diff options
Diffstat (limited to 'arch/um/kernel')
99 files changed, 13673 insertions, 0 deletions
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile new file mode 100644 index 00000000000..dc796c1bf39 --- /dev/null +++ b/arch/um/kernel/Makefile @@ -0,0 +1,58 @@ +#  +# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +extra-y := vmlinux.lds +clean-files := vmlinux.lds.S config.tmp + +obj-y = checksum.o config.o exec_kern.o exitcode.o \ +	helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o mem_user.o \ +	physmem.o process.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 sys_call_table.o tempfile.o time.o time_kern.o \ +	tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o umid.o \ +	user_util.o + +obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o +obj-$(CONFIG_GPROF)	+= gprof_syms.o +obj-$(CONFIG_GCOV)	+= gmon_syms.o +obj-$(CONFIG_TTY_LOG)	+= tty_log.o +obj-$(CONFIG_SYSCALL_DEBUG) += syscall_user.o + +obj-$(CONFIG_MODE_TT) += tt/ +obj-$(CONFIG_MODE_SKAS) += skas/ + +# This needs be compiled with frame pointers regardless of how the rest of the +# kernel is built. +CFLAGS_frame.o := -fno-omit-frame-pointer + +user-objs-$(CONFIG_TTY_LOG) += tty_log.o + +USER_OBJS := $(user-objs-y) config.o helper.o main.o process.o tempfile.o \ +	time.o tty_log.o umid.o user_util.o frame.o + +include arch/um/scripts/Makefile.rules + +targets += config.c + +# Be careful with the below Sed code - sed is pitfall-rich! +# We use sed to lower build requirements, for "embedded" builders for instance. + +$(obj)/config.tmp: $(objtree)/.config FORCE +	$(call if_changed,quote1) + +quiet_cmd_quote1 = QUOTE   $@ +      cmd_quote1 = sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' \ +		   $< > $@ + +$(obj)/config.c: $(src)/config.c.in $(obj)/config.tmp FORCE +	$(call if_changed,quote2) + +quiet_cmd_quote2 = QUOTE   $@ +      cmd_quote2 = sed -e '/CONFIG/{'          \ +		  -e 's/"CONFIG"\;/""/'        \ +		  -e 'r $(obj)/config.tmp'     \ +		  -e 'a""\;'                   \ +		  -e '}'                       \ +		  $< > $@ diff --git a/arch/um/kernel/checksum.c b/arch/um/kernel/checksum.c new file mode 100644 index 00000000000..e69b2be951d --- /dev/null +++ b/arch/um/kernel/checksum.c @@ -0,0 +1,36 @@ +#include "asm/uaccess.h" +#include "linux/errno.h" +#include "linux/module.h" + +unsigned int arch_csum_partial(const unsigned char *buff, int len, int sum); + +unsigned int csum_partial(unsigned char *buff, int len, int sum) +{ +        return arch_csum_partial(buff, len, sum); +} + +EXPORT_SYMBOL(csum_partial); + +unsigned int csum_partial_copy_to(const unsigned char *src, +                                  unsigned char __user *dst, int len, int sum, +                                  int *err_ptr) +{ +        if(copy_to_user(dst, src, len)){ +                *err_ptr = -EFAULT; +                return(-1); +        } + +        return(arch_csum_partial(src, len, sum)); +} + +unsigned int csum_partial_copy_from(const unsigned char __user *src, +                                    unsigned char *dst,	int len, int sum, +                                    int *err_ptr) +{ +        if(copy_from_user(dst, src, len)){ +                *err_ptr = -EFAULT; +                return(-1); +        } + +        return arch_csum_partial(dst, len, sum); +} diff --git a/arch/um/kernel/config.c.in b/arch/um/kernel/config.c.in new file mode 100644 index 00000000000..c062cbfe386 --- /dev/null +++ b/arch/um/kernel/config.c.in @@ -0,0 +1,32 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include "init.h" + +static __initdata char *config = "CONFIG"; + +static int __init print_config(char *line, int *add) +{ +	printf("%s", config); +	exit(0); +} + +__uml_setup("--showconfig", print_config, +"--showconfig\n" +"    Prints the config file that this UML binary was generated from.\n\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/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S new file mode 100644 index 00000000000..715b0838a68 --- /dev/null +++ b/arch/um/kernel/dyn.lds.S @@ -0,0 +1,176 @@ +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT(ELF_FORMAT) +OUTPUT_ARCH(ELF_ARCH) +ENTRY(_start) +jiffies = jiffies_64; + +SECTIONS +{ +  PROVIDE (__executable_start = START); +  . = START + SIZEOF_HEADERS; +  .interp         : { *(.interp) } +  /* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start +   * is remapped.*/ +  __binary_start = .; +  . = ALIGN(4096);		/* Init code and data */ +  _stext = .; +  __init_begin = .; +  .init.text : { +	_sinittext = .; +	*(.init.text) +	_einittext = .; +  } + +  . = ALIGN(4096); + +  /* Read-only sections, merged into text segment: */ +  .hash           : { *(.hash) } +  .dynsym         : { *(.dynsym) } +  .dynstr         : { *(.dynstr) } +  .gnu.version    : { *(.gnu.version) } +  .gnu.version_d  : { *(.gnu.version_d) } +  .gnu.version_r  : { *(.gnu.version_r) } +  .rel.init       : { *(.rel.init) } +  .rela.init      : { *(.rela.init) } +  .rel.text       : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } +  .rela.text      : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } +  .rel.fini       : { *(.rel.fini) } +  .rela.fini      : { *(.rela.fini) } +  .rel.rodata     : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } +  .rela.rodata    : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } +  .rel.data       : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } +  .rela.data      : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } +  .rel.tdata	  : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } +  .rela.tdata	  : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } +  .rel.tbss	  : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } +  .rela.tbss	  : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } +  .rel.ctors      : { *(.rel.ctors) } +  .rela.ctors     : { *(.rela.ctors) } +  .rel.dtors      : { *(.rel.dtors) } +  .rela.dtors     : { *(.rela.dtors) } +  .rel.got        : { *(.rel.got) } +  .rela.got       : { *(.rela.got) } +  .rel.bss        : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } +  .rela.bss       : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } +  .rel.plt        : { *(.rel.plt) } +  .rela.plt       : { *(.rela.plt) } +  .init           : { +    KEEP (*(.init)) +  } =0x90909090 +  .plt            : { *(.plt) } +  .text           : { +    *(.text) +    SCHED_TEXT +    LOCK_TEXT +    *(.fixup) +    *(.stub .text.* .gnu.linkonce.t.*) +    /* .gnu.warning sections are handled specially by elf32.em.  */ +    *(.gnu.warning) +  } =0x90909090 +  .fini           : { +    KEEP (*(.fini)) +  } =0x90909090 + +  .kstrtab : { *(.kstrtab) } + +  #include "asm/common.lds.S" + +  init.data : { *(.init.data) } + +  /* Ensure the __preinit_array_start label is properly aligned.  We +     could instead move the label definition inside the section, but +     the linker would then create the section even if it turns out to +     be empty, which isn't pretty.  */ +  . = ALIGN(32 / 8); +  .preinit_array     : { *(.preinit_array) } +  .init_array     : { *(.init_array) } +  .fini_array     : { *(.fini_array) } +  .data           : { +    . = ALIGN(KERNEL_STACK_SIZE);		/* init_task */ +    *(.data.init_task) +    *(.data .data.* .gnu.linkonce.d.*) +    SORT(CONSTRUCTORS) +  } +  .data1          : { *(.data1) } +  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) } +  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } +  .eh_frame       : { KEEP (*(.eh_frame)) } +  .gcc_except_table   : { *(.gcc_except_table) } +  .dynamic        : { *(.dynamic) } +  .ctors          : { +    /* gcc uses crtbegin.o to find the start of +       the constructors, so we make sure it is +       first.  Because this is a wildcard, it +       doesn't matter if the user does not +       actually link against crtbegin.o; the +       linker won't look for a file to match a +       wildcard.  The wildcard also means that it +       doesn't matter which directory crtbegin.o +       is in.  */ +    KEEP (*crtbegin.o(.ctors)) +    /* We don't want to include the .ctor section from +       from the crtend.o file until after the sorted ctors. +       The .ctor section from the crtend file contains the +       end of ctors marker and it must be last */ +    KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) +    KEEP (*(SORT(.ctors.*))) +    KEEP (*(.ctors)) +  } +  .dtors          : { +    KEEP (*crtbegin.o(.dtors)) +    KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) +    KEEP (*(SORT(.dtors.*))) +    KEEP (*(.dtors)) +  } +  .jcr            : { KEEP (*(.jcr)) } +  .got            : { *(.got.plt) *(.got) } +  _edata = .; +  PROVIDE (edata = .); +  __bss_start = .; +  .bss            : { +   *(.dynbss) +   *(.bss .bss.* .gnu.linkonce.b.*) +   *(COMMON) +   /* Align here to ensure that the .bss section occupies space up to +      _end.  Align after .bss to ensure correct alignment even if the +      .bss section disappears because there are no input sections.  */ +   . = ALIGN(32 / 8); +  . = ALIGN(32 / 8); +  } +  _end = .; +  PROVIDE (end = .); +   /* Stabs debugging sections.  */ +  .stab          0 : { *(.stab) } +  .stabstr       0 : { *(.stabstr) } +  .stab.excl     0 : { *(.stab.excl) } +  .stab.exclstr  0 : { *(.stab.exclstr) } +  .stab.index    0 : { *(.stab.index) } +  .stab.indexstr 0 : { *(.stab.indexstr) } +  .comment       0 : { *(.comment) } +  /* DWARF debug sections. +     Symbols in the DWARF debugging sections are relative to the beginning +     of the section so we begin them at 0.  */ +  /* DWARF 1 */ +  .debug          0 : { *(.debug) } +  .line           0 : { *(.line) } +  /* GNU DWARF 1 extensions */ +  .debug_srcinfo  0 : { *(.debug_srcinfo) } +  .debug_sfnames  0 : { *(.debug_sfnames) } +  /* DWARF 1.1 and DWARF 2 */ +  .debug_aranges  0 : { *(.debug_aranges) } +  .debug_pubnames 0 : { *(.debug_pubnames) } +  /* DWARF 2 */ +  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) } +  .debug_abbrev   0 : { *(.debug_abbrev) } +  .debug_line     0 : { *(.debug_line) } +  .debug_frame    0 : { *(.debug_frame) } +  .debug_str      0 : { *(.debug_str) } +  .debug_loc      0 : { *(.debug_loc) } +  .debug_macinfo  0 : { *(.debug_macinfo) } +  /* SGI/MIPS DWARF 2 extensions */ +  .debug_weaknames 0 : { *(.debug_weaknames) } +  .debug_funcnames 0 : { *(.debug_funcnames) } +  .debug_typenames 0 : { *(.debug_typenames) } +  .debug_varnames  0 : { *(.debug_varnames) } +} diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c new file mode 100644 index 00000000000..49ddabe69be --- /dev/null +++ b/arch/um/kernel/exec_kern.c @@ -0,0 +1,91 @@ +/*  + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/slab.h" +#include "linux/smp_lock.h" +#include "linux/ptrace.h" +#include "asm/ptrace.h" +#include "asm/pgtable.h" +#include "asm/tlbflush.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "kern.h" +#include "irq_user.h" +#include "tlb.h" +#include "2_5compat.h" +#include "os.h" +#include "time_user.h" +#include "choose-mode.h" +#include "mode_kern.h" + +void flush_thread(void) +{ +	CHOOSE_MODE(flush_thread_tt(), flush_thread_skas()); +} + +void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp) +{ +	CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp); +} + +extern void log_exec(char **argv, void *tty); + +static long execve1(char *file, char __user * __user *argv, +		    char *__user __user *env) +{ +        long error; + +#ifdef CONFIG_TTY_LOG +	log_exec(argv, current->tty); +#endif +        error = do_execve(file, argv, env, ¤t->thread.regs); +        if (error == 0){ +		task_lock(current); +                current->ptrace &= ~PT_DTRACE; +		task_unlock(current); +                set_cmdline(current_cmd()); +        } +        return(error); +} + +long um_execve(char *file, char __user *__user *argv, char __user *__user *env) +{ +	long err; + +	err = execve1(file, argv, env); +	if(!err) +		do_longjmp(current->thread.exec_buf, 1); +	return(err); +} + +long sys_execve(char *file, char __user *__user *argv, +		char __user *__user *env) +{ +	long error; +	char *filename; + +	lock_kernel(); +	filename = getname((char __user *) file); +	error = PTR_ERR(filename); +	if (IS_ERR(filename)) goto out; +	error = execve1(filename, argv, env); +	putname(filename); + out: +	unlock_kernel(); +	return(error); +} + +/* + * 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/exitcode.c b/arch/um/kernel/exitcode.c new file mode 100644 index 00000000000..0ea87f24b36 --- /dev/null +++ b/arch/um/kernel/exitcode.c @@ -0,0 +1,73 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/ctype.h" +#include "linux/proc_fs.h" +#include "asm/uaccess.h" + +/* If read and write race, the read will still atomically read a valid + * value. + */ +int uml_exitcode = 0; + +static int read_proc_exitcode(char *page, char **start, off_t off, +			      int count, int *eof, void *data) +{ +	int len; + +	len = sprintf(page, "%d\n", uml_exitcode); +	len -= off; +	if(len <= off+count) *eof = 1; +	*start = page + off; +	if(len > count) len = count; +	if(len < 0) len = 0; +	return(len); +} + +static int write_proc_exitcode(struct file *file, const char __user *buffer, +			       unsigned long count, void *data) +{ +	char *end, buf[sizeof("nnnnn\0")]; +	int tmp; + +	if(copy_from_user(buf, buffer, count)) +		return(-EFAULT); +	tmp = simple_strtol(buf, &end, 0); +	if((*end != '\0') && !isspace(*end)) +		return(-EINVAL); +	uml_exitcode = tmp; +	return(count); +} + +static int make_proc_exitcode(void) +{ +	struct proc_dir_entry *ent; + +	ent = create_proc_entry("exitcode", 0600, &proc_root); +	if(ent == NULL){ +		printk("make_proc_exitcode : Failed to register " +		       "/proc/exitcode\n"); +		return(0); +	} + +	ent->read_proc = read_proc_exitcode; +	ent->write_proc = write_proc_exitcode; +	 +	return(0); +} + +__initcall(make_proc_exitcode); + +/* + * 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/gmon_syms.c b/arch/um/kernel/gmon_syms.c new file mode 100644 index 00000000000..2c86e7fdb01 --- /dev/null +++ b/arch/um/kernel/gmon_syms.c @@ -0,0 +1,34 @@ +/*  + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" + +extern void __bb_init_func(void *); +EXPORT_SYMBOL(__bb_init_func); + +/* This is defined (and referred to in profiling stub code) only by some GCC + * versions in libgcov. + * + * Since SuSE backported the fix, we cannot handle it depending on GCC version. + * So, unconditinally export it. But also give it a weak declaration, which will + * be overriden by any other one. + */ + +extern void __gcov_init(void *) __attribute__((weak)); +EXPORT_SYMBOL(__gcov_init); + +extern void __gcov_merge_add(void *) __attribute__((weak)); +EXPORT_SYMBOL(__gcov_merge_add); + +/* + * 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/gprof_syms.c b/arch/um/kernel/gprof_syms.c new file mode 100644 index 00000000000..9244f018d44 --- /dev/null +++ b/arch/um/kernel/gprof_syms.c @@ -0,0 +1,20 @@ +/*  + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" + +extern void mcount(void); +EXPORT_SYMBOL(mcount); + +/* + * 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/helper.c b/arch/um/kernel/helper.c new file mode 100644 index 00000000000..13b1f5c2f7e --- /dev/null +++ b/arch/um/kernel/helper.c @@ -0,0 +1,173 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sched.h> +#include <sys/signal.h> +#include <sys/wait.h> +#include "user.h" +#include "kern_util.h" +#include "user_util.h" +#include "os.h" + +struct helper_data { +	void (*pre_exec)(void*); +	void *pre_data; +	char **argv; +	int fd; +}; + +/* Debugging aid, changed only from gdb */ +int helper_pause = 0; + +static void helper_hup(int sig) +{ +} + +static int helper_child(void *arg) +{ +	struct helper_data *data = arg; +	char **argv = data->argv; +	int errval; + +	if(helper_pause){ +		signal(SIGHUP, helper_hup); +		pause(); +	} +	if(data->pre_exec != NULL) +		(*data->pre_exec)(data->pre_data); +	execvp(argv[0], argv); +	errval = errno; +	printk("execvp of '%s' failed - errno = %d\n", argv[0], errno); +	os_write_file(data->fd, &errval, sizeof(errval)); +	os_kill_process(os_getpid(), 0); +	return(0); +} + +/* Returns either the pid of the child process we run or -E* on failure. + * XXX The alloc_stack here breaks if this is called in the tracing thread */ +int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, +	       unsigned long *stack_out) +{ +	struct helper_data data; +	unsigned long stack, sp; +	int pid, fds[2], ret, n; + +	if((stack_out != NULL) && (*stack_out != 0)) +		stack = *stack_out; +	else stack = alloc_stack(0, um_in_interrupt()); +	if(stack == 0) +		return(-ENOMEM); + +	ret = os_pipe(fds, 1, 0); +	if(ret < 0){ +		printk("run_helper : pipe failed, ret = %d\n", -ret); +		goto out_free; +	} + +	ret = os_set_exec_close(fds[1], 1); +	if(ret < 0){ +		printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n", +		       -ret); +		goto out_close; +	} + +	sp = stack + page_size() - sizeof(void *); +	data.pre_exec = pre_exec; +	data.pre_data = pre_data; +	data.argv = argv; +	data.fd = fds[1]; +	pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data); +	if(pid < 0){ +		printk("run_helper : clone failed, errno = %d\n", errno); +		ret = -errno; +		goto out_close; +	} + +	os_close_file(fds[1]); +	fds[1] = -1; + +	/*Read the errno value from the child.*/ +	n = os_read_file(fds[0], &ret, sizeof(ret)); +	if(n < 0){ +		printk("run_helper : read on pipe failed, ret = %d\n", -n); +		ret = n; +		os_kill_process(pid, 1); +	} +	else if(n != 0){ +		CATCH_EINTR(n = waitpid(pid, NULL, 0)); +		ret = -errno; +	} else { +		ret = pid; +	} + +out_close: +	if (fds[1] != -1) +		os_close_file(fds[1]); +	os_close_file(fds[0]); +out_free: +	if(stack_out == NULL) +		free_stack(stack, 0); +	else *stack_out = stack; +	return(ret); +} + +int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,  +		      unsigned long *stack_out, int stack_order) +{ +	unsigned long stack, sp; +	int pid, status; + +	stack = alloc_stack(stack_order, um_in_interrupt()); +	if(stack == 0) return(-ENOMEM); + +	sp = stack + (page_size() << stack_order) - sizeof(void *); +	pid = clone(proc, (void *) sp, flags | SIGCHLD, arg); +	if(pid < 0){ +		printk("run_helper_thread : clone failed, errno = %d\n",  +		       errno); +		return(-errno); +	} +	if(stack_out == NULL){ +		CATCH_EINTR(pid = waitpid(pid, &status, 0)); +		if(pid < 0){ +			printk("run_helper_thread - wait failed, errno = %d\n", +			       errno); +			pid = -errno; +		} +		if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) +			printk("run_helper_thread - thread returned status " +			       "0x%x\n", status); +		free_stack(stack, stack_order); +	} +	else *stack_out = stack; +	return(pid); +} + +int helper_wait(int pid, int block) +{ +	int ret; + +	CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG)); +	if(ret < 0){ +		printk("helper_wait : waitpid failed, errno = %d\n", errno); +		return(-errno); +	} +	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/init_task.c b/arch/um/kernel/init_task.c new file mode 100644 index 00000000000..cd7c85be0a1 --- /dev/null +++ b/arch/um/kernel/init_task.c @@ -0,0 +1,61 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/mm.h" +#include "linux/module.h" +#include "linux/sched.h" +#include "linux/init_task.h" +#include "linux/mqueue.h" +#include "asm/uaccess.h" +#include "asm/pgtable.h" +#include "user_util.h" +#include "mem_user.h" + +static struct fs_struct init_fs = INIT_FS; +struct mm_struct init_mm = INIT_MM(init_mm); +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +EXPORT_SYMBOL(init_mm); + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ + +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); + +/* + * Initial thread structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ + +union thread_union init_thread_union  +__attribute__((__section__(".data.init_task"))) =  +{ INIT_THREAD_INFO(init_task) }; + +void unprotect_stack(unsigned long stack) +{ +	protect_memory(stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE,  +		       1, 1, 0, 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: + */ diff --git a/arch/um/kernel/initrd_kern.c b/arch/um/kernel/initrd_kern.c new file mode 100644 index 00000000000..fc568af468b --- /dev/null +++ b/arch/um/kernel/initrd_kern.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/initrd.h" +#include "asm/types.h" +#include "user_util.h" +#include "kern_util.h" +#include "initrd.h" +#include "init.h" +#include "os.h" + +/* Changed by uml_initrd_setup, which is a setup */ +static char *initrd __initdata = NULL; + +static int __init read_initrd(void) +{ +	void *area; +	long long size; +	int err; + +	if(initrd == NULL) return 0; +	err = os_file_size(initrd, &size); +	if(err) return 0; +	area = alloc_bootmem(size); +	if(area == NULL) return 0; +	if(load_initrd(initrd, area, size) == -1) return 0; +	initrd_start = (unsigned long) area; +	initrd_end = initrd_start + size; +	return 0; +} + +__uml_postsetup(read_initrd); + +static int __init uml_initrd_setup(char *line, int *add) +{ +	initrd = line; +	return 0; +} + +__uml_setup("initrd=", uml_initrd_setup,  +"initrd=<initrd image>\n" +"    This is used to boot UML from an initrd image.  The argument is the\n" +"    name of the file containing the image.\n\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/kernel/initrd_user.c b/arch/um/kernel/initrd_user.c new file mode 100644 index 00000000000..cb90681e151 --- /dev/null +++ b/arch/um/kernel/initrd_user.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "initrd.h" +#include "os.h" + +int load_initrd(char *filename, void *buf, int size) +{ +	int fd, n; + +	fd = os_open_file(filename, of_read(OPENFLAGS()), 0); +	if(fd < 0){ +		printk("Opening '%s' failed - err = %d\n", filename, -fd); +		return(-1); +	} +	n = os_read_file(fd, buf, size); +	if(n != size){ +		printk("Read of %d bytes from '%s' failed, err = %d\n", size, +		       filename, -n); +		return(-1); +	} + +	os_close_file(fd); +	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/kernel/irq.c b/arch/um/kernel/irq.c new file mode 100644 index 00000000000..d71e8f00810 --- /dev/null +++ b/arch/um/kernel/irq.c @@ -0,0 +1,178 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: + *	Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/module.h" +#include "linux/smp.h" +#include "linux/irq.h" +#include "linux/kernel_stat.h" +#include "linux/interrupt.h" +#include "linux/random.h" +#include "linux/slab.h" +#include "linux/file.h" +#include "linux/proc_fs.h" +#include "linux/init.h" +#include "linux/seq_file.h" +#include "linux/profile.h" +#include "linux/hardirq.h" +#include "asm/irq.h" +#include "asm/hw_irq.h" +#include "asm/atomic.h" +#include "asm/signal.h" +#include "asm/system.h" +#include "asm/errno.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "irq_user.h" +#include "irq_kern.h" + + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ +	int i = *(loff_t *) v, j; +	struct irqaction * action; +	unsigned long flags; + +	if (i == 0) { +		seq_printf(p, "           "); +		for_each_online_cpu(j) +			seq_printf(p, "CPU%d       ",j); +		seq_putc(p, '\n'); +	} + +	if (i < NR_IRQS) { +		spin_lock_irqsave(&irq_desc[i].lock, flags); +		action = irq_desc[i].action; +		if (!action)  +			goto skip; +		seq_printf(p, "%3d: ",i); +#ifndef CONFIG_SMP +		seq_printf(p, "%10u ", kstat_irqs(i)); +#else +		for_each_online_cpu(j) +			seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif +		seq_printf(p, " %14s", irq_desc[i].handler->typename); +		seq_printf(p, "  %s", action->name); + +		for (action=action->next; action; action = action->next) +			seq_printf(p, ", %s", action->name); + +		seq_putc(p, '\n'); +skip: +		spin_unlock_irqrestore(&irq_desc[i].lock, flags); +	} else if (i == NR_IRQS) { +		seq_putc(p, '\n'); +	} + +	return 0; +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +unsigned int do_IRQ(int irq, union uml_pt_regs *regs) +{ +       irq_enter(); +       __do_IRQ(irq, (struct pt_regs *) regs); +       irq_exit(); +       return 1; +} + +int um_request_irq(unsigned int irq, int fd, int type, +		   irqreturn_t (*handler)(int, void *, struct pt_regs *), +		   unsigned long irqflags, const char * devname, +		   void *dev_id) +{ +	int err; + +	err = request_irq(irq, handler, irqflags, devname, dev_id); +	if(err) +		return(err); + +	if(fd != -1) +		err = activate_fd(irq, fd, type, dev_id); +	return(err); +} +EXPORT_SYMBOL(um_request_irq); +EXPORT_SYMBOL(reactivate_fd); + +static DEFINE_SPINLOCK(irq_spinlock); + +unsigned long irq_lock(void) +{ +	unsigned long flags; + +	spin_lock_irqsave(&irq_spinlock, flags); +	return(flags); +} + +void irq_unlock(unsigned long flags) +{ +	spin_unlock_irqrestore(&irq_spinlock, flags); +} + +/*  presently hw_interrupt_type must define (startup || enable) && + *  disable && end */ +static void dummy(unsigned int irq) +{ +} + +static struct hw_interrupt_type SIGIO_irq_type = { +	.typename = "SIGIO", +	.disable = dummy, +	.enable = dummy, +	.ack = dummy, +	.end = dummy +}; + +static struct hw_interrupt_type SIGVTALRM_irq_type = { +	.typename = "SIGVTALRM", +	.shutdown = dummy, /* never called */ +	.disable = dummy, +	.enable = dummy, +	.ack = dummy, +	.end = dummy +}; + +void __init init_IRQ(void) +{ +	int i; + +	irq_desc[TIMER_IRQ].status = IRQ_DISABLED; +	irq_desc[TIMER_IRQ].action = NULL; +	irq_desc[TIMER_IRQ].depth = 1; +	irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type; +	enable_irq(TIMER_IRQ); +	for(i=1;i<NR_IRQS;i++){ +		irq_desc[i].status = IRQ_DISABLED; +		irq_desc[i].action = NULL; +		irq_desc[i].depth = 1; +		irq_desc[i].handler = &SIGIO_irq_type; +		enable_irq(i); +	} +	init_irq_signals(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/kernel/irq_user.c b/arch/um/kernel/irq_user.c new file mode 100644 index 00000000000..6d6f9484b88 --- /dev/null +++ b/arch/um/kernel/irq_user.c @@ -0,0 +1,443 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <sys/poll.h> +#include <sys/types.h> +#include <sys/time.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_user.h" +#include "sigio.h" +#include "irq_user.h" +#include "os.h" + +struct irq_fd { +	struct irq_fd *next; +	void *id; +	int fd; +	int type; +	int irq; +	int pid; +	int events; +	int current_events; +	int freed; +}; + +static struct irq_fd *active_fds = NULL; +static struct irq_fd **last_irq_ptr = &active_fds; + +static struct pollfd *pollfds = NULL; +static int pollfds_num = 0; +static int pollfds_size = 0; + +extern int io_count, intr_count; + +void sigio_handler(int sig, union uml_pt_regs *regs) +{ +	struct irq_fd *irq_fd, *next; +	int i, n; + +	if(smp_sigio_handler()) return; +	while(1){ +		n = poll(pollfds, pollfds_num, 0); +		if(n < 0){ +			if(errno == EINTR) continue; +			printk("sigio_handler : poll returned %d, " +			       "errno = %d\n", n, errno); +			break; +		} +		if(n == 0) break; + +		irq_fd = active_fds; +		for(i = 0; i < pollfds_num; i++){ +			if(pollfds[i].revents != 0){ +				irq_fd->current_events = pollfds[i].revents; +				pollfds[i].fd = -1; +			} +			irq_fd = irq_fd->next; +		} + +		for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){ +			next = 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_irq_by_irq_and_dev(irq_fd->irq, +								irq_fd->id); +				} +			} +		} +	} +} + +int activate_ipi(int fd, int pid) +{ +	return(os_set_fd_async(fd, pid)); +} + +static void maybe_sigio_broken(int fd, int type) +{ +	if(isatty(fd)){ +		if((type == IRQ_WRITE) && !pty_output_sigio){ +			write_sigio_workaround(); +			add_sigio_fd(fd, 0); +		} +		else if((type == IRQ_READ) && !pty_close_sigio){ +			write_sigio_workaround(); +			add_sigio_fd(fd, 1);			 +		} +	} +} + +int activate_fd(int irq, int fd, int type, void *dev_id) +{ +	struct pollfd *tmp_pfd; +	struct irq_fd *new_fd, *irq_fd; +	unsigned long flags; +	int pid, events, err, n, size; + +	pid = os_getpid(); +	err = os_set_fd_async(fd, pid); +	if(err < 0) +		goto out; + +	new_fd = um_kmalloc(sizeof(*new_fd)); +	err = -ENOMEM; +	if(new_fd == NULL) +		goto out; + +	if(type == IRQ_READ) events = POLLIN | POLLPRI; +	else events = POLLOUT; +	*new_fd = ((struct irq_fd) { .next  		= NULL, +				     .id 		= dev_id, +				     .fd 		= fd, +				     .type 		= type, +				     .irq 		= irq, +				     .pid  		= pid, +				     .events 		= events, +				     .current_events 	= 0, +				     .freed 		= 0  } ); + +	/* Critical section - locked by a spinlock because this stuff can +	 * be changed from interrupt handlers.  The stuff above is done  +	 * outside the lock because it allocates memory. +	 */ + +	/* Actually, it only looks like it can be called from interrupt +	 * context.  The culprit is reactivate_fd, which calls  +	 * maybe_sigio_broken, which calls write_sigio_workaround, +	 * which calls activate_fd.  However, write_sigio_workaround should +	 * only be called once, at boot time.  That would make it clear that +	 * this is called only from process context, and can be locked with +	 * a semaphore. +	 */ +	flags = irq_lock(); +	for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ +		if((irq_fd->fd == fd) && (irq_fd->type == type)){ +			printk("Registering fd %d twice\n", fd); +			printk("Irqs : %d, %d\n", irq_fd->irq, irq); +			printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id); +			goto out_unlock; +		} +	} + +	n = pollfds_num; +	if(n == pollfds_size){ +		while(1){ +			/* Here we have to drop the lock in order to call  +			 * kmalloc, which might sleep.  If something else +			 * came in and changed the pollfds array, we free +			 * the buffer and try again. +			 */ +			irq_unlock(flags); +			size = (pollfds_num + 1) * sizeof(pollfds[0]); +			tmp_pfd = um_kmalloc(size); +			flags = irq_lock(); +			if(tmp_pfd == NULL) +				goto out_unlock; +			if(n == pollfds_size) +				break; +			kfree(tmp_pfd); +		} +		if(pollfds != NULL){ +			memcpy(tmp_pfd, pollfds, +			       sizeof(pollfds[0]) * pollfds_size); +			kfree(pollfds); +		} +		pollfds = tmp_pfd; +		pollfds_size++; +	} + +	if(type == IRQ_WRITE)  +		fd = -1; + +	pollfds[pollfds_num] = ((struct pollfd) { .fd 	= fd, +						  .events 	= events, +						  .revents 	= 0 }); +	pollfds_num++; + +	*last_irq_ptr = new_fd; +	last_irq_ptr = &new_fd->next; + +	irq_unlock(flags); + +	/* This calls activate_fd, so it has to be outside the critical +	 * section. +	 */ +	maybe_sigio_broken(fd, type); + +	return(0); + + out_unlock: +	irq_unlock(flags); +	kfree(new_fd); + out: +	return(err); +} + +static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) +{ +	struct irq_fd **prev; +	unsigned long flags; +	int i = 0; + +	flags = irq_lock(); +	prev = &active_fds; +	while(*prev != NULL){ +		if((*test)(*prev, arg)){ +			struct irq_fd *old_fd = *prev; +			if((pollfds[i].fd != -1) &&  +			   (pollfds[i].fd != (*prev)->fd)){ +				printk("free_irq_by_cb - mismatch between " +				       "active_fds and pollfds, fd %d vs %d\n", +				       (*prev)->fd, pollfds[i].fd); +				goto out; +			} +			memcpy(&pollfds[i], &pollfds[i + 1], +			       (pollfds_num - i - 1) * sizeof(pollfds[0])); +			pollfds_num--; +			if(last_irq_ptr == &old_fd->next)  +				last_irq_ptr = prev; +			*prev = (*prev)->next; +			if(old_fd->type == IRQ_WRITE)  +				ignore_sigio_fd(old_fd->fd); +			kfree(old_fd); +			continue; +		} +		prev = &(*prev)->next; +		i++; +	} + out: +	irq_unlock(flags); +} + +struct irq_and_dev { +	int irq; +	void *dev; +}; + +static int same_irq_and_dev(struct irq_fd *irq, void *d) +{ +	struct irq_and_dev *data = d; + +	return((irq->irq == data->irq) && (irq->id == data->dev)); +} + +void free_irq_by_irq_and_dev(unsigned int irq, void *dev) +{ +	struct irq_and_dev data = ((struct irq_and_dev) { .irq  = irq, +							  .dev  = dev }); + +	free_irq_by_cb(same_irq_and_dev, &data); +} + +static int same_fd(struct irq_fd *irq, void *fd) +{ +	return(irq->fd == *((int *) fd)); +} + +void free_irq_by_fd(int fd) +{ +	free_irq_by_cb(same_fd, &fd); +} + +static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) +{ +	struct irq_fd *irq; +	int i = 0; + +	for(irq=active_fds; irq != NULL; irq = irq->next){ +		if((irq->fd == fd) && (irq->irq == irqnum)) break; +		i++; +	} +	if(irq == NULL){ +		printk("find_irq_by_fd doesn't have descriptor %d\n", fd); +		goto out; +	} +	if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){ +		printk("find_irq_by_fd - mismatch between active_fds and " +		       "pollfds, fd %d vs %d, need %d\n", irq->fd,  +		       pollfds[i].fd, fd); +		irq = NULL; +		goto out; +	} +	*index_out = i; + 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; +	unsigned long flags; +	int i; + +	flags = irq_lock(); +	irq = find_irq_by_fd(fd, irqnum, &i); +	if(irq == NULL){ +		irq_unlock(flags); +		return; +	} + +	pollfds[i].fd = irq->fd; + +	irq_unlock(flags); + +	/* This calls activate_fd, so it has to be outside the critical +	 * section. +	 */ +	maybe_sigio_broken(fd, irq->type); +} + +void deactivate_fd(int fd, int irqnum) +{ +	struct irq_fd *irq; +	unsigned long flags; +	int i; + +	flags = irq_lock(); +	irq = find_irq_by_fd(fd, irqnum, &i); +	if(irq == NULL) +		goto out; +	pollfds[i].fd = -1; + out: +	irq_unlock(flags); +} + +int deactivate_all_fds(void) +{ +	struct irq_fd *irq; +	int err; + +	for(irq=active_fds;irq != NULL;irq = irq->next){ +		err = os_clear_fd_async(irq->fd); +		if(err) +			return(err); +	} +	/* If there is a signal already queued, after unblocking ignore it */ +	set_handler(SIGIO, SIG_IGN, 0, -1); + +	return(0); +} + +void forward_ipi(int fd, int pid) +{ +	int err; + +	err = os_set_owner(fd, pid); +	if(err < 0) +		printk("forward_ipi: set_owner failed, fd = %d, me = %d, " +		       "target = %d, err = %d\n", fd, os_getpid(), pid, -err); +} + +void forward_interrupts(int pid) +{ +	struct irq_fd *irq; +	unsigned long flags; +	int err; + +	flags = irq_lock(); +	for(irq=active_fds;irq != NULL;irq = irq->next){ +		err = os_set_owner(irq->fd, pid); +		if(err < 0){ +			/* XXX Just remove the irq rather than +			 * print out an infinite stream of these +			 */ +			printk("Failed to forward %d to pid %d, err = %d\n", +			       irq->fd, pid, -err); +		} + +		irq->pid = pid; +	} +	irq_unlock(flags); +} + +void init_irq_signals(int on_sigstack) +{ +	__sighandler_t h; +	int flags; + +	flags = on_sigstack ? SA_ONSTACK : 0; +	if(timer_irq_inited) h = (__sighandler_t) alarm_handler; +	else h = boot_timer_handler; + +	set_handler(SIGVTALRM, h, flags | SA_RESTART,  +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1); +	set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART, +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	signal(SIGWINCH, SIG_IGN); +} + +/* + * 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/ksyms.c b/arch/um/kernel/ksyms.c new file mode 100644 index 00000000000..b41d3397d07 --- /dev/null +++ b/arch/um/kernel/ksyms.c @@ -0,0 +1,137 @@ +/*  + * Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/module.h" +#include "linux/string.h" +#include "linux/smp_lock.h" +#include "linux/spinlock.h" +#include "linux/highmem.h" +#include "asm/current.h" +#include "asm/delay.h" +#include "asm/processor.h" +#include "asm/unistd.h" +#include "asm/pgalloc.h" +#include "asm/pgtable.h" +#include "asm/page.h" +#include "asm/tlbflush.h" +#include "kern_util.h" +#include "user_util.h" +#include "mem_user.h" +#include "os.h" +#include "helper.h" + +EXPORT_SYMBOL(stop); +EXPORT_SYMBOL(uml_physmem); +EXPORT_SYMBOL(set_signals); +EXPORT_SYMBOL(get_signals); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(__const_udelay); +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(sys_waitpid); +EXPORT_SYMBOL(task_size); +EXPORT_SYMBOL(flush_tlb_range); +EXPORT_SYMBOL(host_task_size); +EXPORT_SYMBOL(arch_validate); +EXPORT_SYMBOL(get_kmem_end); + +EXPORT_SYMBOL(page_to_phys); +EXPORT_SYMBOL(phys_to_page); +EXPORT_SYMBOL(high_physmem); +EXPORT_SYMBOL(empty_zero_page); +EXPORT_SYMBOL(um_virt_to_phys); +EXPORT_SYMBOL(__virt_to_page); +EXPORT_SYMBOL(to_phys); +EXPORT_SYMBOL(to_virt); +EXPORT_SYMBOL(mode_tt); +EXPORT_SYMBOL(handle_page_fault); +EXPORT_SYMBOL(find_iomem); +EXPORT_SYMBOL(end_iomem); + +#ifdef CONFIG_MODE_TT +EXPORT_SYMBOL(strncpy_from_user_tt); +EXPORT_SYMBOL(copy_from_user_tt); +EXPORT_SYMBOL(copy_to_user_tt); +#endif + +#ifdef CONFIG_MODE_SKAS +EXPORT_SYMBOL(strncpy_from_user_skas); +EXPORT_SYMBOL(copy_to_user_skas); +EXPORT_SYMBOL(copy_from_user_skas); +#endif +EXPORT_SYMBOL(uml_strdup); + +EXPORT_SYMBOL(os_stat_fd); +EXPORT_SYMBOL(os_stat_file); +EXPORT_SYMBOL(os_access); +EXPORT_SYMBOL(os_print_error); +EXPORT_SYMBOL(os_get_exec_close); +EXPORT_SYMBOL(os_set_exec_close); +EXPORT_SYMBOL(os_getpid); +EXPORT_SYMBOL(os_open_file); +EXPORT_SYMBOL(os_read_file); +EXPORT_SYMBOL(os_write_file); +EXPORT_SYMBOL(os_seek_file); +EXPORT_SYMBOL(os_lock_file); +EXPORT_SYMBOL(os_ioctl_generic); +EXPORT_SYMBOL(os_pipe); +EXPORT_SYMBOL(os_file_type); +EXPORT_SYMBOL(os_file_mode); +EXPORT_SYMBOL(os_file_size); +EXPORT_SYMBOL(os_flush_stdout); +EXPORT_SYMBOL(os_close_file); +EXPORT_SYMBOL(os_set_fd_async); +EXPORT_SYMBOL(os_set_fd_block); +EXPORT_SYMBOL(helper_wait); +EXPORT_SYMBOL(os_shutdown_socket); +EXPORT_SYMBOL(os_create_unix_socket); +EXPORT_SYMBOL(os_connect_socket); +EXPORT_SYMBOL(os_accept_connection); +EXPORT_SYMBOL(os_rcv_fd); +EXPORT_SYMBOL(run_helper); +EXPORT_SYMBOL(start_thread); +EXPORT_SYMBOL(dump_thread); + +EXPORT_SYMBOL(do_gettimeofday); +EXPORT_SYMBOL(do_settimeofday); + +/* This is here because UML expands open to sys_open, not to a system + * call instruction. + */ +EXPORT_SYMBOL(sys_open); +EXPORT_SYMBOL(sys_lseek); +EXPORT_SYMBOL(sys_read); +EXPORT_SYMBOL(sys_wait4); + +#ifdef CONFIG_SMP + +/* required for SMP */ + +extern void FASTCALL( __write_lock_failed(rwlock_t *rw)); +EXPORT_SYMBOL(__write_lock_failed); + +extern void FASTCALL( __read_lock_failed(rwlock_t *rw)); +EXPORT_SYMBOL(__read_lock_failed); + +#endif + +#ifdef CONFIG_HIGHMEM +EXPORT_SYMBOL(kmap); +EXPORT_SYMBOL(kunmap); +EXPORT_SYMBOL(kmap_atomic); +EXPORT_SYMBOL(kunmap_atomic); +EXPORT_SYMBOL(kmap_atomic_to_page); +#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/main.c b/arch/um/kernel/main.c new file mode 100644 index 00000000000..a17c49703f9 --- /dev/null +++ b/arch/um/kernel/main.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <sys/resource.h> +#include <sys/mman.h> +#include <sys/user.h> +#include <asm/page.h> +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "signal_user.h" +#include "time_user.h" +#include "irq_user.h" +#include "user.h" +#include "init.h" +#include "mode.h" +#include "choose-mode.h" +#include "uml-config.h" +#include "irq_user.h" +#include "time_user.h" +#include "os.h" + +/* Set in set_stklim, which is called from main and __wrap_malloc. + * __wrap_malloc only calls it if main hasn't started. + */ +unsigned long stacksizelim; + +/* Set in main */ +char *linux_prog; + +#define PGD_BOUND (4 * 1024 * 1024) +#define STACKSIZE (8 * 1024 * 1024) +#define THREAD_NAME_LEN (256) + +static void set_stklim(void) +{ +	struct rlimit lim; + +	if(getrlimit(RLIMIT_STACK, &lim) < 0){ +		perror("getrlimit"); +		exit(1); +	} +	if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ +		lim.rlim_cur = STACKSIZE; +		if(setrlimit(RLIMIT_STACK, &lim) < 0){ +			perror("setrlimit"); +			exit(1); +		} +	} +	stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); +} + +static __init void do_uml_initcalls(void) +{ +	initcall_t *call; + +	call = &__uml_initcall_start; +	while (call < &__uml_initcall_end){; +		(*call)(); +		call++; +	} +} + +static void last_ditch_exit(int sig) +{ +	CHOOSE_MODE(kmalloc_ok = 0, (void) 0); +	signal(SIGINT, SIG_DFL); +	signal(SIGTERM, SIG_DFL); +	signal(SIGHUP, SIG_DFL); +	uml_cleanup(); +	exit(1); +} + +extern int uml_exitcode; + +extern void scan_elf_aux( char **envp); + +int main(int argc, char **argv, char **envp) +{ +	char **new_argv; +	sigset_t mask; +	int ret, i; + +	/* 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_MODE_TT +	/* Allocate memory for thread command lines */ +	if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ + +		char padding[THREAD_NAME_LEN] = { +			[ 0 ...  THREAD_NAME_LEN - 2] = ' ', '\0' +		}; + +		new_argv = malloc((argc + 2) * sizeof(char*)); +		if(!new_argv) { +			perror("Allocating extended argv"); +			exit(1); +		} + +		new_argv[0] = argv[0]; +		new_argv[1] = padding; + +		for(i = 2; i <= argc; i++) +			new_argv[i] = argv[i - 1]; +		new_argv[argc + 1] = NULL; + +		execvp(new_argv[0], new_argv); +		perror("execing with extended args"); +		exit(1); +	} +#endif + +	linux_prog = argv[0]; + +	set_stklim(); + +	new_argv = malloc((argc + 1) * sizeof(char *)); +	if(new_argv == NULL){ +		perror("Mallocing argv"); +		exit(1); +	} +	for(i=0;i<argc;i++){ +		new_argv[i] = strdup(argv[i]); +		if(new_argv[i] == NULL){ +			perror("Mallocing an arg"); +			exit(1); +		} +	} +	new_argv[argc] = NULL; + +	set_handler(SIGINT, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); +	set_handler(SIGTERM, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); +	set_handler(SIGHUP, last_ditch_exit, SA_ONESHOT | SA_NODEFER, -1); + +	scan_elf_aux( envp); + +	do_uml_initcalls(); +	ret = linux_main(argc, argv); + +	/* Disable SIGPROF - I have no idea why libc doesn't do this or turn +	 * off the profiling time, but UML dies with a SIGPROF just before +	 * exiting when profiling is active. +	 */ +	change_sig(SIGPROF, 0); + +	/* Reboot */ +	if(ret){ +		int err; + +		printf("\n"); + +		/* stop timers and set SIG*ALRM to be ignored */ +		disable_timer(); + +		/* disable SIGIO for the fds and set SIGIO to be ignored */ +		err = deactivate_all_fds(); +		if(err) +			printf("deactivate_all_fds failed, errno = %d\n", +			       -err); + +		/* Let any pending signals fire now.  This ensures +		 * that they won't be delivered after the exec, when +		 * they are definitely not expected. +		 */ +		unblock_signals(); + +		execvp(new_argv[0], new_argv); +		perror("Failed to exec kernel"); +		ret = 1; +	} +	printf("\n"); +	return(uml_exitcode); +} + +#define CAN_KMALLOC() \ +	(kmalloc_ok && CHOOSE_MODE((os_getpid() != tracing_pid), 1)) + +extern void *__real_malloc(int); + +void *__wrap_malloc(int size) +{ +	void *ret; + +	if(!CAN_KMALLOC()) +		return(__real_malloc(size)); +	else if(size <= PAGE_SIZE) /* finding contiguos pages can be hard*/ +		ret = um_kmalloc(size); +	else ret = um_vmalloc(size); + +	/* glibc people insist that if malloc fails, errno should be +	 * set by malloc as well. So we do. +	 */ +	if(ret == NULL) +		errno = ENOMEM; + +	return(ret); +} + +void *__wrap_calloc(int n, int size) +{ +	void *ptr = __wrap_malloc(n * size); + +	if(ptr == NULL) return(NULL); +	memset(ptr, 0, n * size); +	return(ptr); +} + +extern void __real_free(void *); + +extern unsigned long high_physmem; + +void __wrap_free(void *ptr) +{ +	unsigned long addr = (unsigned long) ptr; + +	/* We need to know how the allocation happened, so it can be correctly +	 * freed.  This is done by seeing what region of memory the pointer is +	 * in - +	 * 	physical memory - kmalloc/kfree +	 *	kernel virtual memory - vmalloc/vfree +	 * 	anywhere else - malloc/free +	 * If kmalloc is not yet possible, then either high_physmem and/or +	 * end_vm are still 0 (as at startup), in which case we call free, or +	 * we have set them, but anyway addr has not been allocated from those +	 * areas. So, in both cases __real_free is called. +	 * +	 * CAN_KMALLOC is checked because it would be bad to free a buffer +	 * with kmalloc/vmalloc after they have been turned off during +	 * shutdown. +	 * XXX: However, we sometimes shutdown CAN_KMALLOC temporarily, so +	 * there is a possibility for memory leaks. +	 */ + +	if((addr >= uml_physmem) && (addr < high_physmem)){ +		if(CAN_KMALLOC()) +			kfree(ptr); +	} +	else if((addr >= start_vm) && (addr < end_vm)){ +		if(CAN_KMALLOC()) +			vfree(ptr); +	} +	else __real_free(ptr); +} + +/* + * 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/mem.c b/arch/um/kernel/mem.c new file mode 100644 index 00000000000..f156661781c --- /dev/null +++ b/arch/um/kernel/mem.c @@ -0,0 +1,359 @@ +/*  + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/kernel.h" +#include "linux/mm.h" +#include "linux/bootmem.h" +#include "linux/swap.h" +#include "linux/highmem.h" +#include "linux/gfp.h" +#include "asm/page.h" +#include "asm/fixmap.h" +#include "asm/pgalloc.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mem_user.h" +#include "uml_uaccess.h" +#include "os.h" + +extern char __binary_start; + +/* Changed during early boot */ +unsigned long *empty_zero_page = NULL; +unsigned long *empty_bad_page = NULL; +pgd_t swapper_pg_dir[PTRS_PER_PGD]; +unsigned long highmem; +int kmalloc_ok = 0; + +static unsigned long brk_end; + +void unmap_physmem(void) +{ +	os_unmap_memory((void *) brk_end, uml_reserved - brk_end); +} + +static void map_cb(void *unused) +{ +	map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0); +} + +#ifdef CONFIG_HIGHMEM +static void setup_highmem(unsigned long highmem_start, +			  unsigned long highmem_len) +{ +	struct page *page; +	unsigned long highmem_pfn; +	int i; + +	highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT; +	for(i = 0; i < highmem_len >> PAGE_SHIFT; i++){ +		page = &mem_map[highmem_pfn + i]; +		ClearPageReserved(page); +		set_bit(PG_highmem, &page->flags); +		set_page_count(page, 1); +		__free_page(page); +	} +} +#endif + +void mem_init(void) +{ +	unsigned long start; + +	max_low_pfn = (high_physmem - uml_physmem) >> PAGE_SHIFT; + +        /* clear the zero-page */ +        memset((void *) empty_zero_page, 0, PAGE_SIZE); + +	/* Map in the area just after the brk now that kmalloc is about +	 * to be turned on. +	 */ +	brk_end = (unsigned long) UML_ROUND_UP(sbrk(0)); +	map_cb(NULL); +	initial_thread_cb(map_cb, NULL); +	free_bootmem(__pa(brk_end), uml_reserved - brk_end); +	uml_reserved = brk_end; + +	/* Fill in any hole at the start of the binary */ +	start = (unsigned long) &__binary_start & PAGE_MASK; +	if(uml_physmem != start){ +		map_memory(uml_physmem, __pa(uml_physmem), start - uml_physmem, +			   1, 1, 0); +	} + +	/* this will put all low memory onto the freelists */ +	totalram_pages = free_all_bootmem(); +	totalhigh_pages = highmem >> PAGE_SHIFT; +	totalram_pages += totalhigh_pages; +	num_physpages = totalram_pages; +	max_pfn = totalram_pages; +	printk(KERN_INFO "Memory: %luk available\n",  +	       (unsigned long) nr_free_pages() << (PAGE_SHIFT-10)); +	kmalloc_ok = 1; + +#ifdef CONFIG_HIGHMEM +	setup_highmem(end_iomem, highmem); +#endif +} + +static void __init fixrange_init(unsigned long start, unsigned long end,  +				 pgd_t *pgd_base) +{ +	pgd_t *pgd; +	pmd_t *pmd; +	pte_t *pte; +	int i, j; +	unsigned long vaddr; + +	vaddr = start; +	i = pgd_index(vaddr); +	j = pmd_index(vaddr); +	pgd = pgd_base + i; + +	for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) { +		pmd = (pmd_t *)pgd; +		for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) { +			if (pmd_none(*pmd)) { +				pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); +				set_pmd(pmd, __pmd(_KERNPG_TABLE +  +						   (unsigned long) __pa(pte))); +				if (pte != pte_offset_kernel(pmd, 0)) +					BUG(); +			} +			vaddr += PMD_SIZE; +		} +		j = 0; +	} +} + +#ifdef CONFIG_HIGHMEM +pte_t *kmap_pte; +pgprot_t kmap_prot; + +#define kmap_get_fixmap_pte(vaddr)					\ +	pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),\ + 			  (vaddr)), (vaddr)) + +static void __init kmap_init(void) +{ +	unsigned long kmap_vstart; + +	/* cache the first kmap pte */ +	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); +	kmap_pte = kmap_get_fixmap_pte(kmap_vstart); + +	kmap_prot = PAGE_KERNEL; +} + +static void init_highmem(void) +{ +	pgd_t *pgd; +	pud_t *pud; +	pmd_t *pmd; +	pte_t *pte; +	unsigned long vaddr; + +	/* +	 * Permanent kmaps: +	 */ +	vaddr = PKMAP_BASE; +	fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir); + +	pgd = swapper_pg_dir + pgd_index(vaddr); +	pud = pud_offset(pgd, vaddr); +	pmd = pmd_offset(pud, vaddr); +	pte = pte_offset_kernel(pmd, vaddr); +	pkmap_page_table = pte; + +	kmap_init(); +} +#endif /* CONFIG_HIGHMEM */ + +static void __init fixaddr_user_init( void) +{ +#if CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA +	long size = FIXADDR_USER_END - FIXADDR_USER_START; +	pgd_t *pgd; +	pud_t *pud; +	pmd_t *pmd; +	pte_t *pte; +	unsigned long paddr, vaddr = FIXADDR_USER_START; + +	if (  ! size ) +		return; + +	fixrange_init( FIXADDR_USER_START, FIXADDR_USER_END, swapper_pg_dir); +	paddr = (unsigned long)alloc_bootmem_low_pages( size); +	memcpy( (void *)paddr, (void *)FIXADDR_USER_START, size); +	paddr = __pa(paddr); +	for ( ; size > 0; size-=PAGE_SIZE, vaddr+=PAGE_SIZE, paddr+=PAGE_SIZE){ +		pgd = swapper_pg_dir + pgd_index(vaddr); +		pud = pud_offset(pgd, vaddr); +		pmd = pmd_offset(pud, vaddr); +		pte = pte_offset_kernel(pmd, vaddr); +		pte_set_val( (*pte), paddr, PAGE_READONLY); +	} +#endif +} + +void paging_init(void) +{ +	unsigned long zones_size[MAX_NR_ZONES], vaddr; +	int i; + +	empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); +	empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); +	for(i=0;i<sizeof(zones_size)/sizeof(zones_size[0]);i++)  +		zones_size[i] = 0; +	zones_size[0] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT); +	zones_size[2] = highmem >> PAGE_SHIFT; +	free_area_init(zones_size); + +	/* +	 * Fixed mappings, only the page table structure has to be +	 * created - mappings will be set by set_fixmap(): +	 */ +	vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; +	fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir); + +	fixaddr_user_init(); + +#ifdef CONFIG_HIGHMEM +	init_highmem(); +#endif +} + +struct page *arch_validate(struct page *page, int mask, int order) +{ +	unsigned long addr, zero = 0; +	int i; + + again: +	if(page == NULL) return(page); +	if(PageHighMem(page)) return(page); + +	addr = (unsigned long) page_address(page); +	for(i = 0; i < (1 << order); i++){ +		current->thread.fault_addr = (void *) addr; +		if(__do_copy_to_user((void __user *) addr, &zero, +				     sizeof(zero), +				     ¤t->thread.fault_addr, +				     ¤t->thread.fault_catcher)){ +			if(!(mask & __GFP_WAIT)) return(NULL); +			else break; +		} +		addr += PAGE_SIZE; +	} + +	if(i == (1 << order)) return(page); +	page = alloc_pages(mask, order); +	goto again; +} + +/* This can't do anything because nothing in the kernel image can be freed + * since it's not in kernel physical memory. + */ + +void free_initmem(void) +{ +} + +#ifdef CONFIG_BLK_DEV_INITRD + +void free_initrd_mem(unsigned long start, unsigned long end) +{ +	if (start < end) +		printk ("Freeing initrd memory: %ldk freed\n",  +			(end - start) >> 10); +	for (; start < end; start += PAGE_SIZE) { +		ClearPageReserved(virt_to_page(start)); +		set_page_count(virt_to_page(start), 1); +		free_page(start); +		totalram_pages++; +	} +} +	 +#endif + +void show_mem(void) +{ +        int pfn, total = 0, reserved = 0; +        int shared = 0, cached = 0; +        int highmem = 0; +	struct page *page; + +        printk("Mem-info:\n"); +        show_free_areas(); +        printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); +        pfn = max_mapnr; +        while(pfn-- > 0) { +		page = pfn_to_page(pfn); +                total++; +                if(PageHighMem(page)) +                        highmem++; +                if(PageReserved(page)) +                        reserved++; +                else if(PageSwapCache(page)) +                        cached++; +                else if(page_count(page)) +                        shared += page_count(page) - 1; +        } +        printk("%d pages of RAM\n", total); +        printk("%d pages of HIGHMEM\n", highmem); +        printk("%d reserved pages\n", reserved); +        printk("%d pages shared\n", shared); +        printk("%d pages swap cached\n", cached); +} + +/* + * Allocate and free page tables. + */ + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ +	pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL); + +	if (pgd) { +		memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); +		memcpy(pgd + USER_PTRS_PER_PGD,  +		       swapper_pg_dir + USER_PTRS_PER_PGD,  +		       (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); +	} +	return pgd; +} + +void pgd_free(pgd_t *pgd) +{ +	free_page((unsigned long) pgd); +} + +pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ +	pte_t *pte; + +	pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); +	return pte; +} + +struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ +	struct page *pte; +    +	pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); +	return pte; +} + +/* + * 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/mem_user.c b/arch/um/kernel/mem_user.c new file mode 100644 index 00000000000..4a663fd434b --- /dev/null +++ b/arch/um/kernel/mem_user.c @@ -0,0 +1,273 @@ +/* + * arch/um/kernel/mem_user.c + * + * BRIEF MODULE DESCRIPTION + * user side memory routines for supporting IO memory inside user mode linux + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + *         Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + * + *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED + *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN + *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT, + *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF + *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT + *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *  You should have received a copy of the  GNU General Public License along + *  with this program; if not, write  to the Free Software Foundation, Inc., + *  675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/mman.h> +#include "kern_util.h" +#include "user.h" +#include "user_util.h" +#include "mem_user.h" +#include "init.h" +#include "os.h" +#include "tempfile.h" +#include "kern_constants.h" + +#define TEMPNAME_TEMPLATE "vm_file-XXXXXX" + +static int create_tmp_file(unsigned long len) +{ +	int fd, err; +	char zero; + +	fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); +	if(fd < 0) { +		os_print_error(fd, "make_tempfile"); +		exit(1); +	} + +	err = os_mode_fd(fd, 0777); +	if(err < 0){ +		os_print_error(err, "os_mode_fd"); +		exit(1); +	} +	err = os_seek_file(fd, len); +	if(err < 0){ +		os_print_error(err, "os_seek_file"); +		exit(1); +	} +	zero = 0; +	err = os_write_file(fd, &zero, 1); +	if(err != 1){ +		os_print_error(err, "os_write_file"); +		exit(1); +	} + +	return(fd); +} + +void check_tmpexec(void) +{ +	void *addr; +	int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); + +	addr = mmap(NULL, UM_KERN_PAGE_SIZE, +		    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); +	printf("Checking PROT_EXEC mmap in /tmp..."); +	fflush(stdout); +	if(addr == MAP_FAILED){ +		err = errno; +		perror("failed"); +		if(err == EPERM) +			printf("/tmp must be not mounted noexec\n"); +		exit(1); +	} +	printf("OK\n"); +	munmap(addr, UM_KERN_PAGE_SIZE); + +	os_close_file(fd); +} + +static int have_devanon = 0; + +void check_devanon(void) +{ +	int fd; + +	printk("Checking for /dev/anon on the host..."); +	fd = open("/dev/anon", O_RDWR); +	if(fd < 0){ +		printk("Not available (open failed with errno %d)\n", errno); +		return; +	} + +	printk("OK\n"); +	have_devanon = 1; +} + +static int create_anon_file(unsigned long len) +{ +	void *addr; +	int fd; + +	fd = open("/dev/anon", O_RDWR); +	if(fd < 0) { +		os_print_error(fd, "opening /dev/anon"); +		exit(1); +	} + +	addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); +	if(addr == MAP_FAILED){ +		perror("mapping physmem file"); +		exit(1); +	} +	munmap(addr, len); + +	return(fd); +} + +int create_mem_file(unsigned long len) +{ +	int err, fd; + +	if(have_devanon) +		fd = create_anon_file(len); +	else fd = create_tmp_file(len); + +	err = os_set_exec_close(fd, 1); +	if(err < 0) +		os_print_error(err, "exec_close"); +	return(fd); +} + +struct iomem_region *iomem_regions = NULL; +int iomem_size = 0; + +static int __init parse_iomem(char *str, int *add) +{ +	struct iomem_region *new; +	struct uml_stat buf; +	char *file, *driver; +	int fd, err, size; + +	driver = str; +	file = strchr(str,','); +	if(file == NULL){ +		printf("parse_iomem : failed to parse iomem\n"); +		goto out; +	} +	*file = '\0'; +	file++; +	fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0); +	if(fd < 0){ +		os_print_error(fd, "parse_iomem - Couldn't open io file"); +		goto out; +	} + +	err = os_stat_fd(fd, &buf); +	if(err < 0){ +		os_print_error(err, "parse_iomem - cannot stat_fd file"); +		goto out_close; +	} + +	new = malloc(sizeof(*new)); +	if(new == NULL){ +		perror("Couldn't allocate iomem_region struct"); +		goto out_close; +	} + +	size = (buf.ust_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); + +	*new = ((struct iomem_region) { .next		= iomem_regions, +					.driver		= driver, +					.fd		= fd, +					.size		= size, +					.phys		= 0, +					.virt		= 0 }); +	iomem_regions = new; +	iomem_size += new->size + UM_KERN_PAGE_SIZE; + +	return(0); + out_close: +	os_close_file(fd); + out: +	return(1); +} + +__uml_setup("iomem=", parse_iomem, +"iomem=<name>,<file>\n" +"    Configure <file> as an IO memory region named <name>.\n\n" +); + +int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, +		   int must_succeed) +{ +	int err; + +	err = os_protect_memory((void *) addr, len, r, w, x); +	if(err < 0){ +                if(must_succeed) +			panic("protect failed, err = %d", -err); +		else return(err); +	} +	return(0); +} + +#if 0 +/* Debugging facility for dumping stuff out to the host, avoiding the timing + * problems that come with printf and breakpoints. + * Enable in case of emergency. + */ + +int logging = 1; +int logging_fd = -1; + +int logging_line = 0; +char logging_buf[512]; + +void log(char *fmt, ...) +{ +        va_list ap; +        struct timeval tv; +        struct openflags flags; + +        if(logging == 0) return; +        if(logging_fd < 0){ +                flags = of_create(of_trunc(of_rdwr(OPENFLAGS()))); +                logging_fd = os_open_file("log", flags, 0644); +        } +        gettimeofday(&tv, NULL); +        sprintf(logging_buf, "%d\t %u.%u  ", logging_line++, tv.tv_sec, +                tv.tv_usec); +        va_start(ap, fmt); +        vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap); +        va_end(ap); +        write(logging_fd, logging_buf, strlen(logging_buf)); +} +#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/physmem.c b/arch/um/kernel/physmem.c new file mode 100644 index 00000000000..420e6d51fa0 --- /dev/null +++ b/arch/um/kernel/physmem.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "linux/rbtree.h" +#include "linux/slab.h" +#include "linux/vmalloc.h" +#include "linux/bootmem.h" +#include "linux/module.h" +#include "asm/types.h" +#include "asm/pgtable.h" +#include "kern_util.h" +#include "user_util.h" +#include "mode_kern.h" +#include "mem.h" +#include "mem_user.h" +#include "os.h" +#include "kern.h" +#include "init.h" + +struct phys_desc { +	struct rb_node rb; +	int fd; +	__u64 offset; +	void *virt; +	unsigned long phys; +	struct list_head list; +}; + +static struct rb_root phys_mappings = RB_ROOT; + +static struct rb_node **find_rb(void *virt) +{ +	struct rb_node **n = &phys_mappings.rb_node; +	struct phys_desc *d; + +	while(*n != NULL){ +		d = rb_entry(*n, struct phys_desc, rb); +		if(d->virt == virt) +			return(n); + +		if(d->virt > virt) +			n = &(*n)->rb_left; +		else +			n = &(*n)->rb_right; +	} + +	return(n); +} + +static struct phys_desc *find_phys_mapping(void *virt) +{ +	struct rb_node **n = find_rb(virt); + +	if(*n == NULL) +		return(NULL); + +	return(rb_entry(*n, struct phys_desc, rb)); +} + +static void insert_phys_mapping(struct phys_desc *desc) +{ +	struct rb_node **n = find_rb(desc->virt); + +	if(*n != NULL) +		panic("Physical remapping for %p already present", +		      desc->virt); + +	rb_link_node(&desc->rb, (*n)->rb_parent, n); +	rb_insert_color(&desc->rb, &phys_mappings); +} + +LIST_HEAD(descriptor_mappings); + +struct desc_mapping { +	int fd; +	struct list_head list; +	struct list_head pages; +}; + +static struct desc_mapping *find_mapping(int fd) +{ +	struct desc_mapping *desc; +	struct list_head *ele; + +	list_for_each(ele, &descriptor_mappings){ +		desc = list_entry(ele, struct desc_mapping, list); +		if(desc->fd == fd) +			return(desc); +	} + +	return(NULL); +} + +static struct desc_mapping *descriptor_mapping(int fd) +{ +	struct desc_mapping *desc; + +	desc = find_mapping(fd); +	if(desc != NULL) +		return(desc); + +	desc = kmalloc(sizeof(*desc), GFP_ATOMIC); +	if(desc == NULL) +		return(NULL); + +	*desc = ((struct desc_mapping) +		{ .fd =		fd, +		  .list =	LIST_HEAD_INIT(desc->list), +		  .pages =	LIST_HEAD_INIT(desc->pages) }); +	list_add(&desc->list, &descriptor_mappings); + +	return(desc); +} + +int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) +{ +	struct desc_mapping *fd_maps; +	struct phys_desc *desc; +	unsigned long phys; +	int err; + +	fd_maps = descriptor_mapping(fd); +	if(fd_maps == NULL) +		return(-ENOMEM); + +	phys = __pa(virt); +	desc = find_phys_mapping(virt); +  	if(desc != NULL) +		panic("Address 0x%p is already substituted\n", virt); + +	err = -ENOMEM; +	desc = kmalloc(sizeof(*desc), GFP_ATOMIC); +	if(desc == NULL) +		goto out; + +	*desc = ((struct phys_desc) +		{ .fd =			fd, +		  .offset =		offset, +		  .virt =		virt, +		  .phys =		__pa(virt), +		  .list = 		LIST_HEAD_INIT(desc->list) }); +	insert_phys_mapping(desc); + +	list_add(&desc->list, &fd_maps->pages); + +	virt = (void *) ((unsigned long) virt & PAGE_MASK); +	err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0); +	if(!err) +		goto out; + +	rb_erase(&desc->rb, &phys_mappings); +	kfree(desc); + out: +	return(err); +} + +static int physmem_fd = -1; + +static void remove_mapping(struct phys_desc *desc) +{ +	void *virt = desc->virt; +	int err; + +	rb_erase(&desc->rb, &phys_mappings); +	list_del(&desc->list); +	kfree(desc); + +	err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0); +	if(err) +		panic("Failed to unmap block device page from physical memory, " +		      "errno = %d", -err); +} + +int physmem_remove_mapping(void *virt) +{ +	struct phys_desc *desc; + +	virt = (void *) ((unsigned long) virt & PAGE_MASK); +	desc = find_phys_mapping(virt); +	if(desc == NULL) +		return(0); + +	remove_mapping(desc); +	return(1); +} + +void physmem_forget_descriptor(int fd) +{ +	struct desc_mapping *desc; +	struct phys_desc *page; +	struct list_head *ele, *next; +	__u64 offset; +	void *addr; +	int err; + +	desc = find_mapping(fd); +	if(desc == NULL) +		return; + +	list_for_each_safe(ele, next, &desc->pages){ +		page = list_entry(ele, struct phys_desc, list); +		offset = page->offset; +		addr = page->virt; +		remove_mapping(page); +		err = os_seek_file(fd, offset); +		if(err) +			panic("physmem_forget_descriptor - failed to seek " +			      "to %lld in fd %d, error = %d\n", +			      offset, fd, -err); +		err = os_read_file(fd, addr, PAGE_SIZE); +		if(err < 0) +			panic("physmem_forget_descriptor - failed to read " +			      "from fd %d to 0x%p, error = %d\n", +			      fd, addr, -err); +	} + +	list_del(&desc->list); +	kfree(desc); +} + +EXPORT_SYMBOL(physmem_forget_descriptor); +EXPORT_SYMBOL(physmem_remove_mapping); +EXPORT_SYMBOL(physmem_subst_mapping); + +void arch_free_page(struct page *page, int order) +{ +	void *virt; +	int i; + +	for(i = 0; i < (1 << order); i++){ +		virt = __va(page_to_phys(page + i)); +		physmem_remove_mapping(virt); +	} +} + +int is_remapped(void *virt) +{ +  	struct phys_desc *desc = find_phys_mapping(virt); + +	return(desc != NULL); +} + +/* Changed during early boot */ +unsigned long high_physmem; + +extern unsigned long physmem_size; + +void *to_virt(unsigned long phys) +{ +	return((void *) uml_physmem + phys); +} + +unsigned long to_phys(void *virt) +{ +	return(((unsigned long) virt) - uml_physmem); +} + +int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) +{ +	struct page *p, *map; +	unsigned long phys_len, phys_pages, highmem_len, highmem_pages; +	unsigned long iomem_len, iomem_pages, total_len, total_pages; +	int i; + +	phys_pages = physmem >> PAGE_SHIFT; +	phys_len = phys_pages * sizeof(struct page); + +	iomem_pages = iomem >> PAGE_SHIFT; +	iomem_len = iomem_pages * sizeof(struct page); + +	highmem_pages = highmem >> PAGE_SHIFT; +	highmem_len = highmem_pages * sizeof(struct page); + +	total_pages = phys_pages + iomem_pages + highmem_pages; +	total_len = phys_len + iomem_pages + highmem_len; + +	if(kmalloc_ok){ +		map = kmalloc(total_len, GFP_KERNEL); +		if(map == NULL) +			map = vmalloc(total_len); +	} +	else map = alloc_bootmem_low_pages(total_len); + +	if(map == NULL) +		return(-ENOMEM); + +	for(i = 0; i < total_pages; i++){ +		p = &map[i]; +		set_page_count(p, 0); +		SetPageReserved(p); +		INIT_LIST_HEAD(&p->lru); +	} + +	max_mapnr = total_pages; +	return(0); +} + +struct page *phys_to_page(const unsigned long phys) +{ +	return(&mem_map[phys >> PAGE_SHIFT]); +} + +struct page *__virt_to_page(const unsigned long virt) +{ +	return(&mem_map[__pa(virt) >> PAGE_SHIFT]); +} + +phys_t page_to_phys(struct page *page) +{ +	return((page - mem_map) << PAGE_SHIFT); +} + +pte_t mk_pte(struct page *page, pgprot_t pgprot) +{ +	pte_t pte; + +	pte_set_val(pte, page_to_phys(page), pgprot); +	if(pte_present(pte)) +		pte_mknewprot(pte_mknewpage(pte)); +	return(pte); +} + +/* Changed during early boot */ +static unsigned long kmem_top = 0; + +unsigned long get_kmem_end(void) +{ +	if(kmem_top == 0) +		kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas); +	return(kmem_top); +} + +void map_memory(unsigned long virt, unsigned long phys, unsigned long len, +		int r, int w, int x) +{ +	__u64 offset; +	int fd, err; + +	fd = phys_mapping(phys, &offset); +	err = os_map_memory((void *) virt, fd, offset, len, r, w, x); +	if(err) { +		if(err == -ENOMEM) +			printk("try increasing the host's " +			       "/proc/sys/vm/max_map_count to <physical " +			       "memory size>/4096\n"); +		panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " +		      "err = %d\n", virt, fd, offset, len, r, w, x, err); +	} +} + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) + +void setup_physmem(unsigned long start, unsigned long reserve_end, +		   unsigned long len, unsigned long highmem) +{ +	unsigned long reserve = reserve_end - start; +	int pfn = PFN_UP(__pa(reserve_end)); +	int delta = (len - reserve) >> PAGE_SHIFT; +	int err, offset, bootmap_size; + +	physmem_fd = create_mem_file(len + highmem); + +	offset = uml_reserved - uml_physmem; +	err = os_map_memory((void *) uml_reserved, physmem_fd, offset, +			    len - offset, 1, 1, 0); +	if(err < 0){ +		os_print_error(err, "Mapping memory"); +		exit(1); +	} + +	bootmap_size = init_bootmem(pfn, pfn + delta); +	free_bootmem(__pa(reserve_end) + bootmap_size, +		     len - bootmap_size - reserve); +} + +int phys_mapping(unsigned long phys, __u64 *offset_out) +{ +	struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK)); +	int fd = -1; + +	if(desc != NULL){ +		fd = desc->fd; +		*offset_out = desc->offset; +	} +	else if(phys < physmem_size){ +		fd = physmem_fd; +		*offset_out = phys; +	} +	else if(phys < __pa(end_iomem)){ +		struct iomem_region *region = iomem_regions; + +		while(region != NULL){ +			if((phys >= region->phys) && +			   (phys < region->phys + region->size)){ +				fd = region->fd; +				*offset_out = phys - region->phys; +				break; +			} +			region = region->next; +		} +	} +	else if(phys < __pa(end_iomem) + highmem){ +		fd = physmem_fd; +		*offset_out = phys - iomem_size; +	} + +	return(fd); +} + +static int __init uml_mem_setup(char *line, int *add) +{ +	char *retptr; +	physmem_size = memparse(line,&retptr); +	return 0; +} +__uml_setup("mem=", uml_mem_setup, +"mem=<Amount of desired ram>\n" +"    This controls how much \"physical\" memory the kernel allocates\n" +"    for the system. The size is specified as a number followed by\n" +"    one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" +"    This is not related to the amount of memory in the host.  It can\n" +"    be more, and the excess, if it's ever used, will just be swapped out.\n" +"	Example: mem=64M\n\n" +); + +unsigned long find_iomem(char *driver, unsigned long *len_out) +{ +	struct iomem_region *region = iomem_regions; + +	while(region != NULL){ +		if(!strcmp(region->driver, driver)){ +			*len_out = region->size; +			return(region->virt); +		} +	} + +	return(0); +} + +int setup_iomem(void) +{ +	struct iomem_region *region = iomem_regions; +	unsigned long iomem_start = high_physmem + PAGE_SIZE; +	int err; + +	while(region != NULL){ +		err = os_map_memory((void *) iomem_start, region->fd, 0, +				    region->size, 1, 1, 0); +		if(err) +			printk("Mapping iomem region for driver '%s' failed, " +			       "errno = %d\n", region->driver, -err); +		else { +			region->virt = iomem_start; +			region->phys = __pa(region->virt); +		} + +		iomem_start += region->size + PAGE_SIZE; +		region = region->next; +	} + +	return(0); +} + +__initcall(setup_iomem); + +/* + * 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/process.c b/arch/um/kernel/process.c new file mode 100644 index 00000000000..f76a2692adc --- /dev/null +++ b/arch/um/kernel/process.c @@ -0,0 +1,423 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <sched.h> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <setjmp.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <asm/unistd.h> +#include <asm/page.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_kern.h" +#include "signal_user.h" +#include "sysdep/ptrace.h" +#include "sysdep/sigcontext.h" +#include "irq_user.h" +#include "ptrace_user.h" +#include "time_user.h" +#include "init.h" +#include "os.h" +#include "uml-config.h" +#include "ptrace_user.h" +#include "choose-mode.h" +#include "mode.h" +#ifdef UML_CONFIG_MODE_SKAS +#include "skas.h" +#include "skas_ptrace.h" +#include "registers.h" +#endif + +void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)) +{ +	int flags = 0, pages; + +	if(sig_stack != NULL){ +		pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER); +		set_sigstack(sig_stack, pages * page_size()); +		flags = SA_ONSTACK; +	} +	if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); +} + +void init_new_thread_signals(int altstack) +{ +	int flags = altstack ? SA_ONSTACK : 0; + +	set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags,  +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	set_handler(SIGFPE, (__sighandler_t) sig_handler, flags,  +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	set_handler(SIGILL, (__sighandler_t) sig_handler, flags,  +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	set_handler(SIGBUS, (__sighandler_t) sig_handler, flags,  +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	set_handler(SIGWINCH, (__sighandler_t) sig_handler, flags,  +		    SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	set_handler(SIGUSR2, (__sighandler_t) sig_handler,  +		    flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); +	signal(SIGHUP, SIG_IGN); + +	init_irq_signals(altstack); +} + +struct tramp { +	int (*tramp)(void *); +	void *tramp_data; +	unsigned long temp_stack; +	int flags; +	int pid; +}; + +/* See above for why sigkill is here */ + +int sigkill = SIGKILL; + +int outer_tramp(void *arg) +{ +	struct tramp *t; +	int sig = sigkill; + +	t = arg; +	t->pid = clone(t->tramp, (void *) t->temp_stack + page_size()/2, +		       t->flags, t->tramp_data); +	if(t->pid > 0) wait_for_stop(t->pid, SIGSTOP, PTRACE_CONT, NULL); +	kill(os_getpid(), sig); +	_exit(0); +} + +int start_fork_tramp(void *thread_arg, unsigned long temp_stack,  +		     int clone_flags, int (*tramp)(void *)) +{ +	struct tramp arg; +	unsigned long sp; +	int new_pid, status, err; + +	/* The trampoline will run on the temporary stack */ +	sp = stack_sp(temp_stack); + +	clone_flags |= CLONE_FILES | SIGCHLD; + +	arg.tramp = tramp; +	arg.tramp_data = thread_arg; +	arg.temp_stack = temp_stack; +	arg.flags = clone_flags; + +	/* Start the process and wait for it to kill itself */ +	new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg); +	if(new_pid < 0) +		return(new_pid); + +	CATCH_EINTR(err = waitpid(new_pid, &status, 0)); +	if(err < 0) +		panic("Waiting for outer trampoline failed - errno = %d", +		      errno); + +	if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) +		panic("outer trampoline didn't exit with SIGKILL, " +		      "status = %d", status); + +	return(arg.pid); +} + +static int ptrace_child(void *arg) +{ +	int ret; +	int pid = os_getpid(), ppid = getppid(); +	int sc_result; + +	if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ +		perror("ptrace"); +		os_kill_process(pid, 0); +	} +	os_stop_process(pid); + +	/*This syscall will be intercepted by the parent. Don't call more than +	 * once, please.*/ +	sc_result = os_getpid(); + +	if (sc_result == pid) +		ret = 1; /*Nothing modified by the parent, we are running +			   normally.*/ +	else if (sc_result == ppid) +		ret = 0; /*Expected in check_ptrace and check_sysemu when they +			   succeed in modifying the stack frame*/ +	else +		ret = 2; /*Serious trouble! This could be caused by a bug in +			   host 2.6 SKAS3/2.6 patch before release -V6, together +			   with a bug in the UML code itself.*/ +	_exit(ret); +} + +static int start_ptraced_child(void **stack_out) +{ +	void *stack; +	unsigned long sp; +	int pid, n, status; +	 +	stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, +		     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +	if(stack == MAP_FAILED) +		panic("check_ptrace : mmap failed, errno = %d", errno); +	sp = (unsigned long) stack + PAGE_SIZE - sizeof(void *); +	pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL); +	if(pid < 0) +		panic("check_ptrace : clone failed, errno = %d", errno); +	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); +	if(n < 0) +		panic("check_ptrace : wait failed, errno = %d", errno); +	if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) +		panic("check_ptrace : expected SIGSTOP, got status = %d", +		      status); + +	*stack_out = stack; +	return(pid); +} + +/* When testing for SYSEMU support, if it is one of the broken versions, we must + * just avoid using sysemu, not panic, but only if SYSEMU features are broken. + * So only for SYSEMU features we test mustpanic, while normal host features + * must work anyway!*/ +static int stop_ptraced_child(int pid, void *stack, int exitcode, int mustpanic) +{ +	int status, n, ret = 0; + +	if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) +		panic("check_ptrace : ptrace failed, errno = %d", errno); +	CATCH_EINTR(n = waitpid(pid, &status, 0)); +	if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { +		int exit_with = WEXITSTATUS(status); +		if (exit_with == 2) +			printk("check_ptrace : child exited with status 2. " +			       "Serious trouble happening! Try updating your " +			       "host skas patch!\nDisabling SYSEMU support."); +		printk("check_ptrace : child exited with exitcode %d, while " +		      "expecting %d; status 0x%x", exit_with, +		      exitcode, status); +		if (mustpanic) +			panic("\n"); +		else +			printk("\n"); +		ret = -1; +	} + +	if(munmap(stack, PAGE_SIZE) < 0) +		panic("check_ptrace : munmap failed, errno = %d", errno); +	return ret; +} + +static int force_sysemu_disabled = 0; + +static int __init nosysemu_cmd_param(char *str, int* add) +{ +	force_sysemu_disabled = 1; +	return 0; +} + +__uml_setup("nosysemu", nosysemu_cmd_param, +		"nosysemu\n" +		"    Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" +		"    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" +		"    behaviour of ptrace() and helps reducing host context switch rate.\n" +		"    To make it working, you need a kernel patch for your host, too.\n" +		"    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further information.\n\n"); + +static void __init check_sysemu(void) +{ +	void *stack; +	int pid, syscall, n, status, count=0; + +	printk("Checking syscall emulation patch for ptrace..."); +	sysemu_supported = 0; +	pid = start_ptraced_child(&stack); + +	if(ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) +		goto fail; + +	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); +	if (n < 0) +		panic("check_sysemu : wait failed, errno = %d", errno); +	if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) +		panic("check_sysemu : expected SIGTRAP, " +		      "got status = %d", status); + +	n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, +		   os_getpid()); +	if(n < 0) +		panic("check_sysemu : failed to modify system " +		      "call return, errno = %d", errno); + +	if (stop_ptraced_child(pid, stack, 0, 0) < 0) +		goto fail_stopped; + +	sysemu_supported = 1; +	printk("OK\n"); +	set_using_sysemu(!force_sysemu_disabled); + +	printk("Checking advanced syscall emulation patch for ptrace..."); +	pid = start_ptraced_child(&stack); +	while(1){ +		count++; +		if(ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) +			goto fail; +		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); +		if(n < 0) +			panic("check_ptrace : wait failed, errno = %d", errno); +		if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) +			panic("check_ptrace : expected (SIGTRAP|SYSCALL_TRAP), " +			      "got status = %d", status); + +		syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, +				 0); +		if(syscall == __NR_getpid){ +			if (!count) +				panic("check_ptrace : SYSEMU_SINGLESTEP doesn't singlestep"); +			n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, +				   os_getpid()); +			if(n < 0) +				panic("check_sysemu : failed to modify system " +				      "call return, errno = %d", errno); +			break; +		} +	} +	if (stop_ptraced_child(pid, stack, 0, 0) < 0) +		goto fail_stopped; + +	sysemu_supported = 2; +	printk("OK\n"); + +	if ( !force_sysemu_disabled ) +		set_using_sysemu(sysemu_supported); +	return; + +fail: +	stop_ptraced_child(pid, stack, 1, 0); +fail_stopped: +	printk("missing\n"); +} + +void __init check_ptrace(void) +{ +	void *stack; +	int pid, syscall, n, status; + +	printk("Checking that ptrace can change system call numbers..."); +	pid = start_ptraced_child(&stack); + +	if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) +		panic("check_ptrace: PTRACE_SETOPTIONS failed, errno = %d", errno); + +	while(1){ +		if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) +			panic("check_ptrace : ptrace failed, errno = %d",  +			      errno); +		CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); +		if(n < 0) +			panic("check_ptrace : wait failed, errno = %d", errno); +		if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP + 0x80)) +			panic("check_ptrace : expected SIGTRAP + 0x80, " +			      "got status = %d", status); +		 +		syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, +				 0); +		if(syscall == __NR_getpid){ +			n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, +				   __NR_getppid); +			if(n < 0) +				panic("check_ptrace : failed to modify system " +				      "call, errno = %d", errno); +			break; +		} +	} +	stop_ptraced_child(pid, stack, 0, 1); +	printk("OK\n"); +	check_sysemu(); +} + +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); +} + +void forward_pending_sigio(int target) +{ +	sigset_t sigs; + +	if(sigpending(&sigs))  +		panic("forward_pending_sigio : sigpending failed"); +	if(sigismember(&sigs, SIGIO)) +		kill(target, SIGIO); +} + +#ifdef UML_CONFIG_MODE_SKAS +static inline int check_skas3_ptrace_support(void) +{ +	struct ptrace_faultinfo fi; +	void *stack; +	int pid, n, ret = 1; + +	printf("Checking for the skas3 patch in the host..."); +	pid = start_ptraced_child(&stack); + +	n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi); +	if (n < 0) { +		if(errno == EIO) +			printf("not found\n"); +		else { +			perror("not found"); +		} +		ret = 0; +	} else { +		printf("found\n"); +	} + +	init_registers(pid); +	stop_ptraced_child(pid, stack, 1, 1); + +	return(ret); +} + +int can_do_skas(void) +{ +	int ret = 1; + +	printf("Checking for /proc/mm..."); +	if (os_access("/proc/mm", OS_ACC_W_OK) < 0) { +		printf("not found\n"); +		ret = 0; +		goto out; +	} else { +		printf("found\n"); +	} + +	ret = check_skas3_ptrace_support(); +out: +	return ret; +} +#else +int can_do_skas(void) +{ +	return(0); +} +#endif diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c new file mode 100644 index 00000000000..1d719d5b4bb --- /dev/null +++ b/arch/um/kernel/process_kern.c @@ -0,0 +1,500 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright 2003 PathScale, Inc. + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/mm.h" +#include "linux/slab.h" +#include "linux/utsname.h" +#include "linux/fs.h" +#include "linux/utime.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "linux/capability.h" +#include "linux/vmalloc.h" +#include "linux/spinlock.h" +#include "linux/proc_fs.h" +#include "linux/ptrace.h" +#include "linux/random.h" +#include "asm/unistd.h" +#include "asm/mman.h" +#include "asm/segment.h" +#include "asm/stat.h" +#include "asm/pgtable.h" +#include "asm/processor.h" +#include "asm/tlbflush.h" +#include "asm/uaccess.h" +#include "asm/user.h" +#include "user_util.h" +#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" +#include "2_5compat.h" +#include "os.h" +#include "mode.h" +#include "mode_kern.h" +#include "choose-mode.h" + +/* This is a per-cpu array.  A processor only modifies its entry and it only + * cares about its entry, so it's OK if another processor is modifying its + * entry. + */ +struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; + +struct task_struct *get_task(int pid, int require) +{ +        struct task_struct *ret; + +        read_lock(&tasklist_lock); +	ret = find_task_by_pid(pid); +        read_unlock(&tasklist_lock); + +        if(require && (ret == NULL)) panic("get_task couldn't find a task\n"); +        return(ret); +} + +int external_pid(void *t) +{ +	struct task_struct *task = t ? t : current; + +	return(CHOOSE_MODE_PROC(external_pid_tt, external_pid_skas, task)); +} + +int pid_to_processor_id(int pid) +{ +	int i; + +	for(i = 0; i < ncpus; i++){ +		if(cpu_tasks[i].pid == pid) return(i); +	} +	return(-1); +} + +void free_stack(unsigned long stack, int order) +{ +	free_pages(stack, order); +} + +unsigned long alloc_stack(int order, int atomic) +{ +	unsigned long page; +	int flags = GFP_KERNEL; + +	if(atomic) flags |= GFP_ATOMIC; +	page = __get_free_pages(flags, order); +	if(page == 0) +		return(0); +	stack_protections(page); +	return(page); +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ +	int pid; + +	current->thread.request.u.thread.proc = fn; +	current->thread.request.u.thread.arg = arg; +	pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0, NULL, 0, NULL, +		      NULL); +	if(pid < 0) +		panic("do_fork failed in kernel_thread, errno = %d", pid); +	return(pid); +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next,  +	       struct task_struct *tsk) +{ +	int cpu = smp_processor_id(); + +	if (prev != next)  +		cpu_clear(cpu, prev->cpu_vm_mask); +	cpu_set(cpu, next->cpu_vm_mask); +} + +void set_current(void *t) +{ +	struct task_struct *task = t; + +	cpu_tasks[task->thread_info->cpu] = ((struct cpu_task)  +		{ external_pid(task), task }); +} + +void *_switch_to(void *prev, void *next, void *last) +{ +	return(CHOOSE_MODE(switch_to_tt(prev, next),  +			   switch_to_skas(prev, next))); +} + +void interrupt_end(void) +{ +	if(need_resched()) schedule(); +	if(test_tsk_thread_flag(current, TIF_SIGPENDING)) do_signal(); +} + +void release_thread(struct task_struct *task) +{ +	CHOOSE_MODE(release_thread_tt(task), release_thread_skas(task)); +} +  +void exit_thread(void) +{ +	CHOOSE_MODE(exit_thread_tt(), exit_thread_skas()); +	unprotect_stack((unsigned long) current_thread); +} +  +void *get_current(void) +{ +	return(current); +} + +void prepare_to_copy(struct task_struct *tsk) +{ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, +		unsigned long stack_top, struct task_struct * p,  +		struct pt_regs *regs) +{ +	p->thread = (struct thread_struct) INIT_THREAD; +	return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,  +				clone_flags, sp, stack_top, p, regs)); +} + +void initial_thread_cb(void (*proc)(void *), void *arg) +{ +	int save_kmalloc_ok = kmalloc_ok; + +	kmalloc_ok = 0; +	CHOOSE_MODE_PROC(initial_thread_cb_tt, initial_thread_cb_skas, proc,  +			 arg); +	kmalloc_ok = save_kmalloc_ok; +} +  +unsigned long stack_sp(unsigned long page) +{ +	return(page + PAGE_SIZE - sizeof(void *)); +} + +int current_pid(void) +{ +	return(current->pid); +} + +void default_idle(void) +{ +	uml_idle_timer(); + +	atomic_inc(&init_mm.mm_count); +	current->mm = &init_mm; +	current->active_mm = &init_mm; + +	while(1){ +		/* endless idle loop with no priority at all */ +		SET_PRI(current); + +		/* +		 * although we are an idle CPU, we do not want to +		 * get into the scheduler unnecessarily. +		 */ +		if(need_resched()) +			schedule(); +		 +		idle_sleep(10); +	} +} + +void cpu_idle(void) +{ +	CHOOSE_MODE(init_idle_tt(), init_idle_skas()); +} + +int page_size(void) +{ +	return(PAGE_SIZE); +} + +unsigned long page_mask(void) +{ +	return(PAGE_MASK); +} + +void *um_virt_to_phys(struct task_struct *task, unsigned long addr,  +		      pte_t *pte_out) +{ +	pgd_t *pgd; +	pud_t *pud; +	pmd_t *pmd; +	pte_t *pte; + +	if(task->mm == NULL)  +		return(ERR_PTR(-EINVAL)); +	pgd = pgd_offset(task->mm, addr); +	if(!pgd_present(*pgd)) +		return(ERR_PTR(-EINVAL)); + +	pud = pud_offset(pgd, addr); +	if(!pud_present(*pud)) +		return(ERR_PTR(-EINVAL)); + +	pmd = pmd_offset(pud, addr); +	if(!pmd_present(*pmd))  +		return(ERR_PTR(-EINVAL)); + +	pte = pte_offset_kernel(pmd, addr); +	if(!pte_present(*pte))  +		return(ERR_PTR(-EINVAL)); + +	if(pte_out != NULL) +		*pte_out = *pte; +	return((void *) (pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK)); +} + +char *current_cmd(void) +{ +#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM) +	return("(Unknown)"); +#else +	void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL); +	return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr); +#endif +} + +void force_sigbus(void) +{ +	printk(KERN_ERR "Killing pid %d because of a lack of memory\n",  +	       current->pid); +	lock_kernel(); +	sigaddset(¤t->pending.signal, SIGBUS); +	recalc_sigpending(); +	current->flags |= PF_SIGNALED; +	do_exit(SIGBUS | 0x80); +} + +void dump_thread(struct pt_regs *regs, struct user *u) +{ +} + +void enable_hlt(void) +{ +	panic("enable_hlt"); +} + +EXPORT_SYMBOL(enable_hlt); + +void disable_hlt(void) +{ +	panic("disable_hlt"); +} + +EXPORT_SYMBOL(disable_hlt); + +void *um_kmalloc(int size) +{ +	return(kmalloc(size, GFP_KERNEL)); +} + +void *um_kmalloc_atomic(int size) +{ +	return(kmalloc(size, GFP_ATOMIC)); +} + +void *um_vmalloc(int size) +{ +	return(vmalloc(size)); +} + +unsigned long get_fault_addr(void) +{ +	return((unsigned long) current->thread.fault_addr); +} + +EXPORT_SYMBOL(get_fault_addr); + +void not_implemented(void) +{ +	printk(KERN_DEBUG "Something isn't implemented in here\n"); +} + +EXPORT_SYMBOL(not_implemented); + +int user_context(unsigned long sp) +{ +	unsigned long stack; + +	stack = sp & (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER); +	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) +{ +	exitcall_t *call; + +	call = &__uml_exitcall_end; +	while (--call >= &__uml_exitcall_begin) +		(*call)(); +} + +char *uml_strdup(char *string) +{ +	char *new; + +	new = kmalloc(strlen(string) + 1, GFP_KERNEL); +	if(new == NULL) return(NULL); +	strcpy(new, string); +	return(new); +} + +void *get_init_task(void) +{ +	return(&init_thread_union.thread_info.task); +} + +int copy_to_user_proc(void __user *to, void *from, int size) +{ +	return(copy_to_user(to, from, size)); +} + +int copy_from_user_proc(void *to, void __user *from, int size) +{ +	return(copy_from_user(to, from, size)); +} + +int clear_user_proc(void __user *buf, int size) +{ +	return(clear_user(buf, size)); +} + +int strlen_user_proc(char __user *str) +{ +	return(strlen_user(str)); +} + +int smp_sigio_handler(void) +{ +#ifdef CONFIG_SMP +	int cpu = current_thread->cpu; +	IPI_handler(cpu); +	if(cpu != 0) +		return(1); +#endif +	return(0); +} + +int um_in_interrupt(void) +{ +	return(in_interrupt()); +} + +int cpu(void) +{ +	return(current_thread->cpu); +} + +static atomic_t using_sysemu = ATOMIC_INIT(0); +int sysemu_supported; + +void set_using_sysemu(int value) +{ +	if (value > sysemu_supported) +		return; +	atomic_set(&using_sysemu, value); +} + +int get_using_sysemu(void) +{ +	return atomic_read(&using_sysemu); +} + +static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data) +{ +	if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size) /*No overflow*/ +		*eof = 1; + +	return strlen(buf); +} + +static int proc_write_sysemu(struct file *file,const char *buf, unsigned long count,void *data) +{ +	char tmp[2]; + +	if (copy_from_user(tmp, buf, 1)) +		return -EFAULT; + +	if (tmp[0] >= '0' && tmp[0] <= '2') +		set_using_sysemu(tmp[0] - '0'); +	return count; /*We use the first char, but pretend to write everything*/ +} + +int __init make_proc_sysemu(void) +{ +	struct proc_dir_entry *ent; +	if (!sysemu_supported) +		return 0; + +	ent = create_proc_entry("sysemu", 0600, &proc_root); + +	if (ent == NULL) +	{ +		printk("Failed to register /proc/sysemu\n"); +		return(0); +	} + +	ent->read_proc  = proc_read_sysemu; +	ent->write_proc = proc_write_sysemu; + +	return 0; +} + +late_initcall(make_proc_sysemu); + +int singlestepping(void * t) +{ +	struct task_struct *task = t ? t : current; + +	if ( ! (task->ptrace & PT_DTRACE) ) +		return(0); + +	if (task->thread.singlestep_syscall) +		return(1); + +	return 2; +} + +unsigned long arch_align_stack(unsigned long sp) +{ +	if (randomize_va_space) +		sp -= get_random_int() % 8192; +	return sp & ~0xf; +} + + +/* + * 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/ptrace.c b/arch/um/kernel/ptrace.c new file mode 100644 index 00000000000..3a99ee6d94e --- /dev/null +++ b/arch/um/kernel/ptrace.c @@ -0,0 +1,388 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/errno.h" +#include "linux/smp_lock.h" +#include "linux/security.h" +#include "linux/ptrace.h" +#include "linux/audit.h" +#ifdef CONFIG_PROC_MM +#include "linux/proc_mm.h" +#endif +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "kern_util.h" +#include "skas_ptrace.h" +#include "sysdep/ptrace.h" + +/* + * Called by kernel/ptrace.c when detaching.. + */ +void ptrace_disable(struct task_struct *child) +{  +	child->ptrace &= ~PT_DTRACE; +	child->thread.singlestep_syscall = 0; +} + +long sys_ptrace(long request, long pid, long addr, long data) +{ +	struct task_struct *child; +	int i, ret; + +	lock_kernel(); +	ret = -EPERM; +	if (request == PTRACE_TRACEME) { +		/* are we already being traced? */ +		if (current->ptrace & PT_PTRACED) +			goto out; + +		ret = security_ptrace(current->parent, current); +		if (ret) + 			goto out; + +		/* set the ptrace bit in the process flags. */ +		current->ptrace |= PT_PTRACED; +		ret = 0; +		goto out; +	} +	ret = -ESRCH; +	read_lock(&tasklist_lock); +	child = find_task_by_pid(pid); +	if (child) +		get_task_struct(child); +	read_unlock(&tasklist_lock); +	if (!child) +		goto out; + +	ret = -EPERM; +	if (pid == 1)		/* you may not mess with init */ +		goto out_tsk; + +	if (request == PTRACE_ATTACH) { +		ret = ptrace_attach(child); +		goto out_tsk; +	} + +	ret = ptrace_check_attach(child, request == PTRACE_KILL); +	if (ret < 0) +		goto out_tsk; + +	switch (request) { +		/* when I and D space are separate, these will need to be fixed. */ +	case PTRACE_PEEKTEXT: /* read word at location addr. */  +	case PTRACE_PEEKDATA: { +		unsigned long tmp; +		int copied; + +		ret = -EIO; +		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); +		if (copied != sizeof(tmp)) +			break; +		ret = put_user(tmp, (unsigned long __user *) data); +		break; +	} + +	/* read the word at location addr in the USER area. */ +	case PTRACE_PEEKUSR: { +		unsigned long tmp; + +		ret = -EIO; +		if ((addr & 3) || addr < 0)  +			break; + +		tmp = 0;  /* Default return condition */ +		if(addr < MAX_REG_OFFSET){ +			tmp = getreg(child, addr); +		} +		else if((addr >= offsetof(struct user, u_debugreg[0])) && +			(addr <= offsetof(struct user, u_debugreg[7]))){ +			addr -= offsetof(struct user, u_debugreg[0]); +			addr = addr >> 2; +			tmp = child->thread.arch.debugregs[addr]; +		} +		ret = put_user(tmp, (unsigned long __user *) data); +		break; +	} + +	/* when I and D space are separate, this will have to be fixed. */ +	case PTRACE_POKETEXT: /* write the word at location addr. */ +	case PTRACE_POKEDATA: +		ret = -EIO; +		if (access_process_vm(child, addr, &data, sizeof(data),  +				      1) != sizeof(data)) +			break; +		ret = 0; +		break; + +	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ +		ret = -EIO; +		if ((addr & 3) || addr < 0) +			break; + +		if (addr < MAX_REG_OFFSET) { +			ret = putreg(child, addr, data); +			break; +		} +#if 0 /* XXX x86_64 */ +		else if((addr >= offsetof(struct user, u_debugreg[0])) && +			(addr <= offsetof(struct user, u_debugreg[7]))){ +			  addr -= offsetof(struct user, u_debugreg[0]); +			  addr = addr >> 2; +			  if((addr == 4) || (addr == 5)) break; +			  child->thread.arch.debugregs[addr] = data; +			  ret = 0; +		} +#endif + +		break; + +	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ +	case PTRACE_CONT: { /* restart after signal. */ +		ret = -EIO; +		if ((unsigned long) data > _NSIG) +			break; + +		child->ptrace &= ~PT_DTRACE; +		child->thread.singlestep_syscall = 0; +		if (request == PTRACE_SYSCALL) { +			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); +		} +		else { +			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); +		} +		child->exit_code = data; +		wake_up_process(child); +		ret = 0; +		break; +	} + +/* + * make the child exit.  Best I can do is send it a sigkill.  + * perhaps it should be put in the status that it wants to  + * exit. + */ +	case PTRACE_KILL: { +		ret = 0; +		if (child->exit_state == EXIT_ZOMBIE)	/* already dead */ +			break; + +		child->ptrace &= ~PT_DTRACE; +		child->thread.singlestep_syscall = 0; +		child->exit_code = SIGKILL; +		wake_up_process(child); +		break; +	} + +	case PTRACE_SINGLESTEP: {  /* set the trap flag. */ +		ret = -EIO; +		if ((unsigned long) data > _NSIG) +			break; +		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); +		child->ptrace |= PT_DTRACE; +		child->thread.singlestep_syscall = 0; +		child->exit_code = data; +		/* give it a chance to run. */ +		wake_up_process(child); +		ret = 0; +		break; +	} + +	case PTRACE_DETACH: +		/* detach a process that was attached. */ +		ret = ptrace_detach(child, data); + 		break; + +#ifdef PTRACE_GETREGS +	case PTRACE_GETREGS: { /* Get all gp regs from the child. */ +	  	if (!access_ok(VERIFY_WRITE, (unsigned long *)data,  +			       MAX_REG_OFFSET)) { +			ret = -EIO; +			break; +		} +		for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { +			__put_user(getreg(child, i), +				   (unsigned long __user *) data); +			data += sizeof(long); +		} +		ret = 0; +		break; +	} +#endif +#ifdef PTRACE_SETREGS +	case PTRACE_SETREGS: { /* Set all gp regs in the child. */ +		unsigned long tmp = 0; +	  	if (!access_ok(VERIFY_READ, (unsigned *)data,  +			       MAX_REG_OFFSET)) { +			ret = -EIO; +			break; +		} +		for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { +			__get_user(tmp, (unsigned long __user *) data); +			putreg(child, i, tmp); +			data += sizeof(long); +		} +		ret = 0; +		break; +	} +#endif +#ifdef PTRACE_GETFPREGS +	case PTRACE_GETFPREGS: /* Get the child FPU state. */ +		ret = get_fpregs(data, child); +		break; +#endif +#ifdef PTRACE_SETFPREGS +	case PTRACE_SETFPREGS: /* Set the child FPU state. */ +	        ret = set_fpregs(data, child); +		break; +#endif +#ifdef PTRACE_GETFPXREGS +	case PTRACE_GETFPXREGS: /* Get the child FPU state. */ +		ret = get_fpxregs(data, child); +		break; +#endif +#ifdef PTRACE_SETFPXREGS +	case PTRACE_SETFPXREGS: /* Set the child FPU state. */ +		ret = set_fpxregs(data, child); +		break; +#endif +	case PTRACE_FAULTINFO: { +		struct ptrace_faultinfo fault; + +		fault = ((struct ptrace_faultinfo)  +			{ .is_write	= child->thread.err, +			  .addr		= child->thread.cr2 }); +		ret = copy_to_user((unsigned long __user *) data, &fault, +				   sizeof(fault)); +		if(ret) +			break; +		break; +	} +	case PTRACE_SIGPENDING: +		ret = copy_to_user((unsigned long __user *) data, +				   &child->pending.signal, +				   sizeof(child->pending.signal)); +		break; + +	case PTRACE_LDT: { +		struct ptrace_ldt ldt; + +		if(copy_from_user(&ldt, (unsigned long __user *) data, +				  sizeof(ldt))){ +			ret = -EIO; +			break; +		} + +		/* This one is confusing, so just punt and return -EIO for  +		 * now +		 */ +		ret = -EIO; +		break; +	} +#ifdef CONFIG_PROC_MM +	case PTRACE_SWITCH_MM: { +		struct mm_struct *old = child->mm; +		struct mm_struct *new = proc_mm_get_mm(data); + +		if(IS_ERR(new)){ +			ret = PTR_ERR(new); +			break; +		} + +		atomic_inc(&new->mm_users); +		child->mm = new; +		child->active_mm = new; +		mmput(old); +		ret = 0; +		break; +	} +#endif +	default: +		ret = ptrace_request(child, request, addr, data); +		break; +	} + out_tsk: +	put_task_struct(child); + out: +	unlock_kernel(); +	return ret; +} + +void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs, +		  int error_code) +{ +	struct siginfo info; + +	memset(&info, 0, sizeof(info)); +	info.si_signo = SIGTRAP; +	info.si_code = TRAP_BRKPT; + +	/* User-mode eip? */ +	info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL; + +	/* Send us the fakey SIGTRAP */ +	force_sig_info(SIGTRAP, &info, tsk); +} + +/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and + * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check + */ +void syscall_trace(union uml_pt_regs *regs, int entryexit) +{ +	int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit; +	int tracesysgood; + +	if (unlikely(current->audit_context)) { +		if (!entryexit) +			audit_syscall_entry(current,  +					    UPT_SYSCALL_NR(®s->regs), +					    UPT_SYSCALL_ARG1(®s->regs), +					    UPT_SYSCALL_ARG2(®s->regs), +					    UPT_SYSCALL_ARG3(®s->regs), +					    UPT_SYSCALL_ARG4(®s->regs)); +		else +			audit_syscall_exit(current,  +					   UPT_SYSCALL_RET(®s->regs)); +	} + +	/* Fake a debug trap */ +	if (is_singlestep) +		send_sigtrap(current, regs, 0); + +	if (!test_thread_flag(TIF_SYSCALL_TRACE)) +		return; + +	if (!(current->ptrace & PT_PTRACED)) +		return; + +	/* the 0x80 provides a way for the tracing parent to distinguish +	   between a syscall stop and SIGTRAP delivery */ +	tracesysgood = (current->ptrace & PT_TRACESYSGOOD); +	ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0)); + +	if (entryexit) /* force do_signal() --> is_syscall() */ +		set_thread_flag(TIF_SIGPENDING); + +	/* this isn't the same as continuing with a signal, but it will do +	 * for normal use.  strace only continues with a signal if the +	 * stopping signal is not SIGTRAP.  -brl +	 */ +	if (current->exit_code) { +		send_sig(current->exit_code, current, 1); +		current->exit_code = 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/kernel/reboot.c b/arch/um/kernel/reboot.c new file mode 100644 index 00000000000..207f89d7490 --- /dev/null +++ b/arch/um/kernel/reboot.c @@ -0,0 +1,79 @@ +/*  + * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" +#include "linux/sched.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "os.h" +#include "mode.h" +#include "choose-mode.h" + +#ifdef CONFIG_SMP +static void kill_idlers(int me) +{ +#ifdef CONFIG_MODE_TT +	struct task_struct *p; +	int i; + +	for(i = 0; i < sizeof(idle_threads)/sizeof(idle_threads[0]); i++){ +		p = idle_threads[i]; +		if((p != NULL) && (p->thread.mode.tt.extern_pid != me)) +			os_kill_process(p->thread.mode.tt.extern_pid, 0); +	} +#endif +} +#endif + +static void kill_off_processes(void) +{ +	CHOOSE_MODE(kill_off_processes_tt(), kill_off_processes_skas()); +#ifdef CONFIG_SMP +	kill_idlers(os_getpid()); +#endif +} + +void uml_cleanup(void) +{ +	kill_off_processes(); +	do_uml_exitcalls(); +} + +void machine_restart(char * __unused) +{ +	do_uml_exitcalls(); +	kill_off_processes(); +	CHOOSE_MODE(reboot_tt(), reboot_skas()); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_power_off(void) +{ +	do_uml_exitcalls(); +	kill_off_processes(); +	CHOOSE_MODE(halt_tt(), halt_skas()); +} + +EXPORT_SYMBOL(machine_power_off); + +void machine_halt(void) +{ +	machine_power_off(); +} + +EXPORT_SYMBOL(machine_halt); + +/* + * 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/resource.c b/arch/um/kernel/resource.c new file mode 100644 index 00000000000..32188e12e8a --- /dev/null +++ b/arch/um/kernel/resource.c @@ -0,0 +1,23 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/pci.h" + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, +			     unsigned long start, unsigned long size) +{ +	return start; +} + +/* + * 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/sigio_kern.c b/arch/um/kernel/sigio_kern.c new file mode 100644 index 00000000000..229988463c4 --- /dev/null +++ b/arch/um/kernel/sigio_kern.c @@ -0,0 +1,63 @@ +/*  + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/list.h" +#include "linux/slab.h" +#include "linux/signal.h" +#include "linux/interrupt.h" +#include "init.h" +#include "sigio.h" +#include "irq_user.h" +#include "irq_kern.h" + +/* Protected by sigio_lock() called from write_sigio_workaround */ +static int sigio_irq_fd = -1; + +static irqreturn_t sigio_interrupt(int irq, void *data, struct pt_regs *unused) +{ +	read_sigio_fd(sigio_irq_fd); +	reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ); +	return(IRQ_HANDLED); +} + +int write_sigio_irq(int fd) +{ +	int err; + +	err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt, +			     SA_INTERRUPT | SA_SAMPLE_RANDOM, "write sigio", +			     NULL); +	if(err){ +		printk("write_sigio_irq : um_request_irq failed, err = %d\n", +		       err); +		return(-1); +	} +	sigio_irq_fd = fd; +	return(0); +} + +static DEFINE_SPINLOCK(sigio_spinlock); + +void sigio_lock(void) +{ +	spin_lock(&sigio_spinlock); +} + +void sigio_unlock(void) +{ +	spin_unlock(&sigio_spinlock); +} + +/* + * 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/sigio_user.c b/arch/um/kernel/sigio_user.c new file mode 100644 index 00000000000..668df13d8c9 --- /dev/null +++ b/arch/um/kernel/sigio_user.c @@ -0,0 +1,431 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <unistd.h> +#include <stdlib.h> +#include <termios.h> +#include <pty.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <sched.h> +#include <sys/socket.h> +#include <sys/poll.h> +#include "init.h" +#include "user.h" +#include "kern_util.h" +#include "user_util.h" +#include "sigio.h" +#include "helper.h" +#include "os.h" + +/* Changed during early boot */ +int pty_output_sigio = 0; +int pty_close_sigio = 0; + +/* Used as a flag during SIGIO testing early in boot */ +static volatile int got_sigio = 0; + +void __init handler(int sig) +{ +	got_sigio = 1; +} + +struct openpty_arg { +	int master; +	int slave; +	int err; +}; + +static void openpty_cb(void *arg) +{ +	struct openpty_arg *info = arg; + +	info->err = 0; +	if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) +		info->err = -errno; +} + +void __init check_one_sigio(void (*proc)(int, int)) +{ +	struct sigaction old, new; +	struct openpty_arg pty = { .master = -1, .slave = -1 }; +	int master, slave, err; + +	initial_thread_cb(openpty_cb, &pty); +	if(pty.err){ +		printk("openpty failed, errno = %d\n", -pty.err); +		return; +	} + +	master = pty.master; +	slave = pty.slave; + +	if((master == -1) || (slave == -1)){ +		printk("openpty failed to allocate a pty\n"); +		return; +	} + +	/* Not now, but complain so we now where we failed. */ +	err = raw(master); +	if (err < 0) +		panic("check_sigio : __raw failed, errno = %d\n", -err); + +	err = os_sigio_async(master, slave); +	if(err < 0) +		panic("tty_fds : sigio_async failed, err = %d\n", -err); + +	if(sigaction(SIGIO, NULL, &old) < 0) +		panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); +	new = old; +	new.sa_handler = handler; +	if(sigaction(SIGIO, &new, NULL) < 0) +		panic("check_sigio : sigaction 2 failed, errno = %d\n", errno); + +	got_sigio = 0; +	(*proc)(master, slave); +		 +	os_close_file(master); +	os_close_file(slave); + +	if(sigaction(SIGIO, &old, NULL) < 0) +		panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); +} + +static void tty_output(int master, int slave) +{ +	int n; +	char buf[512]; + +	printk("Checking that host ptys support output SIGIO..."); + +	memset(buf, 0, sizeof(buf)); + +	while(os_write_file(master, buf, sizeof(buf)) > 0) ; +	if(errno != EAGAIN) +		panic("check_sigio : write failed, errno = %d\n", errno); +	while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; + +	if (got_sigio) { +		printk("Yes\n"); +		pty_output_sigio = 1; +	} else if (n == -EAGAIN) { +		printk("No, enabling workaround\n"); +	} else { +		panic("check_sigio : read failed, err = %d\n", n); +	} +} + +static void tty_close(int master, int slave) +{ +	printk("Checking that host ptys support SIGIO on close..."); + +	os_close_file(slave); +	if(got_sigio){ +		printk("Yes\n"); +		pty_close_sigio = 1; +	} +	else printk("No, enabling workaround\n"); +} + +void __init check_sigio(void) +{ +	if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && +	   (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ +		printk("No pseudo-terminals available - skipping pty SIGIO " +		       "check\n"); +		return; +	} +	check_one_sigio(tty_output); +	check_one_sigio(tty_close); +} + +/* Protected by sigio_lock(), also used by sigio_cleanup, which is an  + * exitcall. + */ +static int write_sigio_pid = -1; + +/* These arrays are initialized before the sigio thread is started, and + * the descriptors closed after it is killed.  So, it can't see them change. + * On the UML side, they are changed under the sigio_lock. + */ +static int write_sigio_fds[2] = { -1, -1 }; +static int sigio_private[2] = { -1, -1 }; + +struct pollfds { +	struct pollfd *poll; +	int size; +	int used; +}; + +/* Protected by sigio_lock().  Used by the sigio thread, but the UML thread + * synchronizes with it. + */ +struct pollfds current_poll = { +	.poll  		= NULL, +	.size 		= 0, +	.used 		= 0 +}; + +struct pollfds next_poll = { +	.poll  		= NULL, +	.size 		= 0, +	.used 		= 0 +}; + +static int write_sigio_thread(void *unused) +{ +	struct pollfds *fds, tmp; +	struct pollfd *p; +	int i, n, respond_fd; +	char c; + +	fds = ¤t_poll; +	while(1){ +		n = poll(fds->poll, fds->used, -1); +		if(n < 0){ +			if(errno == EINTR) continue; +			printk("write_sigio_thread : poll returned %d, " +			       "errno = %d\n", n, errno); +		} +		for(i = 0; i < fds->used; i++){ +			p = &fds->poll[i]; +			if(p->revents == 0) continue; +			if(p->fd == sigio_private[1]){ +				n = os_read_file(sigio_private[1], &c, sizeof(c)); +				if(n != sizeof(c)) +					printk("write_sigio_thread : " +					       "read failed, err = %d\n", -n); +				tmp = current_poll; +				current_poll = next_poll; +				next_poll = tmp; +				respond_fd = sigio_private[1]; +			} +			else { +				respond_fd = write_sigio_fds[1]; +				fds->used--; +				memmove(&fds->poll[i], &fds->poll[i + 1], +					(fds->used - i) * sizeof(*fds->poll)); +			} + +			n = os_write_file(respond_fd, &c, sizeof(c)); +			if(n != sizeof(c)) +				printk("write_sigio_thread : write failed, " +				       "err = %d\n", -n); +		} +	} +} + +static int need_poll(int n) +{ +	if(n <= next_poll.size){ +		next_poll.used = n; +		return(0); +	} +	if(next_poll.poll != NULL) kfree(next_poll.poll); +	next_poll.poll = um_kmalloc_atomic(n * sizeof(struct pollfd)); +	if(next_poll.poll == NULL){ +		printk("need_poll : failed to allocate new pollfds\n"); +		next_poll.size = 0; +		next_poll.used = 0; +		return(-1); +	} +	next_poll.size = n; +	next_poll.used = n; +	return(0); +} + +/* Must be called with sigio_lock held, because it's needed by the marked + * critical section. */ +static void update_thread(void) +{ +	unsigned long flags; +	int n; +	char c; + +	flags = set_signals(0); +	n = os_write_file(sigio_private[0], &c, sizeof(c)); +	if(n != sizeof(c)){ +		printk("update_thread : write failed, err = %d\n", -n); +		goto fail; +	} + +	n = os_read_file(sigio_private[0], &c, sizeof(c)); +	if(n != sizeof(c)){ +		printk("update_thread : read failed, err = %d\n", -n); +		goto fail; +	} + +	set_signals(flags); +	return; + fail: +	/* Critical section start */ +	if(write_sigio_pid != -1)  +		os_kill_process(write_sigio_pid, 1); +	write_sigio_pid = -1; +	os_close_file(sigio_private[0]); +	os_close_file(sigio_private[1]); +	os_close_file(write_sigio_fds[0]); +	os_close_file(write_sigio_fds[1]); +	/* Critical section end */ +	set_signals(flags); +} + +int add_sigio_fd(int fd, int read) +{ +	int err = 0, i, n, events; + +	sigio_lock(); +	for(i = 0; i < current_poll.used; i++){ +		if(current_poll.poll[i].fd == fd)  +			goto out; +	} + +	n = current_poll.used + 1; +	err = need_poll(n); +	if(err)  +		goto out; + +	for(i = 0; i < current_poll.used; i++) +		next_poll.poll[i] = current_poll.poll[i]; + +	if(read) events = POLLIN; +	else events = POLLOUT; + +	next_poll.poll[n - 1] = ((struct pollfd) { .fd  	= fd, +						   .events 	= events, +						   .revents 	= 0 }); +	update_thread(); + out: +	sigio_unlock(); +	return(err); +} + +int ignore_sigio_fd(int fd) +{ +	struct pollfd *p; +	int err = 0, i, n = 0; + +	sigio_lock(); +	for(i = 0; i < current_poll.used; i++){ +		if(current_poll.poll[i].fd == fd) break; +	} +	if(i == current_poll.used) +		goto out; +	 +	err = need_poll(current_poll.used - 1); +	if(err) +		goto out; + +	for(i = 0; i < current_poll.used; i++){ +		p = ¤t_poll.poll[i]; +		if(p->fd != fd) next_poll.poll[n++] = current_poll.poll[i]; +	} +	if(n == i){ +		printk("ignore_sigio_fd : fd %d not found\n", fd); +		err = -1; +		goto out; +	} + +	update_thread(); + out: +	sigio_unlock(); +	return(err); +} + +static int setup_initial_poll(int fd) +{ +	struct pollfd *p; + +	p = um_kmalloc(sizeof(struct pollfd)); +	if(p == NULL){ +		printk("setup_initial_poll : failed to allocate poll\n"); +		return(-1); +	} +	*p = ((struct pollfd) { .fd  	= fd, +				.events 	= POLLIN, +				.revents 	= 0 }); +	current_poll = ((struct pollfds) { .poll 	= p, +					   .used 	= 1, +					   .size 	= 1 }); +	return(0); +} + +void write_sigio_workaround(void) +{ +	unsigned long stack; +	int err; + +	sigio_lock(); +	if(write_sigio_pid != -1) +		goto out; + +	err = os_pipe(write_sigio_fds, 1, 1); +	if(err < 0){ +		printk("write_sigio_workaround - os_pipe 1 failed, " +		       "err = %d\n", -err); +		goto out; +	} +	err = os_pipe(sigio_private, 1, 1); +	if(err < 0){ +		printk("write_sigio_workaround - os_pipe 2 failed, " +		       "err = %d\n", -err); +		goto out_close1; +	} +	if(setup_initial_poll(sigio_private[1])) +		goto out_close2; + +	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_irq(write_sigio_fds[0]))  +		goto out_kill; + + out: +	sigio_unlock(); +	return; + + out_kill: +	os_kill_process(write_sigio_pid, 1); +	write_sigio_pid = -1; + out_close2: +	os_close_file(sigio_private[0]); +	os_close_file(sigio_private[1]); + out_close1: +	os_close_file(write_sigio_fds[0]); +	os_close_file(write_sigio_fds[1]); +	sigio_unlock(); +} + +int read_sigio_fd(int fd) +{ +	int n; +	char c; + +	n = os_read_file(fd, &c, sizeof(c)); +	if(n != sizeof(c)){ +		if(n < 0) { +			printk("read_sigio_fd - read failed, err = %d\n", -n); +			return(n); +		} +		else { +			printk("read_sigio_fd - short read, bytes = %d\n", n); +			return(-EIO); +		} +	} +	return(n); +} + +static void sigio_cleanup(void) +{ +	if (write_sigio_pid != -1) { +		os_kill_process(write_sigio_pid, 1); +		write_sigio_pid = -1; +	} +} + +__uml_exitcall(sigio_cleanup); diff --git a/arch/um/kernel/signal_kern.c b/arch/um/kernel/signal_kern.c new file mode 100644 index 00000000000..7807a3e8c42 --- /dev/null +++ b/arch/um/kernel/signal_kern.c @@ -0,0 +1,213 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/stddef.h" +#include "linux/sys.h" +#include "linux/sched.h" +#include "linux/wait.h" +#include "linux/kernel.h" +#include "linux/smp_lock.h" +#include "linux/module.h" +#include "linux/slab.h" +#include "linux/tty.h" +#include "linux/binfmts.h" +#include "linux/ptrace.h" +#include "asm/signal.h" +#include "asm/uaccess.h" +#include "asm/unistd.h" +#include "user_util.h" +#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" +#include "mode.h" + +EXPORT_SYMBOL(block_signals); +EXPORT_SYMBOL(unblock_signals); + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +/* + * OK, we're invoking a handler + */	 +static int handle_signal(struct pt_regs *regs, unsigned long signr, +			 struct k_sigaction *ka, siginfo_t *info, +			 sigset_t *oldset) +{ +	unsigned long sp; +	int err; + +	/* Always make any pending restarted system calls return -EINTR */ +	current_thread_info()->restart_block.fn = do_no_restart_syscall; + +	/* Did we come from a system call? */ +	if(PT_REGS_SYSCALL_NR(regs) >= 0){ +		/* If so, check system call restarting.. */ +		switch(PT_REGS_SYSCALL_RET(regs)){ +		case -ERESTART_RESTARTBLOCK: +		case -ERESTARTNOHAND: +			PT_REGS_SYSCALL_RET(regs) = -EINTR; +			break; + +		case -ERESTARTSYS: +			if (!(ka->sa.sa_flags & SA_RESTART)) { +				PT_REGS_SYSCALL_RET(regs) = -EINTR; +				break; +			} +		/* fallthrough */ +		case -ERESTARTNOINTR: +			PT_REGS_RESTART_SYSCALL(regs); +			PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); +			break; +		} +	} + +	sp = PT_REGS_SP(regs); +	if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0)) +		sp = current->sas_ss_sp + current->sas_ss_size; + +#ifdef CONFIG_ARCH_HAS_SC_SIGNALS +	if(!(ka->sa.sa_flags & SA_SIGINFO)) +		err = setup_signal_stack_sc(sp, signr, ka, regs, oldset); +	else +#endif +		err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset); + +	if(err){ +		spin_lock_irq(¤t->sighand->siglock); +		current->blocked = *oldset; +		recalc_sigpending(); +		spin_unlock_irq(¤t->sighand->siglock); +		force_sigsegv(signr, current); +	} +	else if(!(ka->sa.sa_flags & SA_NODEFER)){ +		spin_lock_irq(¤t->sighand->siglock); +		sigorsets(¤t->blocked, ¤t->blocked,  +			  &ka->sa.sa_mask); +		sigaddset(¤t->blocked, signr); +		recalc_sigpending(); +		spin_unlock_irq(¤t->sighand->siglock); +	} + +	return err; +} + +static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset) +{ +	struct k_sigaction ka_copy; +	siginfo_t info; +	int sig, handled_sig = 0; + +	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)) +			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){ +			PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs); +			PT_REGS_RESTART_SYSCALL(regs); +		} +		else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){ +			PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall; +			PT_REGS_RESTART_SYSCALL(regs); + 		} +	} + +	/* This closes a way to execute a system call on the host.  If +	 * you set a breakpoint on a system call instruction and singlestep +	 * from it, the tracing thread used to PTRACE_SINGLESTEP the process +	 * rather than PTRACE_SYSCALL it, allowing the system call to execute +	 * on the host.  The tracing thread will check this flag and  +	 * PTRACE_SYSCALL if necessary. +	 */ +	if(current->ptrace & PT_DTRACE) +		current->thread.singlestep_syscall = +			is_syscall(PT_REGS_IP(¤t->thread.regs)); +	return(handled_sig); +} + +int do_signal(void) +{ +	return(kern_do_signal(¤t->thread.regs, ¤t->blocked)); +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +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; +	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); +	} +} + +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 new file mode 100644 index 00000000000..62f457835fb --- /dev/null +++ b/arch/um/kernel/signal_user.c @@ -0,0 +1,157 @@ +/*  + * 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 new file mode 100644 index 00000000000..d37d1bfcd6f --- /dev/null +++ b/arch/um/kernel/skas/Makefile @@ -0,0 +1,13 @@ +#  +# Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com) +# Licensed under the GPL +# + +obj-y := exec_kern.o mem.o mem_user.o mmu.o process.o process_kern.o \ +	syscall_kern.o syscall_user.o time.o tlb.o trap_user.o uaccess.o \ + +subdir- := util + +USER_OBJS := process.o time.o + +include arch/um/scripts/Makefile.rules diff --git a/arch/um/kernel/skas/exec_kern.c b/arch/um/kernel/skas/exec_kern.c new file mode 100644 index 00000000000..c6b4d5dba78 --- /dev/null +++ b/arch/um/kernel/skas/exec_kern.c @@ -0,0 +1,41 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "asm/current.h" +#include "asm/page.h" +#include "asm/signal.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "asm/mmu_context.h" +#include "tlb.h" +#include "skas.h" +#include "um_mmu.h" +#include "os.h" + +void flush_thread_skas(void) +{ +	force_flush_all(); +	switch_mm_skas(current->mm->context.skas.mm_fd); +} + +void start_thread_skas(struct pt_regs *regs, unsigned long eip,  +		       unsigned long esp) +{ +	set_fs(USER_DS); +        PT_REGS_IP(regs) = eip; +	PT_REGS_SP(regs) = esp; +} + +/* + * 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/mmu-skas.h b/arch/um/kernel/skas/include/mmu-skas.h new file mode 100644 index 00000000000..4cd60d7213f --- /dev/null +++ b/arch/um/kernel/skas/include/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 + +struct mmu_context_skas { +	int mm_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/skas/include/mode-skas.h b/arch/um/kernel/skas/include/mode-skas.h new file mode 100644 index 00000000000..c1e33bd788d --- /dev/null +++ b/arch/um/kernel/skas/include/mode-skas.h @@ -0,0 +1,34 @@ +/* + * 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 user_time_init_skas(void); +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/mode_kern-skas.h b/arch/um/kernel/skas/include/mode_kern-skas.h new file mode 100644 index 00000000000..94c56496237 --- /dev/null +++ b/arch/um/kernel/skas/include/mode_kern-skas.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_MODE_KERN_H__ +#define __SKAS_MODE_KERN_H__ + +#include "linux/sched.h" +#include "asm/page.h" +#include "asm/ptrace.h" + +extern void flush_thread_skas(void); +extern void *switch_to_skas(void *prev, void *next); +extern void start_thread_skas(struct pt_regs *regs, unsigned long eip, +			      unsigned long esp); +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 exit_thread_skas(void); +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); +extern void flush_tlb_kernel_vm_skas(void); +extern void __flush_tlb_one_skas(unsigned long addr); +extern void flush_tlb_range_skas(struct vm_area_struct *vma, +				 unsigned long start, unsigned long end); +extern void flush_tlb_mm_skas(struct mm_struct *mm); +extern void force_flush_all_skas(void); +extern long execute_syscall_skas(void *r); +extern void before_mem_skas(unsigned long unused); +extern unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out, +					 unsigned long *task_size_out); +extern int start_uml_skas(void); +extern int external_pid_skas(struct task_struct *task); +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/kernel/skas/include/proc_mm.h new file mode 100644 index 00000000000..cce61a67905 --- /dev/null +++ b/arch/um/kernel/skas/include/proc_mm.h @@ -0,0 +1,55 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_PROC_MM_H +#define __SKAS_PROC_MM_H + +#define MM_MMAP 54 +#define MM_MUNMAP 55 +#define MM_MPROTECT 56 +#define MM_COPY_SEGMENTS 57 + +struct mm_mmap { +	unsigned long addr; +	unsigned long len; +	unsigned long prot; +	unsigned long flags; +	unsigned long fd; +	unsigned long offset; +}; + +struct mm_munmap { +	unsigned long addr; +	unsigned long len;	 +}; + +struct mm_mprotect { +	unsigned long addr; +	unsigned long len; +        unsigned int prot; +}; + +struct proc_mm_op { +	int op; +	union { +		struct mm_mmap mmap; +		struct mm_munmap munmap; +	        struct mm_mprotect mprotect; +		int copy_segments; +	} u; +}; + +#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 new file mode 100644 index 00000000000..f0702c2c720 --- /dev/null +++ b/arch/um/kernel/skas/include/skas.h @@ -0,0 +1,46 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_H +#define __SKAS_H + +#include "sysdep/ptrace.h" + +extern int userspace_pid[]; + +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 void map(int fd, unsigned long virt, unsigned long len, int r, int w, +		int x, int phys_fd, unsigned long long offset); +extern int unmap(int fd, void *addr, unsigned long len); +extern int protect(int fd, unsigned long addr, unsigned long len,  +		   int r, int w, int x); +extern void user_signal(int sig, union uml_pt_regs *regs); +extern int new_mm(int from); +extern void start_userspace(int cpu); +extern long execute_syscall_skas(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/skas/include/uaccess-skas.h b/arch/um/kernel/skas/include/uaccess-skas.h new file mode 100644 index 00000000000..11986c9b9dd --- /dev/null +++ b/arch/um/kernel/skas/include/uaccess-skas.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_UACCESS_H +#define __SKAS_UACCESS_H + +#include "asm/errno.h" +#include "asm/fixmap.h" + +#define access_ok_skas(type, addr, size) \ +	((segment_eq(get_fs(), KERNEL_DS)) || \ +	 (((unsigned long) (addr) < TASK_SIZE) && \ +	  ((unsigned long) (addr) + (size) <= TASK_SIZE)) || \ +	 ((type == VERIFY_READ ) && \ +	  ((unsigned long) (addr) >= FIXADDR_USER_START) && \ +	  ((unsigned long) (addr) + (size) <= FIXADDR_USER_END) && \ +	  ((unsigned long) (addr) + (size) >= (unsigned long)(addr)))) + +static inline int verify_area_skas(int type, const void * addr, +				   unsigned long size) +{ +	return(access_ok_skas(type, addr, size) ? 0 : -EFAULT); +} + +extern int copy_from_user_skas(void *to, const void *from, int n); +extern int copy_to_user_skas(void *to, const void *from, int n); +extern int strncpy_from_user_skas(char *dst, const char *src, int count); +extern int __clear_user_skas(void *mem, int len); +extern int clear_user_skas(void *mem, int len); +extern int strnlen_user_skas(const void *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/kernel/skas/mem.c b/arch/um/kernel/skas/mem.c new file mode 100644 index 00000000000..438db2f4345 --- /dev/null +++ b/arch/um/kernel/skas/mem.c @@ -0,0 +1,35 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/mm.h" +#include "mem_user.h" + +unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out,  +				  unsigned long *task_size_out) +{ +	/* Round up to the nearest 4M */ +	unsigned long top = ROUND_4M((unsigned long) &arg); + +#ifdef CONFIG_HOST_TASK_SIZE +	*host_size_out = CONFIG_HOST_TASK_SIZE; +	*task_size_out = CONFIG_HOST_TASK_SIZE; +#else +	*host_size_out = top; +	*task_size_out = top; +#endif +	return(((unsigned long) set_task_sizes_skas) & ~0xffffff); +} + +/* + * 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/mem_user.c b/arch/um/kernel/skas/mem_user.c new file mode 100644 index 00000000000..1310bf1e88d --- /dev/null +++ b/arch/um/kernel/skas/mem_user.c @@ -0,0 +1,102 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <errno.h> +#include <sys/mman.h> +#include "mem_user.h" +#include "mem.h" +#include "user.h" +#include "os.h" +#include "proc_mm.h" + +void map(int fd, unsigned long virt, unsigned long len, int r, int w, +	 int x, int phys_fd, unsigned long long offset) +{ +	struct proc_mm_op map; +	int prot, n; + +	prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |  +		(x ? PROT_EXEC : 0); + +	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 +				       } } } ); +	n = os_write_file(fd, &map, sizeof(map)); +	if(n != sizeof(map))  +		printk("map : /proc/mm map failed, err = %d\n", -n); +} + +int unmap(int fd, void *addr, unsigned long len) +{ +	struct proc_mm_op unmap; +	int n; + +	unmap = ((struct proc_mm_op) { .op 	= MM_MUNMAP, +				       .u 	=  +				       { .munmap	=  +					 { .addr 	= (unsigned long) addr, +					   .len		= len } } } ); +	n = os_write_file(fd, &unmap, sizeof(unmap)); +	if(n != sizeof(unmap)) { +		if(n < 0) +			return(n); +		else if(n > 0) +			return(-EIO); +	} + +	return(0); +} + +int protect(int fd, unsigned long addr, unsigned long len, int r, int w,  +	    int x, int must_succeed) +{ +	struct proc_mm_op protect; +	int prot, n; + +	prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) |  +		(x ? PROT_EXEC : 0); + +	protect = ((struct proc_mm_op) { .op 	= MM_MPROTECT, +				       .u 	=  +				       { .mprotect	=  +					 { .addr 	= (unsigned long) addr, +					   .len		= len, +					   .prot	= prot } } } ); + +	n = os_write_file(fd, &protect, sizeof(protect)); +	if(n != sizeof(protect)) { +		if(n == 0) return(0); + +		if(must_succeed) +			panic("protect failed, err = %d", -n); + +		return(-EIO); +	} + +	return(0); +} + +void before_mem_skas(unsigned long unused) +{ +} + +/* + * 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/mmu.c b/arch/um/kernel/skas/mmu.c new file mode 100644 index 00000000000..6cb9a6d028a --- /dev/null +++ b/arch/um/kernel/skas/mmu.c @@ -0,0 +1,48 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/list.h" +#include "linux/spinlock.h" +#include "linux/slab.h" +#include "asm/current.h" +#include "asm/segment.h" +#include "asm/mmu.h" +#include "os.h" +#include "skas.h" + +int init_new_context_skas(struct task_struct *task, struct mm_struct *mm) +{ +	int from; + +	if((current->mm != NULL) && (current->mm != &init_mm)) +		from = current->mm->context.skas.mm_fd; +	else from = -1; + +	mm->context.skas.mm_fd = new_mm(from); +	if(mm->context.skas.mm_fd < 0){ +		printk("init_new_context_skas - new_mm failed, errno = %d\n", +		       mm->context.skas.mm_fd); +		return(mm->context.skas.mm_fd); +	} + +	return(0); +} + +void destroy_context_skas(struct mm_struct *mm) +{ +	os_close_file(mm->context.skas.mm_fd); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only.  This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c new file mode 100644 index 00000000000..b4ffaaa8124 --- /dev/null +++ b/arch/um/kernel/skas/process.c @@ -0,0 +1,339 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <setjmp.h> +#include <sched.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <sys/user.h> +#include <asm/unistd.h> +#include "user.h" +#include "ptrace_user.h" +#include "time_user.h" +#include "sysdep/ptrace.h" +#include "user_util.h" +#include "kern_util.h" +#include "skas.h" +#include "sysdep/sigcontext.h" +#include "os.h" +#include "proc_mm.h" +#include "skas_ptrace.h" +#include "chan_user.h" +#include "signal_user.h" +#include "registers.h" + +int is_skas_winch(int pid, int fd, void *data) +{ +	if(pid != os_getpid()) +		return(0); + +	register_winch_irq(-1, fd, -1, data); +	return(1); +} + +static void handle_segv(int pid) +{ +	struct ptrace_faultinfo fault; +	int err; + +	err = ptrace(PTRACE_FAULTINFO, pid, 0, &fault); +	if(err) +		panic("handle_segv - PTRACE_FAULTINFO failed, errno = %d\n", +		      errno); + +	segv(fault.addr, 0, FAULT_WRITE(fault.is_write), 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); +} + +static int userspace_tramp(void *arg) +{ +	init_new_thread_signals(0); +	enable_timer(); +	ptrace(PTRACE_TRACEME, 0, 0, 0); +	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]; + +void start_userspace(int cpu) +{ +	void *stack; +	unsigned long sp; +	int pid, status, n; + +	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 *); + +	pid = clone(userspace_tramp, (void *) sp,  +		    CLONE_FILES | CLONE_VM | SIGCHLD, NULL); +	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_SETOPTIONS failed, errno=%d\n", +		      errno); + +	if(munmap(stack, PAGE_SIZE) < 0) +		panic("start_userspace : munmap failed, errno = %d\n", errno); + +	userspace_pid[cpu] = 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: +				handle_segv(pid); +				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); +				break; +			default: +			        printk("userspace - child stopped with signal " +				       "%d\n", WSTOPSIG(status)); +			} +			interrupt_end(); + +			/* Avoid -ERESTARTSYS handling in host */ +			PT_SYSCALL_NR(regs->skas.regs) = -1; +		} +	} +} + +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; + +	*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(sigsetjmp(fork_buf, 1) == 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; + +	*switch_buf = &buf; +	fork_buf = fb; +	if(sigsetjmp(buf, 1) == 0) +		siglongjmp(*fork_buf, 1); +} + +void switch_threads(void *me, void *next) +{ +	sigjmp_buf my_buf, **me_ptr = me, *next_buf = next; +	 +	*me_ptr = &my_buf; +	if(sigsetjmp(my_buf, 1) == 0) +		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; + +	*fork_buf_ptr = &initial_jmpbuf; +	n = sigsetjmp(initial_jmpbuf, 1); +	if(n == 0) +		new_thread_proc((void *) stack, new_thread_handler); +	else if(n == 1) +		remove_sigstack(); +	else if(n == 2){ +		(*cb_proc)(cb_arg); +		siglongjmp(*cb_back, 1); +	} +	else if(n == 3){ +		kmalloc_ok = 0; +		return(0); +	} +	else if(n == 4){ +		kmalloc_ok = 0; +		return(1); +	} +	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; + +	cb_proc = proc; +	cb_arg = arg; +	cb_back = &here; + +	block_signals(); +	if(sigsetjmp(here, 1) == 0) +		siglongjmp(initial_jmpbuf, 2); +	unblock_signals(); + +	cb_proc = NULL; +	cb_arg = NULL; +	cb_back = NULL; +} + +void halt_skas(void) +{ +	block_signals(); +	siglongjmp(initial_jmpbuf, 3); +} + +void reboot_skas(void) +{ +	block_signals(); +	siglongjmp(initial_jmpbuf, 4); +} + +void switch_mm_skas(int mm_fd) +{ +	int err; + +#warning need cpu pid in switch_mm_skas +	err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_fd); +	if(err) +		panic("switch_mm_skas - PTRACE_SWITCH_MM failed, errno = %d\n", +		      errno); +} + +void kill_off_processes_skas(void) +{ +#warning need to loop over userspace_pids in kill_off_processes_skas +	os_kill_ptraced_process(userspace_pid[0], 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: + */ diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c new file mode 100644 index 00000000000..5d096ea63b9 --- /dev/null +++ b/arch/um/kernel/skas/process_kern.c @@ -0,0 +1,213 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/slab.h" +#include "linux/ptrace.h" +#include "linux/proc_fs.h" +#include "linux/file.h" +#include "linux/errno.h" +#include "linux/init.h" +#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) +{ +	struct task_struct *from, *to; + +	from = prev; +	to = next; + +	/* XXX need to check runqueues[cpu].idle */ +	if(current->pid == 0) +		switch_timers(0); + +	to->thread.prev_sched = from; +	set_current(to); + +	switch_threads(&from->thread.mode.skas.switch_buf,  +		       to->thread.mode.skas.switch_buf); + +	if(current->pid == 0) +		switch_timers(1); + +	return(current->thread.prev_sched); +} + +extern void schedule_tail(struct task_struct *prev); + +void new_thread_handler(int sig) +{ +	int (*fn)(void *), n; +	void *arg; + +	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,  +		    current->thread.mode.skas.fork_buf); + +	if(current->thread.prev_sched != NULL) +		schedule_tail(current->thread.prev_sched); +	current->thread.prev_sched = NULL; + +	/* The return value is 1 if the kernel thread execs a process, +	 * 0 if it just exits +	 */ +	n = run_kernel_thread(fn, arg, ¤t->thread.exec_buf); +	if(n == 1) +		userspace(¤t->thread.regs.regs); +	else do_exit(0); +} + +void new_thread_proc(void *stack, void (*handler)(int sig)) +{ +	init_new_thread_stack(stack, handler); +	os_usr1_process(os_getpid()); +} + +void release_thread_skas(struct task_struct *task) +{ +} + +void exit_thread_skas(void) +{ +} + +void fork_handler(int sig) +{ +        change_sig(SIGUSR1, 1); + 	thread_wait(¤t->thread.mode.skas.switch_buf,  +		    current->thread.mode.skas.fork_buf); +  	 +	force_flush_all(); +	if(current->thread.prev_sched == NULL) +		panic("blech"); + +	schedule_tail(current->thread.prev_sched); +	current->thread.prev_sched = NULL; + +	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,  +		     struct pt_regs *regs) +{ +  	void (*handler)(int); + +	if(current->thread.forking){ +	  	memcpy(&p->thread.regs.regs.skas,  +		       ¤t->thread.regs.regs.skas,  +		       sizeof(p->thread.regs.regs.skas)); +		REGS_SET_SYSCALL_RETURN(p->thread.regs.regs.skas.regs, 0); +		if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp; + +		handler = fork_handler; +	} +	else { +		init_thread_registers(&p->thread.regs.regs); +                p->thread.request.u.thread = current->thread.request.u.thread; +		handler = new_thread_handler; +	} + +	new_thread(p->thread_info, &p->thread.mode.skas.switch_buf, +		   &p->thread.mode.skas.fork_buf, handler); +	return(0); +} + +int new_mm(int from) +{ +	struct proc_mm_op copy; +	int n, 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); +	} + +	return(fd); +} + +void init_idle_skas(void) +{ +	cpu_tasks[current_thread->cpu].pid = os_getpid(); +	default_idle(); +} + +extern void start_kernel(void); + +static int start_kernel_proc(void *unused) +{ +	int pid; + +	block_signals(); +	pid = os_getpid(); + +	cpu_tasks[0].pid = pid; +	cpu_tasks[0].task = current; +#ifdef CONFIG_SMP + 	cpu_online_map = cpumask_of_cpu(0); +#endif +	start_kernel(); +	return(0); +} + +int start_uml_skas(void) +{ +	start_userspace(0); + +	init_new_thread_signals(1); +	uml_idle_timer(); + +	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, +				 &init_task.thread.mode.skas.switch_buf, +				 &init_task.thread.mode.skas.fork_buf)); +} + +int external_pid_skas(struct task_struct *task) +{ +#warning Need to look up userspace_pid by cpu +	return(userspace_pid[0]); +} + +int thread_pid_skas(struct task_struct *task) +{ +#warning Need to look up userspace_pid by cpu +	return(userspace_pid[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/kernel/skas/syscall_kern.c b/arch/um/kernel/skas/syscall_kern.c new file mode 100644 index 00000000000..bdf040ce5b8 --- /dev/null +++ b/arch/um/kernel/skas/syscall_kern.c @@ -0,0 +1,43 @@ +/*  + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/sys.h" +#include "linux/ptrace.h" +#include "asm/errno.h" +#include "asm/unistd.h" +#include "asm/ptrace.h" +#include "asm/current.h" +#include "sysdep/syscalls.h" +#include "kern_util.h" + +extern syscall_handler_t *sys_call_table[]; + +long execute_syscall_skas(void *r) +{ +	struct pt_regs *regs = r; +	long res; +	int syscall; + +	current->thread.nsyscalls++; +	nsyscalls++; +	syscall = UPT_SYSCALL_NR(®s->regs); + +	if((syscall >= NR_syscalls) || (syscall < 0)) +		res = -ENOSYS; +	else res = EXECUTE_SYSCALL(syscall, regs); + +	return(res); +} + +/* + * 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/syscall_user.c b/arch/um/kernel/skas/syscall_user.c new file mode 100644 index 00000000000..2828e6e3772 --- /dev/null +++ b/arch/um/kernel/skas/syscall_user.c @@ -0,0 +1,44 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <signal.h> +#include "kern_util.h" +#include "uml-config.h" +#include "syscall_user.h" +#include "sysdep/ptrace.h" +#include "sysdep/sigcontext.h" +#include "skas.h" + +void handle_syscall(union uml_pt_regs *regs) +{ +	long result; +#if UML_CONFIG_SYSCALL_DEBUG +  	int index; + +  	index = record_syscall_start(UPT_SYSCALL_NR(regs)); +#endif + +	syscall_trace(regs, 0); +	result = execute_syscall_skas(regs); + +	REGS_SET_SYSCALL_RETURN(regs->skas.regs, result); + +	syscall_trace(regs, 1); +#if UML_CONFIG_SYSCALL_DEBUG +  	record_syscall_end(index, result); +#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/time.c b/arch/um/kernel/skas/time.c new file mode 100644 index 00000000000..98091494b89 --- /dev/null +++ b/arch/um/kernel/skas/time.c @@ -0,0 +1,30 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <sys/signal.h> +#include <sys/time.h> +#include "time_user.h" +#include "process.h" +#include "user.h" + +void user_time_init_skas(void) +{ +        if(signal(SIGALRM, (__sighandler_t) alarm_handler) == SIG_ERR) +                panic("Couldn't set SIGALRM handler"); + 	if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR) + 		panic("Couldn't set SIGVTALRM handler"); +	set_interval(ITIMER_VIRTUAL); +} + +/* + * 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/tlb.c b/arch/um/kernel/skas/tlb.c new file mode 100644 index 00000000000..b8c5e71763d --- /dev/null +++ b/arch/um/kernel/skas/tlb.c @@ -0,0 +1,85 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright 2003 PathScale, Inc. + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/mmu.h" +#include "user_util.h" +#include "mem_user.h" +#include "mem.h" +#include "skas.h" +#include "os.h" +#include "tlb.h" + +static void do_ops(int fd, struct host_vm_op *ops, int last) +{ +	struct host_vm_op *op; +	int i; + +	for(i = 0; i <= last; i++){ +		op = &ops[i]; +		switch(op->type){ +		case MMAP: +			map(fd, op->u.mmap.addr, op->u.mmap.len, +			    op->u.mmap.r, op->u.mmap.w, op->u.mmap.x, +			    op->u.mmap.fd, op->u.mmap.offset); +			break; +		case MUNMAP: +			unmap(fd, (void *) op->u.munmap.addr, +			      op->u.munmap.len); +			break; +		case MPROTECT: +			protect(fd, op->u.mprotect.addr, op->u.mprotect.len, +				op->u.mprotect.r, op->u.mprotect.w, +				op->u.mprotect.x); +			break; +		default: +			printk("Unknown op type %d in do_ops\n", op->type); +			break; +		} +	} +} + +static void fix_range(struct mm_struct *mm, unsigned long start_addr, +		      unsigned long end_addr, int force) +{ +        int fd = mm->context.skas.mm_fd; + +        fix_range_common(mm, start_addr, end_addr, force, fd, do_ops); +} + +void __flush_tlb_one_skas(unsigned long addr) +{ +        flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE); +} + +void flush_tlb_range_skas(struct vm_area_struct *vma, unsigned long start,  +		     unsigned long end) +{ +        if(vma->vm_mm == NULL) +                flush_tlb_kernel_range_common(start, end); +        else fix_range(vma->vm_mm, start, end, 0); +} + +void flush_tlb_mm_skas(struct mm_struct *mm) +{ +	/* Don't bother flushing if this address space is about to be +         * destroyed. +         */ +        if(atomic_read(&mm->mm_users) == 0) +                return; + +        fix_range(mm, 0, host_task_size, 0); +        flush_tlb_kernel_range_common(start_vm, end_vm); +} + +void force_flush_all_skas(void) +{ +        fix_range(current->mm, 0, host_task_size, 1); +} diff --git a/arch/um/kernel/skas/trap_user.c b/arch/um/kernel/skas/trap_user.c new file mode 100644 index 00000000000..8e9b46d4702 --- /dev/null +++ b/arch/um/kernel/skas/trap_user.c @@ -0,0 +1,71 @@ +/*  + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include <signal.h> +#include <errno.h> +#include "sysdep/ptrace.h" +#include "signal_user.h" +#include "user_util.h" +#include "kern_util.h" +#include "task.h" +#include "sigcontext.h" + +void sig_handler_common_skas(int sig, void *sc_ptr) +{ +	struct sigcontext *sc = sc_ptr; +	struct skas_regs *r; +	struct signal_info *info; +	int save_errno = errno; +	int save_user; + +	/* This is done because to allow SIGSEGV to be delivered inside a SEGV +	 * handler.  This can happen in copy_user, and if SEGV is disabled, +	 * the process will die. +	 * XXX Figure out why this is better than SA_NODEFER +	 */ +	if(sig == SIGSEGV) +		change_sig(SIGSEGV, 1); + +	r = &TASK_REGS(get_current())->skas; +	save_user = r->is_user; +	r->is_user = 0; +	r->fault_addr = SC_FAULT_ADDR(sc); +	r->fault_type = SC_FAULT_TYPE(sc); +	r->trap_type = SC_TRAP_TYPE(sc); + +	change_sig(SIGUSR1, 1); +	info = &sig_info[sig]; +	if(!info->is_irq) unblock_signals(); + +	(*info->handler)(sig, (union uml_pt_regs *) r); + +	errno = save_errno; +	r->is_user = save_user; +} + +void user_signal(int sig, union uml_pt_regs *regs) +{ +	struct signal_info *info; + +	regs->skas.is_user = 1; +	regs->skas.fault_addr = 0; +	regs->skas.fault_type = 0; +	regs->skas.trap_type = 0; +	info = &sig_info[sig]; +	(*info->handler)(sig, 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/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c new file mode 100644 index 00000000000..7575ec489b6 --- /dev/null +++ b/arch/um/kernel/skas/uaccess.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/kernel.h" +#include "linux/string.h" +#include "linux/fs.h" +#include "linux/highmem.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/uaccess.h" +#include "kern_util.h" +#include "user_util.h" + +extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, +			     pte_t *pte_out); + +static unsigned long maybe_map(unsigned long virt, int is_write) +{ +	pte_t pte; +	int err; + +	void *phys = um_virt_to_phys(current, virt, &pte); +	int dummy_code; + +	if(IS_ERR(phys) || (is_write && !pte_write(pte))){ +		err = handle_page_fault(virt, 0, is_write, 1, &dummy_code); +		if(err) +			return(0); +		phys = um_virt_to_phys(current, virt, NULL); +	} +	return((unsigned long) phys); +} + +static int do_op(unsigned long addr, int len, int is_write, +		 int (*op)(unsigned long addr, int len, void *arg), void *arg) +{ +	struct page *page; +	int n; + +	addr = maybe_map(addr, is_write); +	if(addr == -1) +		return(-1); + +	page = phys_to_page(addr); +	addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK); +	n = (*op)(addr, len, arg); +	kunmap(page); + +	return(n); +} + +static void do_buffer_op(void *jmpbuf, void *arg_ptr) +{ +	va_list args; +	unsigned long addr; +	int len, is_write, size, remain, n; +	int (*op)(unsigned long, int, void *); +	void *arg; +	int *res; + +	/* Some old gccs recognize __va_copy, but not va_copy */ +	__va_copy(args, *(va_list *)arg_ptr); +	addr = va_arg(args, unsigned long); +	len = va_arg(args, int); +	is_write = va_arg(args, int); +	op = va_arg(args, void *); +	arg = va_arg(args, void *); +	res = va_arg(args, int *); +	va_end(args); +	size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); +	remain = len; + +	current->thread.fault_catcher = jmpbuf; +	n = do_op(addr, size, is_write, op, arg); +	if(n != 0){ +		*res = (n < 0 ? remain : 0); +		goto out; +	} + +	addr += size; +	remain -= size; +	if(remain == 0){ +		*res = 0; +		goto out; +	} + +	while(addr < ((addr + remain) & PAGE_MASK)){ +		n = do_op(addr, PAGE_SIZE, is_write, op, arg); +		if(n != 0){ +			*res = (n < 0 ? remain : 0); +			goto out; +		} + +		addr += PAGE_SIZE; +		remain -= PAGE_SIZE; +	} +	if(remain == 0){ +		*res = 0; +		goto out; +	} + +	n = do_op(addr, remain, is_write, op, arg); +	if(n != 0) +		*res = (n < 0 ? remain : 0); +	else *res = 0; + out: +	current->thread.fault_catcher = NULL; +} + +static int buffer_op(unsigned long addr, int len, int is_write, +		     int (*op)(unsigned long addr, int len, void *arg), +		     void *arg) +{ +	int faulted, res; + +	faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg, +				 &res); +	if(!faulted) +		return(res); + +	return(addr + len - (unsigned long) current->thread.fault_addr); +} + +static int copy_chunk_from_user(unsigned long from, int len, void *arg) +{ +	unsigned long *to_ptr = arg, to = *to_ptr; + +	memcpy((void *) to, (void *) from, len); +	*to_ptr += len; +	return(0); +} + +int copy_from_user_skas(void *to, const void __user *from, int n) +{ +	if(segment_eq(get_fs(), KERNEL_DS)){ +		memcpy(to, (__force void*)from, n); +		return(0); +	} + +	return(access_ok_skas(VERIFY_READ, from, n) ? +	       buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to): +	       n); +} + +static int copy_chunk_to_user(unsigned long to, int len, void *arg) +{ +	unsigned long *from_ptr = arg, from = *from_ptr; + +	memcpy((void *) to, (void *) from, len); +	*from_ptr += len; +	return(0); +} + +int copy_to_user_skas(void __user *to, const void *from, int n) +{ +	if(segment_eq(get_fs(), KERNEL_DS)){ +		memcpy((__force void*)to, from, n); +		return(0); +	} + +	return(access_ok_skas(VERIFY_WRITE, to, n) ? +	       buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) : +	       n); +} + +static int strncpy_chunk_from_user(unsigned long from, int len, void *arg) +{ +	char **to_ptr = arg, *to = *to_ptr; +	int n; + +	strncpy(to, (void *) from, len); +	n = strnlen(to, len); +	*to_ptr += n; + +	if(n < len) +	        return(1); +	return(0); +} + +int strncpy_from_user_skas(char *dst, const char __user *src, int count) +{ +	int n; +	char *ptr = dst; + +	if(segment_eq(get_fs(), KERNEL_DS)){ +		strncpy(dst, (__force void*)src, count); +		return(strnlen(dst, count)); +	} + +	if(!access_ok_skas(VERIFY_READ, src, 1)) +		return(-EFAULT); + +	n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, +		      &ptr); +	if(n != 0) +		return(-EFAULT); +	return(strnlen(dst, count)); +} + +static int clear_chunk(unsigned long addr, int len, void *unused) +{ +	memset((void *) addr, 0, len); +	return(0); +} + +int __clear_user_skas(void __user *mem, int len) +{ +	return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL)); +} + +int clear_user_skas(void __user *mem, int len) +{ +	if(segment_eq(get_fs(), KERNEL_DS)){ +		memset((__force void*)mem, 0, len); +		return(0); +	} + +	return(access_ok_skas(VERIFY_WRITE, mem, len) ? +	       buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len); +} + +static int strnlen_chunk(unsigned long str, int len, void *arg) +{ +	int *len_ptr = arg, n; + +	n = strnlen((void *) str, len); +	*len_ptr += n; + +	if(n < len) +		return(1); +	return(0); +} + +int strnlen_user_skas(const void __user *str, int len) +{ +	int count = 0, n; + +	if(segment_eq(get_fs(), KERNEL_DS)) +		return(strnlen((__force char*)str, len) + 1); + +	n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count); +	if(n == 0) +		return(count + 1); +	return(-EFAULT); +} + +/* + * 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/util/Makefile b/arch/um/kernel/skas/util/Makefile new file mode 100644 index 00000000000..17f5909d60f --- /dev/null +++ b/arch/um/kernel/skas/util/Makefile @@ -0,0 +1,4 @@ +hostprogs-y		:= mk_ptregs +always			:= $(hostprogs-y) + +mk_ptregs-objs := mk_ptregs-$(SUBARCH).o diff --git a/arch/um/kernel/skas/util/mk_ptregs-i386.c b/arch/um/kernel/skas/util/mk_ptregs-i386.c new file mode 100644 index 00000000000..0788dd05bca --- /dev/null +++ b/arch/um/kernel/skas/util/mk_ptregs-i386.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <asm/ptrace.h> +#include <asm/user.h> + +#define PRINT_REG(name, val) printf("#define HOST_%s %d\n", (name), (val)) + +int main(int argc, char **argv) +{ +	printf("/* Automatically generated by " +	       "arch/um/kernel/skas/util/mk_ptregs */\n"); +	printf("\n"); +	printf("#ifndef __SKAS_PT_REGS_\n"); +	printf("#define __SKAS_PT_REGS_\n"); +	printf("\n"); +	printf("#define HOST_FRAME_SIZE %d\n", FRAME_SIZE); +	printf("#define HOST_FP_SIZE %d\n", +	       sizeof(struct user_i387_struct) / sizeof(unsigned long)); +	printf("#define HOST_XFP_SIZE %d\n", +	       sizeof(struct user_fxsr_struct) / sizeof(unsigned long)); + +	PRINT_REG("IP", EIP); +	PRINT_REG("SP", UESP); +	PRINT_REG("EFLAGS", EFL); +	PRINT_REG("EAX", EAX); +	PRINT_REG("EBX", EBX); +	PRINT_REG("ECX", ECX); +	PRINT_REG("EDX", EDX); +	PRINT_REG("ESI", ESI); +	PRINT_REG("EDI", EDI); +	PRINT_REG("EBP", EBP); +	PRINT_REG("CS", CS); +	PRINT_REG("SS", SS); +	PRINT_REG("DS", DS); +	PRINT_REG("FS", FS); +	PRINT_REG("ES", ES); +	PRINT_REG("GS", GS); +	printf("\n"); +	printf("#endif\n"); +	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/kernel/skas/util/mk_ptregs-x86_64.c b/arch/um/kernel/skas/util/mk_ptregs-x86_64.c new file mode 100644 index 00000000000..67aee92a70e --- /dev/null +++ b/arch/um/kernel/skas/util/mk_ptregs-x86_64.c @@ -0,0 +1,68 @@ +/* + * Copyright 2003 PathScale, Inc. + * + * Licensed under the GPL + */ + +#include <stdio.h> +#define __FRAME_OFFSETS +#include <asm/ptrace.h> + +#define PRINT_REG(name, val) \ +	printf("#define HOST_%s (%d / sizeof(unsigned long))\n", (name), (val)) + +int main(int argc, char **argv) +{ +	printf("/* Automatically generated by " +	       "arch/um/kernel/skas/util/mk_ptregs */\n"); +	printf("\n"); +	printf("#ifndef __SKAS_PT_REGS_\n"); +	printf("#define __SKAS_PT_REGS_\n"); +	printf("#define HOST_FRAME_SIZE (%d / sizeof(unsigned long))\n", +	       FRAME_SIZE); +	PRINT_REG("RBX", RBX); +	PRINT_REG("RCX", RCX); +	PRINT_REG("RDI", RDI); +	PRINT_REG("RSI", RSI); +	PRINT_REG("RDX", RDX); +	PRINT_REG("RBP", RBP); +	PRINT_REG("RAX", RAX); +	PRINT_REG("R8", R8); +	PRINT_REG("R9", R9); +	PRINT_REG("R10", R10); +	PRINT_REG("R11", R11); +	PRINT_REG("R12", R12); +	PRINT_REG("R13", R13); +	PRINT_REG("R14", R14); +	PRINT_REG("R15", R15); +	PRINT_REG("ORIG_RAX", ORIG_RAX); +	PRINT_REG("CS", CS); +	PRINT_REG("SS", SS); +	PRINT_REG("EFLAGS", EFLAGS); +#if 0 +	PRINT_REG("FS", FS); +	PRINT_REG("GS", GS); +	PRINT_REG("DS", DS); +	PRINT_REG("ES", ES); +#endif + +	PRINT_REG("IP", RIP); +	PRINT_REG("SP", RSP); +	printf("#define HOST_FP_SIZE 0\n"); +	printf("#define HOST_XFP_SIZE 0\n"); +	printf("\n"); +	printf("\n"); +	printf("#endif\n"); +	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/kernel/smp.c b/arch/um/kernel/smp.c new file mode 100644 index 00000000000..72113b0a96e --- /dev/null +++ b/arch/um/kernel/smp.c @@ -0,0 +1,269 @@ +/*  + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/percpu.h" +#include "asm/pgalloc.h" +#include "asm/tlb.h" + +/* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */ +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +#ifdef CONFIG_SMP + +#include "linux/sched.h" +#include "linux/module.h" +#include "linux/threads.h" +#include "linux/interrupt.h" +#include "linux/err.h" +#include "linux/hardirq.h" +#include "asm/smp.h" +#include "asm/processor.h" +#include "asm/spinlock.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "irq_user.h" +#include "os.h" + +/* CPU online map, set by smp_boot_cpus */ +cpumask_t cpu_online_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map = CPU_MASK_NONE; + +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL(cpu_possible_map); + +/* Per CPU bogomips and other parameters + * The only piece used here is the ipi pipe, which is set before SMP is + * started and never changed. + */ +struct cpuinfo_um cpu_data[NR_CPUS]; + +/* A statistic, can be a little off */ +int num_reschedules_sent = 0; + +/* Not changed after boot */ +struct task_struct *idle_threads[NR_CPUS]; + +void smp_send_reschedule(int cpu) +{ +	os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1); +	num_reschedules_sent++; +} + +void smp_send_stop(void) +{ +	int i; + +	printk(KERN_INFO "Stopping all CPUs..."); +	for(i = 0; i < num_online_cpus(); i++){ +		if(i == current_thread->cpu) +			continue; +		os_write_file(cpu_data[i].ipi_pipe[1], "S", 1); +	} +	printk("done\n"); +} + +static cpumask_t smp_commenced_mask = CPU_MASK_NONE; +static cpumask_t cpu_callin_map = CPU_MASK_NONE; + +static int idle_proc(void *cpup) +{ +	int cpu = (int) cpup, err; + +	err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); +	if(err < 0) +		panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); + +	activate_ipi(cpu_data[cpu].ipi_pipe[0],  +		     current->thread.mode.tt.extern_pid); +  +	wmb(); +	if (cpu_test_and_set(cpu, cpu_callin_map)) { +		printk("huh, CPU#%d already present??\n", cpu); +		BUG(); +	} + +	while (!cpu_isset(cpu, smp_commenced_mask)) +		cpu_relax(); + +	cpu_set(cpu, cpu_online_map); +	default_idle(); +	return(0); +} + +static struct task_struct *idle_thread(int cpu) +{ +	struct task_struct *new_task; +	unsigned char c; + +        current->thread.request.u.thread.proc = idle_proc; +        current->thread.request.u.thread.arg = (void *) cpu; +	new_task = fork_idle(cpu); +	if(IS_ERR(new_task)) +		panic("copy_process failed in idle_thread, error = %ld", +		      PTR_ERR(new_task)); + +	cpu_tasks[cpu] = ((struct cpu_task)  +		          { .pid = 	new_task->thread.mode.tt.extern_pid, +			    .task = 	new_task } ); +	idle_threads[cpu] = new_task; +	CHOOSE_MODE(os_write_file(new_task->thread.mode.tt.switch_pipe[1], &c, +			  sizeof(c)), +		    ({ panic("skas mode doesn't support SMP"); })); +	return(new_task); +} + +void smp_prepare_cpus(unsigned int maxcpus) +{ +	struct task_struct *idle; +	unsigned long waittime; +	int err, cpu, me = smp_processor_id(); +	int i; + +	for (i = 0; i < ncpus; ++i) +		cpu_set(i, cpu_possible_map); + +	cpu_clear(me, cpu_online_map); +	cpu_set(me, cpu_online_map); +	cpu_set(me, cpu_callin_map); + +	err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); +	if(err < 0) +		panic("CPU#0 failed to create IPI pipe, errno = %d", -err); + +	activate_ipi(cpu_data[me].ipi_pipe[0], +		     current->thread.mode.tt.extern_pid); + +	for(cpu = 1; cpu < ncpus; cpu++){ +		printk("Booting processor %d...\n", cpu); +		 +		idle = idle_thread(cpu); + +		init_idle(idle, cpu); +		unhash_process(idle); + +		waittime = 200000000; +		while (waittime-- && !cpu_isset(cpu, cpu_callin_map)) +			cpu_relax(); + +		if (cpu_isset(cpu, cpu_callin_map)) +			printk("done\n"); +		else printk("failed\n"); +	} +} + +void smp_prepare_boot_cpu(void) +{ +	cpu_set(smp_processor_id(), cpu_online_map); +} + +int __cpu_up(unsigned int cpu) +{ +	cpu_set(cpu, smp_commenced_mask); +	while (!cpu_isset(cpu, cpu_online_map)) +		mb(); +	return(0); +} + +int setup_profiling_timer(unsigned int multiplier) +{ +	printk(KERN_INFO "setup_profiling_timer\n"); +	return(0); +} + +void smp_call_function_slave(int cpu); + +void IPI_handler(int cpu) +{ +	unsigned char c; +	int fd; + +	fd = cpu_data[cpu].ipi_pipe[0]; +	while (os_read_file(fd, &c, 1) == 1) { +		switch (c) { +		case 'C': +			smp_call_function_slave(cpu); +			break; + +		case 'R': +			set_tsk_need_resched(current); +			break; + +		case 'S': +			printk("CPU#%d stopping\n", cpu); +			while(1) +				pause(); +			break; + +		default: +			printk("CPU#%d received unknown IPI [%c]!\n", cpu, c); +			break; +		} +	} +} + +int hard_smp_processor_id(void) +{ +	return(pid_to_processor_id(os_getpid())); +} + +static DEFINE_SPINLOCK(call_lock); +static atomic_t scf_started; +static atomic_t scf_finished; +static void (*func)(void *info); +static void *info; + +void smp_call_function_slave(int cpu) +{ +	atomic_inc(&scf_started); +	(*func)(info); +	atomic_inc(&scf_finished); +} + +int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic,  +		      int wait) +{ +	int cpus = num_online_cpus() - 1; +	int i; + +	if (!cpus) +		return 0; + +	/* Can deadlock when called with interrupts disabled */ +	WARN_ON(irqs_disabled()); + +	spin_lock_bh(&call_lock); +	atomic_set(&scf_started, 0); +	atomic_set(&scf_finished, 0); +	func = _func; +	info = _info; + +	for_each_online_cpu(i) +		os_write_file(cpu_data[i].ipi_pipe[1], "C", 1); + +	while (atomic_read(&scf_started) != cpus) +		barrier(); + +	if (wait) +		while (atomic_read(&scf_finished) != cpus) +			barrier(); + +	spin_unlock_bh(&call_lock); +	return 0; +} + +#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/sys_call_table.c b/arch/um/kernel/sys_call_table.c new file mode 100644 index 00000000000..7fc06c85b29 --- /dev/null +++ b/arch/um/kernel/sys_call_table.c @@ -0,0 +1,276 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright 2003 PathScale, Inc. + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/unistd.h" +#include "linux/sys.h" +#include "linux/swap.h" +#include "linux/syscalls.h" +#include "linux/sysctl.h" +#include "asm/signal.h" +#include "sysdep/syscalls.h" +#include "kern_util.h" + +#ifdef CONFIG_NFSD +#define NFSSERVCTL sys_nfsservctl +#else +#define NFSSERVCTL sys_ni_syscall +#endif + +#define LAST_GENERIC_SYSCALL __NR_keyctl + +#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL +#define LAST_SYSCALL LAST_GENERIC_SYSCALL +#else +#define LAST_SYSCALL LAST_ARCH_SYSCALL +#endif + +extern syscall_handler_t sys_fork; +extern syscall_handler_t sys_execve; +extern syscall_handler_t um_time; +extern syscall_handler_t um_stime; +extern syscall_handler_t sys_pipe; +extern syscall_handler_t sys_olduname; +extern syscall_handler_t sys_sigaction; +extern syscall_handler_t sys_sigsuspend; +extern syscall_handler_t old_readdir; +extern syscall_handler_t sys_uname; +extern syscall_handler_t sys_ipc; +extern syscall_handler_t sys_sigreturn; +extern syscall_handler_t sys_clone; +extern syscall_handler_t sys_rt_sigreturn; +extern syscall_handler_t sys_sigaltstack; +extern syscall_handler_t sys_vfork; +extern syscall_handler_t old_select; +extern syscall_handler_t sys_modify_ldt; +extern syscall_handler_t sys_rt_sigsuspend; +extern syscall_handler_t sys_mbind; +extern syscall_handler_t sys_get_mempolicy; +extern syscall_handler_t sys_set_mempolicy; +extern syscall_handler_t sys_sys_setaltroot; + +syscall_handler_t *sys_call_table[] = { +	[ __NR_restart_syscall ] = (syscall_handler_t *) sys_restart_syscall, +	[ __NR_exit ] = (syscall_handler_t *) sys_exit, +	[ __NR_fork ] = (syscall_handler_t *) sys_fork, +	[ __NR_read ] = (syscall_handler_t *) sys_read, +	[ __NR_write ] = (syscall_handler_t *) sys_write, + +	/* These three are declared differently in asm/unistd.h */ +	[ __NR_open ] = (syscall_handler_t *) sys_open, +	[ __NR_close ] = (syscall_handler_t *) sys_close, +	[ __NR_creat ] = (syscall_handler_t *) sys_creat, +	[ __NR_link ] = (syscall_handler_t *) sys_link, +	[ __NR_unlink ] = (syscall_handler_t *) sys_unlink, +	[ __NR_execve ] = (syscall_handler_t *) sys_execve, + +	/* declared differently in kern_util.h */ +	[ __NR_chdir ] = (syscall_handler_t *) sys_chdir, +	[ __NR_time ] = um_time, +	[ __NR_mknod ] = (syscall_handler_t *) sys_mknod, +	[ __NR_chmod ] = (syscall_handler_t *) sys_chmod, +	[ __NR_lchown ] = (syscall_handler_t *) sys_lchown16, +	[ __NR_lseek ] = (syscall_handler_t *) sys_lseek, +	[ __NR_getpid ] = (syscall_handler_t *) sys_getpid, +	[ __NR_mount ] = (syscall_handler_t *) sys_mount, +	[ __NR_setuid ] = (syscall_handler_t *) sys_setuid16, +	[ __NR_getuid ] = (syscall_handler_t *) sys_getuid16, + 	[ __NR_ptrace ] = (syscall_handler_t *) sys_ptrace, +	[ __NR_alarm ] = (syscall_handler_t *) sys_alarm, +	[ __NR_pause ] = (syscall_handler_t *) sys_pause, +	[ __NR_utime ] = (syscall_handler_t *) sys_utime, +	[ __NR_access ] = (syscall_handler_t *) sys_access, +	[ __NR_sync ] = (syscall_handler_t *) sys_sync, +	[ __NR_kill ] = (syscall_handler_t *) sys_kill, +	[ __NR_rename ] = (syscall_handler_t *) sys_rename, +	[ __NR_mkdir ] = (syscall_handler_t *) sys_mkdir, +	[ __NR_rmdir ] = (syscall_handler_t *) sys_rmdir, + +	/* Declared differently in asm/unistd.h */ +	[ __NR_dup ] = (syscall_handler_t *) sys_dup, +	[ __NR_pipe ] = (syscall_handler_t *) sys_pipe, +	[ __NR_times ] = (syscall_handler_t *) sys_times, +	[ __NR_brk ] = (syscall_handler_t *) sys_brk, +	[ __NR_setgid ] = (syscall_handler_t *) sys_setgid16, +	[ __NR_getgid ] = (syscall_handler_t *) sys_getgid16, +	[ __NR_geteuid ] = (syscall_handler_t *) sys_geteuid16, +	[ __NR_getegid ] = (syscall_handler_t *) sys_getegid16, +	[ __NR_acct ] = (syscall_handler_t *) sys_acct, +	[ __NR_umount2 ] = (syscall_handler_t *) sys_umount, +	[ __NR_ioctl ] = (syscall_handler_t *) sys_ioctl, +	[ __NR_fcntl ] = (syscall_handler_t *) sys_fcntl, +	[ __NR_setpgid ] = (syscall_handler_t *) sys_setpgid, +	[ __NR_umask ] = (syscall_handler_t *) sys_umask, +	[ __NR_chroot ] = (syscall_handler_t *) sys_chroot, +	[ __NR_ustat ] = (syscall_handler_t *) sys_ustat, +	[ __NR_dup2 ] = (syscall_handler_t *) sys_dup2, +	[ __NR_getppid ] = (syscall_handler_t *) sys_getppid, +	[ __NR_getpgrp ] = (syscall_handler_t *) sys_getpgrp, +	[ __NR_setsid ] = (syscall_handler_t *) sys_setsid, +	[ __NR_setreuid ] = (syscall_handler_t *) sys_setreuid16, +	[ __NR_setregid ] = (syscall_handler_t *) sys_setregid16, +	[ __NR_sethostname ] = (syscall_handler_t *) sys_sethostname, +	[ __NR_setrlimit ] = (syscall_handler_t *) sys_setrlimit, +	[ __NR_getrlimit ] = (syscall_handler_t *) sys_old_getrlimit, +	[ __NR_getrusage ] = (syscall_handler_t *) sys_getrusage, +	[ __NR_gettimeofday ] = (syscall_handler_t *) sys_gettimeofday, +	[ __NR_settimeofday ] = (syscall_handler_t *) sys_settimeofday, +	[ __NR_getgroups ] = (syscall_handler_t *) sys_getgroups16, +	[ __NR_setgroups ] = (syscall_handler_t *) sys_setgroups16, +	[ __NR_symlink ] = (syscall_handler_t *) sys_symlink, +	[ __NR_readlink ] = (syscall_handler_t *) sys_readlink, +	[ __NR_uselib ] = (syscall_handler_t *) sys_uselib, +	[ __NR_swapon ] = (syscall_handler_t *) sys_swapon, +	[ __NR_reboot ] = (syscall_handler_t *) sys_reboot, +	[ __NR_munmap ] = (syscall_handler_t *) sys_munmap, +	[ __NR_truncate ] = (syscall_handler_t *) sys_truncate, +	[ __NR_ftruncate ] = (syscall_handler_t *) sys_ftruncate, +	[ __NR_fchmod ] = (syscall_handler_t *) sys_fchmod, +	[ __NR_fchown ] = (syscall_handler_t *) sys_fchown16, +	[ __NR_getpriority ] = (syscall_handler_t *) sys_getpriority, +	[ __NR_setpriority ] = (syscall_handler_t *) sys_setpriority, +	[ __NR_statfs ] = (syscall_handler_t *) sys_statfs, +	[ __NR_fstatfs ] = (syscall_handler_t *) sys_fstatfs, +	[ __NR_ioperm ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_syslog ] = (syscall_handler_t *) sys_syslog, +	[ __NR_setitimer ] = (syscall_handler_t *) sys_setitimer, +	[ __NR_getitimer ] = (syscall_handler_t *) sys_getitimer, +	[ __NR_stat ] = (syscall_handler_t *) sys_newstat, +	[ __NR_lstat ] = (syscall_handler_t *) sys_newlstat, +	[ __NR_fstat ] = (syscall_handler_t *) sys_newfstat, +	[ __NR_vhangup ] = (syscall_handler_t *) sys_vhangup, +	[ __NR_wait4 ] = (syscall_handler_t *) sys_wait4, +	[ __NR_swapoff ] = (syscall_handler_t *) sys_swapoff, +	[ __NR_sysinfo ] = (syscall_handler_t *) sys_sysinfo, +	[ __NR_fsync ] = (syscall_handler_t *) sys_fsync, +	[ __NR_clone ] = (syscall_handler_t *) sys_clone, +	[ __NR_setdomainname ] = (syscall_handler_t *) sys_setdomainname, +	[ __NR_uname ] = (syscall_handler_t *) sys_newuname, +	[ __NR_adjtimex ] = (syscall_handler_t *) sys_adjtimex, +	[ __NR_mprotect ] = (syscall_handler_t *) sys_mprotect, +	[ __NR_create_module ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_init_module ] = (syscall_handler_t *) sys_init_module, +	[ __NR_delete_module ] = (syscall_handler_t *) sys_delete_module, +	[ __NR_get_kernel_syms ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_quotactl ] = (syscall_handler_t *) sys_quotactl, +	[ __NR_getpgid ] = (syscall_handler_t *) sys_getpgid, +	[ __NR_fchdir ] = (syscall_handler_t *) sys_fchdir, +	[ __NR_sysfs ] = (syscall_handler_t *) sys_sysfs, +	[ __NR_personality ] = (syscall_handler_t *) sys_personality, +	[ __NR_afs_syscall ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_setfsuid ] = (syscall_handler_t *) sys_setfsuid16, +	[ __NR_setfsgid ] = (syscall_handler_t *) sys_setfsgid16, +	[ __NR_getdents ] = (syscall_handler_t *) sys_getdents, +	[ __NR_flock ] = (syscall_handler_t *) sys_flock, +	[ __NR_msync ] = (syscall_handler_t *) sys_msync, +	[ __NR_readv ] = (syscall_handler_t *) sys_readv, +	[ __NR_writev ] = (syscall_handler_t *) sys_writev, +	[ __NR_getsid ] = (syscall_handler_t *) sys_getsid, +	[ __NR_fdatasync ] = (syscall_handler_t *) sys_fdatasync, +	[ __NR__sysctl ] = (syscall_handler_t *) sys_sysctl, +	[ __NR_mlock ] = (syscall_handler_t *) sys_mlock, +	[ __NR_munlock ] = (syscall_handler_t *) sys_munlock, +	[ __NR_mlockall ] = (syscall_handler_t *) sys_mlockall, +	[ __NR_munlockall ] = (syscall_handler_t *) sys_munlockall, +	[ __NR_sched_setparam ] = (syscall_handler_t *) sys_sched_setparam, +	[ __NR_sched_getparam ] = (syscall_handler_t *) sys_sched_getparam, +	[ __NR_sched_setscheduler ] = (syscall_handler_t *) sys_sched_setscheduler, +	[ __NR_sched_getscheduler ] = (syscall_handler_t *) sys_sched_getscheduler, +	[ __NR_sched_yield ] = (syscall_handler_t *) yield, +	[ __NR_sched_get_priority_max ] = (syscall_handler_t *) sys_sched_get_priority_max, +	[ __NR_sched_get_priority_min ] = (syscall_handler_t *) sys_sched_get_priority_min, +	[ __NR_sched_rr_get_interval ] = (syscall_handler_t *) sys_sched_rr_get_interval, +	[ __NR_nanosleep ] = (syscall_handler_t *) sys_nanosleep, +	[ __NR_mremap ] = (syscall_handler_t *) sys_mremap, +	[ __NR_setresuid ] = (syscall_handler_t *) sys_setresuid16, +	[ __NR_getresuid ] = (syscall_handler_t *) sys_getresuid16, +	[ __NR_query_module ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_poll ] = (syscall_handler_t *) sys_poll, +	[ __NR_nfsservctl ] = (syscall_handler_t *) NFSSERVCTL, +	[ __NR_setresgid ] = (syscall_handler_t *) sys_setresgid16, +	[ __NR_getresgid ] = (syscall_handler_t *) sys_getresgid16, +	[ __NR_prctl ] = (syscall_handler_t *) sys_prctl, +	[ __NR_rt_sigreturn ] = (syscall_handler_t *) sys_rt_sigreturn, +	[ __NR_rt_sigaction ] = (syscall_handler_t *) sys_rt_sigaction, +	[ __NR_rt_sigprocmask ] = (syscall_handler_t *) sys_rt_sigprocmask, +	[ __NR_rt_sigpending ] = (syscall_handler_t *) sys_rt_sigpending, +	[ __NR_rt_sigtimedwait ] = (syscall_handler_t *) sys_rt_sigtimedwait, +	[ __NR_rt_sigqueueinfo ] = (syscall_handler_t *) sys_rt_sigqueueinfo, +	[ __NR_rt_sigsuspend ] = (syscall_handler_t *) sys_rt_sigsuspend, +	[ __NR_pread64 ] = (syscall_handler_t *) sys_pread64, +	[ __NR_pwrite64 ] = (syscall_handler_t *) sys_pwrite64, +	[ __NR_chown ] = (syscall_handler_t *) sys_chown16, +	[ __NR_getcwd ] = (syscall_handler_t *) sys_getcwd, +	[ __NR_capget ] = (syscall_handler_t *) sys_capget, +	[ __NR_capset ] = (syscall_handler_t *) sys_capset, +	[ __NR_sigaltstack ] = (syscall_handler_t *) sys_sigaltstack, +	[ __NR_sendfile ] = (syscall_handler_t *) sys_sendfile, +	[ __NR_getpmsg ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_putpmsg ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_vfork ] = (syscall_handler_t *) sys_vfork, +	[ __NR_getdents64 ] = (syscall_handler_t *) sys_getdents64, +	[ __NR_gettid ] = (syscall_handler_t *) sys_gettid, +	[ __NR_readahead ] = (syscall_handler_t *) sys_readahead, +	[ __NR_setxattr ] = (syscall_handler_t *) sys_setxattr, +	[ __NR_lsetxattr ] = (syscall_handler_t *) sys_lsetxattr, +	[ __NR_fsetxattr ] = (syscall_handler_t *) sys_fsetxattr, +	[ __NR_getxattr ] = (syscall_handler_t *) sys_getxattr, +	[ __NR_lgetxattr ] = (syscall_handler_t *) sys_lgetxattr, +	[ __NR_fgetxattr ] = (syscall_handler_t *) sys_fgetxattr, +	[ __NR_listxattr ] = (syscall_handler_t *) sys_listxattr, +	[ __NR_llistxattr ] = (syscall_handler_t *) sys_llistxattr, +	[ __NR_flistxattr ] = (syscall_handler_t *) sys_flistxattr, +	[ __NR_removexattr ] = (syscall_handler_t *) sys_removexattr, +	[ __NR_lremovexattr ] = (syscall_handler_t *) sys_lremovexattr, +	[ __NR_fremovexattr ] = (syscall_handler_t *) sys_fremovexattr, +	[ __NR_tkill ] = (syscall_handler_t *) sys_tkill, +	[ __NR_futex ] = (syscall_handler_t *) sys_futex, +	[ __NR_sched_setaffinity ] = (syscall_handler_t *) sys_sched_setaffinity, +	[ __NR_sched_getaffinity ] = (syscall_handler_t *) sys_sched_getaffinity, +	[ __NR_io_setup ] = (syscall_handler_t *) sys_io_setup, +	[ __NR_io_destroy ] = (syscall_handler_t *) sys_io_destroy, +	[ __NR_io_getevents ] = (syscall_handler_t *) sys_io_getevents, +	[ __NR_io_submit ] = (syscall_handler_t *) sys_io_submit, +	[ __NR_io_cancel ] = (syscall_handler_t *) sys_io_cancel, +	[ __NR_exit_group ] = (syscall_handler_t *) sys_exit_group, +	[ __NR_lookup_dcookie ] = (syscall_handler_t *) sys_lookup_dcookie, +	[ __NR_epoll_create ] = (syscall_handler_t *) sys_epoll_create, +	[ __NR_epoll_ctl ] = (syscall_handler_t *) sys_epoll_ctl, +	[ __NR_epoll_wait ] = (syscall_handler_t *) sys_epoll_wait, +	[ __NR_remap_file_pages ] = (syscall_handler_t *) sys_remap_file_pages, +	[ __NR_set_tid_address ] = (syscall_handler_t *) sys_set_tid_address, +	[ __NR_timer_create ] = (syscall_handler_t *) sys_timer_create, +	[ __NR_timer_settime ] = (syscall_handler_t *) sys_timer_settime, +	[ __NR_timer_gettime ] = (syscall_handler_t *) sys_timer_gettime, +	[ __NR_timer_getoverrun ] = (syscall_handler_t *) sys_timer_getoverrun, +	[ __NR_timer_delete ] = (syscall_handler_t *) sys_timer_delete, +	[ __NR_clock_settime ] = (syscall_handler_t *) sys_clock_settime, +	[ __NR_clock_gettime ] = (syscall_handler_t *) sys_clock_gettime, +	[ __NR_clock_getres ] = (syscall_handler_t *) sys_clock_getres, +	[ __NR_clock_nanosleep ] = (syscall_handler_t *) sys_clock_nanosleep, +	[ __NR_tgkill ] = (syscall_handler_t *) sys_tgkill, +	[ __NR_utimes ] = (syscall_handler_t *) sys_utimes, +	[ __NR_fadvise64 ] = (syscall_handler_t *) sys_fadvise64, +	[ __NR_vserver ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_mbind ] = (syscall_handler_t *) sys_mbind, +	[ __NR_get_mempolicy ] = (syscall_handler_t *) sys_get_mempolicy, +	[ __NR_set_mempolicy ] = (syscall_handler_t *) sys_set_mempolicy, +	[ __NR_mq_open ] = (syscall_handler_t *) sys_mq_open, +	[ __NR_mq_unlink ] = (syscall_handler_t *) sys_mq_unlink, +	[ __NR_mq_timedsend ] = (syscall_handler_t *) sys_mq_timedsend, +	[ __NR_mq_timedreceive ] = (syscall_handler_t *) sys_mq_timedreceive, +	[ __NR_mq_notify ] = (syscall_handler_t *) sys_mq_notify, +	[ __NR_mq_getsetattr ] = (syscall_handler_t *) sys_mq_getsetattr, +	[ __NR_kexec_load ] = (syscall_handler_t *) sys_ni_syscall, +	[ __NR_waitid ] = (syscall_handler_t *) sys_waitid, +	[ __NR_add_key ] = (syscall_handler_t *) sys_add_key, +	[ __NR_request_key ] = (syscall_handler_t *) sys_request_key, +	[ __NR_keyctl ] = (syscall_handler_t *) sys_keyctl, + +	ARCH_SYSCALLS +	[ LAST_SYSCALL + 1 ... NR_syscalls ] =  +		(syscall_handler_t *) sys_ni_syscall +}; diff --git a/arch/um/kernel/syscall_kern.c b/arch/um/kernel/syscall_kern.c new file mode 100644 index 00000000000..42731e04f50 --- /dev/null +++ b/arch/um/kernel/syscall_kern.c @@ -0,0 +1,176 @@ +/*  + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/file.h" +#include "linux/smp_lock.h" +#include "linux/mm.h" +#include "linux/utsname.h" +#include "linux/msg.h" +#include "linux/shm.h" +#include "linux/sys.h" +#include "linux/syscalls.h" +#include "linux/unistd.h" +#include "linux/slab.h" +#include "linux/utime.h" +#include "asm/mman.h" +#include "asm/uaccess.h" +#include "asm/ipc.h" +#include "kern_util.h" +#include "user_util.h" +#include "sysdep/syscalls.h" +#include "mode_kern.h" +#include "choose-mode.h" + +/*  Unlocked, I don't care if this is a bit off */ +int nsyscalls = 0; + +long sys_fork(void) +{ +	long ret; + +	current->thread.forking = 1; +        ret = do_fork(SIGCHLD, 0, NULL, 0, NULL, NULL); +	current->thread.forking = 0; +	return(ret); +} + +long sys_vfork(void) +{ +	long ret; + +	current->thread.forking = 1; +	ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0, NULL, +		      NULL); +	current->thread.forking = 0; +	return(ret); +} + +/* common code for old and new mmaps */ +long sys_mmap2(unsigned long addr, unsigned long len, +	       unsigned long prot, unsigned long flags, +	       unsigned long fd, unsigned long pgoff) +{ +	long error = -EBADF; +	struct file * file = NULL; + +	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); +	if (!(flags & MAP_ANONYMOUS)) { +		file = fget(fd); +		if (!file) +			goto out; +	} + +	down_write(¤t->mm->mmap_sem); +	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); +	up_write(¤t->mm->mmap_sem); + +	if (file) +		fput(file); + out: +	return error; +} + +long old_mmap(unsigned long addr, unsigned long len, +	      unsigned long prot, unsigned long flags, +	      unsigned long fd, unsigned long offset) +{ +	long err = -EINVAL; +	if (offset & ~PAGE_MASK) +		goto out; + +	err = sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); + out: +	return err; +} +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +long sys_pipe(unsigned long __user * fildes) +{ +        int fd[2]; +        long error; + +        error = do_pipe(fd); +        if (!error) { +		if (copy_to_user(fildes, fd, sizeof(fd))) +                        error = -EFAULT; +        } +        return error; +} + + +long sys_uname(struct old_utsname * name) +{ +	long err; +	if (!name) +		return -EFAULT; +	down_read(&uts_sem); +	err=copy_to_user(name, &system_utsname, sizeof (*name)); +	up_read(&uts_sem); +	return err?-EFAULT:0; +} + +long sys_olduname(struct oldold_utsname * name) +{ +	long error; + +	if (!name) +		return -EFAULT; +	if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) +		return -EFAULT; +   +  	down_read(&uts_sem); +	 +	error = __copy_to_user(&name->sysname,&system_utsname.sysname, +			       __OLD_UTS_LEN); +	error |= __put_user(0,name->sysname+__OLD_UTS_LEN); +	error |= __copy_to_user(&name->nodename,&system_utsname.nodename, +				__OLD_UTS_LEN); +	error |= __put_user(0,name->nodename+__OLD_UTS_LEN); +	error |= __copy_to_user(&name->release,&system_utsname.release, +				__OLD_UTS_LEN); +	error |= __put_user(0,name->release+__OLD_UTS_LEN); +	error |= __copy_to_user(&name->version,&system_utsname.version, +				__OLD_UTS_LEN); +	error |= __put_user(0,name->version+__OLD_UTS_LEN); +	error |= __copy_to_user(&name->machine,&system_utsname.machine, +				__OLD_UTS_LEN); +	error |= __put_user(0,name->machine+__OLD_UTS_LEN); +	 +	up_read(&uts_sem); +	 +	error = error ? -EFAULT : 0; + +	return error; +} + +DEFINE_SPINLOCK(syscall_lock); + +static int syscall_index = 0; + +int next_syscall_index(int limit) +{ +	int ret; + +	spin_lock(&syscall_lock); +	ret = syscall_index; +	if(++syscall_index == limit) +		syscall_index = 0; +	spin_unlock(&syscall_lock); +	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/syscall_user.c b/arch/um/kernel/syscall_user.c new file mode 100644 index 00000000000..01b711e00a8 --- /dev/null +++ b/arch/um/kernel/syscall_user.c @@ -0,0 +1,48 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <sys/time.h> +#include "kern_util.h" +#include "syscall_user.h" + +struct { +	int syscall; +	int pid; +	long result; +	struct timeval start; +	struct timeval end; +} syscall_record[1024]; + +int record_syscall_start(int syscall) +{ +	int max, index; + +	max = sizeof(syscall_record)/sizeof(syscall_record[0]); +	index = next_syscall_index(max); + +	syscall_record[index].syscall = syscall; +	syscall_record[index].pid = current_pid(); +	syscall_record[index].result = 0xdeadbeef; +	gettimeofday(&syscall_record[index].start, NULL); +	return(index); +} + +void record_syscall_end(int index, long result) +{ +	syscall_record[index].result = result; +	gettimeofday(&syscall_record[index].end, NULL); +} + +/* + * 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/sysrq.c b/arch/um/kernel/sysrq.c new file mode 100644 index 00000000000..e630438f9e7 --- /dev/null +++ b/arch/um/kernel/sysrq.c @@ -0,0 +1,81 @@ +/*  + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/kernel.h" +#include "linux/module.h" +#include "linux/kallsyms.h" +#include "asm/page.h" +#include "asm/processor.h" +#include "sysrq.h" +#include "user_util.h" + +void show_trace(unsigned long * stack) +{ +	/* XXX: Copy the CONFIG_FRAME_POINTER stack-walking backtrace from +	 * arch/i386/kernel/traps.c, and then move this to sys-i386/sysrq.c.*/ +        unsigned long addr; + +        if (!stack) { +                stack = (unsigned long*) &stack; +		WARN_ON(1); +	} + +        printk("Call Trace: \n"); +        while (((long) stack & (THREAD_SIZE-1)) != 0) { +                addr = *stack; +		if (__kernel_text_address(addr)) { +			printk("%08lx:  [<%08lx>]", (unsigned long) stack, addr); +			print_symbol(" %s", addr); +			printk("\n"); +                } +                stack++; +        } +        printk("\n"); +} + +/* + * stack dumps generator - this is used by arch-independent code. + * And this is identical to i386 currently. + */ +void dump_stack(void) +{ +	unsigned long stack; + +	show_trace(&stack); +} +EXPORT_SYMBOL(dump_stack); + +/*Stolen from arch/i386/kernel/traps.c */ +static int kstack_depth_to_print = 24; + +/* This recently started being used in arch-independent code too, as in + * kernel/sched.c.*/ +void show_stack(struct task_struct *task, unsigned long *esp) +{ +	unsigned long *stack; +	int i; + +	if (esp == NULL) { +		if (task != current) { +			esp = (unsigned long *) KSTK_ESP(task); +			/* Which one? No actual difference - just coding style.*/ +			//esp = (unsigned long *) PT_REGS_IP(&task->thread.regs); +		} else { +			esp = (unsigned long *) &esp; +		} +	} + +	stack = esp; +	for(i = 0; i < kstack_depth_to_print; i++) { +		if (kstack_end(stack)) +			break; +		if (i && ((i % 8) == 0)) +			printk("\n       "); +		printk("%08lx ", *stack++); +	} + +	show_trace(esp); +} diff --git a/arch/um/kernel/tempfile.c b/arch/um/kernel/tempfile.c new file mode 100644 index 00000000000..b1674bc1395 --- /dev/null +++ b/arch/um/kernel/tempfile.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/param.h> +#include "init.h" + +/* Modified from create_mem_file and start_debugger */ +static char *tempdir = NULL; + +static void __init find_tempdir(void) +{ +	char *dirs[] = { "TMP", "TEMP", "TMPDIR", NULL }; +	int i; +	char *dir = NULL; + +	if(tempdir != NULL) return;	/* We've already been called */ +	for(i = 0; dirs[i]; i++){ +		dir = getenv(dirs[i]); +		if((dir != NULL) && (*dir != '\0')) +			break; +	} +	if((dir == NULL) || (*dir == '\0'))  +		dir = "/tmp"; + +	tempdir = malloc(strlen(dir) + 2); +	if(tempdir == NULL){ +		fprintf(stderr, "Failed to malloc tempdir, " +			"errno = %d\n", errno); +		return; +	} +	strcpy(tempdir, dir); +	strcat(tempdir, "/"); +} + +int make_tempfile(const char *template, char **out_tempname, int do_unlink) +{ +	char tempname[MAXPATHLEN]; +	int fd; + +	find_tempdir(); +	if (*template != '/') +		strcpy(tempname, tempdir); +	else +		*tempname = 0; +	strcat(tempname, template); +	fd = mkstemp(tempname); +	if(fd < 0){ +		fprintf(stderr, "open - cannot create %s: %s\n", tempname,  +			strerror(errno)); +		return -1; +	} +	if(do_unlink && (unlink(tempname) < 0)){ +		perror("unlink"); +		return -1; +	} +	if(out_tempname){ +		*out_tempname = strdup(tempname); +		if(*out_tempname == NULL){ +			perror("strdup"); +			return -1; +		} +	} +	return(fd); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only.  This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c new file mode 100644 index 00000000000..c40c86a3f91 --- /dev/null +++ b/arch/um/kernel/time.c @@ -0,0 +1,167 @@ +/*  + * 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 "signal_user.h" +#include "time_user.h" +#include "kern_constants.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; + +extern struct timeval xtime; + +struct timeval local_offset = { 0, 0 }; + +void timer(void) +{ +	gettimeofday(&xtime, NULL); +	timeradd(&xtime, &local_offset, &xtime); +} + +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) +{ +	int usec = 1000000/hz(); +	struct itimerval enable = ((struct itimerval) { { 0, usec }, +							{ 0, usec }}); +	if(setitimer(ITIMER_VIRTUAL, &enable, NULL)) +		printk("enable_timer - setitimer failed, errno = %d\n", +		       errno); +} + +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); +} + +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 int do_posix_clock_monotonic_gettime(struct timespec *tp); + +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; +} + +/* Declared in linux/time.h, which can't be included here */ +extern void clock_was_set(void); + +void do_gettimeofday(struct timeval *tv) +{ +	unsigned long flags; + +	flags = time_lock(); +	gettimeofday(tv, NULL); +	timeradd(tv, &local_offset, tv); +	time_unlock(flags); +	clock_was_set(); +} + +int do_settimeofday(struct timespec *tv) +{ +	struct timeval now; +	unsigned long flags; +	struct timeval tv_in; + +	if ((unsigned long) tv->tv_nsec >= UM_NSEC_PER_SEC) +		return -EINVAL; + +	tv_in.tv_sec = tv->tv_sec; +	tv_in.tv_usec = tv->tv_nsec / 1000; + +	flags = time_lock(); +	gettimeofday(&now, NULL); +	timersub(&tv_in, &now, &local_offset); +	time_unlock(flags); + +	return(0); +} + +void idle_sleep(int secs) +{ +	struct timespec ts; + +	ts.tv_sec = secs; +	ts.tv_nsec = 0; +	nanosleep(&ts, NULL); +} + +/* + * 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/time_kern.c b/arch/um/kernel/time_kern.c new file mode 100644 index 00000000000..2461cd73ca8 --- /dev/null +++ b/arch/um/kernel/time_kern.c @@ -0,0 +1,203 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/module.h" +#include "linux/unistd.h" +#include "linux/stddef.h" +#include "linux/spinlock.h" +#include "linux/time.h" +#include "linux/sched.h" +#include "linux/interrupt.h" +#include "linux/init.h" +#include "linux/delay.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" + +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +int hz(void) +{ +	return(HZ); +} + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ +	return (unsigned long long)jiffies_64 * (1000000000 / HZ); +} + +/* Changed at early boot */ +int timer_irq_inited = 0; + +static int first_tick; +static unsigned long long prev_usecs; +#ifdef CONFIG_UML_REAL_TIME_CLOCK +static long long delta;   		/* Deviation per interval */ +#endif + +#define MILLION 1000000 + +void timer_irq(union uml_pt_regs *regs) +{ +	unsigned long long ticks = 0; + +	if(!timer_irq_inited){ +		/* This is to ensure that ticks don't pile up when +		 * the timer handler is suspended */ +		first_tick = 0; +		return; +	} + +	if(first_tick){ +#ifdef CONFIG_UML_REAL_TIME_CLOCK +		/* We've had 1 tick */ +		unsigned long long usecs = os_usecs(); + +		delta += usecs - prev_usecs; +		prev_usecs = usecs; + +		/* Protect against the host clock being set backwards */ +		if(delta < 0) +			delta = 0; + +		ticks += (delta * HZ) / MILLION; +		delta -= (ticks * MILLION) / HZ; +#else +		ticks = 1; +#endif +	} +	else { +		prev_usecs = os_usecs(); +		first_tick = 1; +	} + +	while(ticks > 0){ +		do_IRQ(TIMER_IRQ, regs); +		ticks--; +	} +} + +void boot_timer_handler(int sig) +{ +	struct pt_regs regs; + +	CHOOSE_MODE((void)  +		    (UPT_SC(®s.regs) = (struct sigcontext *) (&sig + 1)), +		    (void) (regs.regs.skas.is_user = 0)); +	do_timer(®s); +} + +irqreturn_t um_timer(int irq, void *dev, struct pt_regs *regs) +{ +	unsigned long flags; + +	do_timer(regs); +	write_seqlock_irqsave(&xtime_lock, flags); +	timer(); +	write_sequnlock_irqrestore(&xtime_lock, flags); +	return(IRQ_HANDLED); +} + +long um_time(int __user *tloc) +{ +	struct timeval now; + +	do_gettimeofday(&now); +	if (tloc) { + 		if (put_user(now.tv_sec, tloc)) +			now.tv_sec = -EFAULT; +	} +	return now.tv_sec; +} + +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); +	return 0; +} + +void __udelay(unsigned long usecs) +{ +	int i, n; + +	n = (loops_per_jiffy * HZ * usecs) / MILLION; +	for(i=0;i<n;i++) ; +} + +void __const_udelay(unsigned long usecs) +{ +	int i, n; + +	n = (loops_per_jiffy * HZ * usecs) / MILLION; +	for(i=0;i<n;i++) ; +} + +void timer_handler(int sig, union uml_pt_regs *regs) +{ +	local_irq_disable(); +	update_process_times(CHOOSE_MODE(user_context(UPT_SP(regs)), (regs)->skas.is_user)); +	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; + +	CHOOSE_MODE(user_time_init_tt(), user_time_init_skas()); +	err = request_irq(TIMER_IRQ, um_timer, SA_INTERRUPT, "timer", NULL); +	if(err != 0) +		printk(KERN_ERR "timer_init : request_irq failed - " +		       "errno = %d\n", -err); +	timer_irq_inited = 1; +	return(0); +} + +__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/tlb.c b/arch/um/kernel/tlb.c new file mode 100644 index 00000000000..eda477edfdf --- /dev/null +++ b/arch/um/kernel/tlb.c @@ -0,0 +1,369 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "asm/page.h" +#include "asm/pgalloc.h" +#include "asm/tlbflush.h" +#include "choose-mode.h" +#include "mode_kern.h" +#include "user_util.h" +#include "tlb.h" +#include "mem.h" +#include "mem_user.h" +#include "os.h" + +#define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1)) + +void fix_range_common(struct mm_struct *mm, unsigned long start_addr, +                      unsigned long end_addr, int force, int data, +                      void (*do_ops)(int, struct host_vm_op *, int)) +{ +        pgd_t *npgd; +        pud_t *npud; +        pmd_t *npmd; +        pte_t *npte; +        unsigned long addr, end; +        int r, w, x; +        struct host_vm_op ops[16]; +        int op_index = -1, last_op = sizeof(ops) / sizeof(ops[0]) - 1; + +        if(mm == NULL) return; + +        for(addr = start_addr; addr < end_addr;){ +                npgd = pgd_offset(mm, addr); +                if(!pgd_present(*npgd)){ +                        end = ADD_ROUND(addr, PGDIR_SIZE); +                        if(end > end_addr) +                                end = end_addr; +                        if(force || pgd_newpage(*npgd)){ +                                op_index = add_munmap(addr, end - addr, ops, +                                                      op_index, last_op, data, +                                                      do_ops); +                                pgd_mkuptodate(*npgd); +                        } +                        addr = end; +                        continue; +                } + +                npud = pud_offset(npgd, addr); +                if(!pud_present(*npud)){ +                        end = ADD_ROUND(addr, PUD_SIZE); +                        if(end > end_addr) +                                end = end_addr; +                        if(force || pud_newpage(*npud)){ +                                op_index = add_munmap(addr, end - addr, ops, +                                                      op_index, last_op, data, +                                                      do_ops); +                                pud_mkuptodate(*npud); +                        } +                        addr = end; +                        continue; +                } + +                npmd = pmd_offset(npud, addr); +                if(!pmd_present(*npmd)){ +                        end = ADD_ROUND(addr, PMD_SIZE); +                        if(end > end_addr) +                                end = end_addr; +                        if(force || pmd_newpage(*npmd)){ +                                op_index = add_munmap(addr, end - addr, ops, +                                                      op_index, last_op, data, +                                                      do_ops); +                                pmd_mkuptodate(*npmd); +                        } +                        addr = end; +                        continue; +                } + +                npte = pte_offset_kernel(npmd, addr); +                r = pte_read(*npte); +                w = pte_write(*npte); +                x = pte_exec(*npte); +                if(!pte_dirty(*npte)) +                        w = 0; +                if(!pte_young(*npte)){ +                        r = 0; +                        w = 0; +                } +                if(force || pte_newpage(*npte)){ +                        if(pte_present(*npte)) +                                op_index = add_mmap(addr, +                                                    pte_val(*npte) & PAGE_MASK, +                                                    PAGE_SIZE, r, w, x, ops, +                                                    op_index, last_op, data, +                                                    do_ops); +                        else op_index = add_munmap(addr, PAGE_SIZE, ops, +                                                   op_index, last_op, data, +                                                   do_ops); +                } +                else if(pte_newprot(*npte)) +                        op_index = add_mprotect(addr, PAGE_SIZE, r, w, x, ops, +                                                op_index, last_op, data, +                                                do_ops); + +                *npte = pte_mkuptodate(*npte); +                addr += PAGE_SIZE; +        } +        (*do_ops)(data, ops, op_index); +} + +int flush_tlb_kernel_range_common(unsigned long start, unsigned long end) +{ +        struct mm_struct *mm; +        pgd_t *pgd; +        pud_t *pud; +        pmd_t *pmd; +        pte_t *pte; +        unsigned long addr, last; +        int updated = 0, err; + +        mm = &init_mm; +        for(addr = start; addr < end;){ +                pgd = pgd_offset(mm, addr); +                if(!pgd_present(*pgd)){ +                        last = ADD_ROUND(addr, PGDIR_SIZE); +                        if(last > end) +                                last = end; +                        if(pgd_newpage(*pgd)){ +                                updated = 1; +                                err = os_unmap_memory((void *) addr, +                                                      last - addr); +                                if(err < 0) +                                        panic("munmap failed, errno = %d\n", +                                              -err); +                        } +                        addr = last; +                        continue; +                } + +                pud = pud_offset(pgd, addr); +                if(!pud_present(*pud)){ +                        last = ADD_ROUND(addr, PUD_SIZE); +                        if(last > end) +                                last = end; +                        if(pud_newpage(*pud)){ +                                updated = 1; +                                err = os_unmap_memory((void *) addr, +                                                      last - addr); +                                if(err < 0) +                                        panic("munmap failed, errno = %d\n", +                                              -err); +                        } +                        addr = last; +                        continue; +                } + +                pmd = pmd_offset(pud, addr); +                if(!pmd_present(*pmd)){ +                        last = ADD_ROUND(addr, PMD_SIZE); +                        if(last > end) +                                last = end; +                        if(pmd_newpage(*pmd)){ +                                updated = 1; +                                err = os_unmap_memory((void *) addr, +                                                      last - addr); +                                if(err < 0) +                                        panic("munmap failed, errno = %d\n", +                                              -err); +                        } +                        addr = last; +                        continue; +                } + +                pte = pte_offset_kernel(pmd, addr); +                if(!pte_present(*pte) || pte_newpage(*pte)){ +                        updated = 1; +                        err = os_unmap_memory((void *) addr, +                                              PAGE_SIZE); +                        if(err < 0) +                                panic("munmap failed, errno = %d\n", +                                      -err); +                        if(pte_present(*pte)) +                                map_memory(addr, +                                           pte_val(*pte) & PAGE_MASK, +                                           PAGE_SIZE, 1, 1, 1); +                } +                else if(pte_newprot(*pte)){ +                        updated = 1; +                        protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1); +                } +                addr += PAGE_SIZE; +        } +        return(updated); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) +{ +        address &= PAGE_MASK; +        flush_tlb_range(vma, address, address + PAGE_SIZE); +} + +void flush_tlb_all(void) +{ +        flush_tlb_mm(current->mm); +} +   +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ +        CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt, +                         flush_tlb_kernel_range_common, start, end); +} + +void flush_tlb_kernel_vm(void) +{ +        CHOOSE_MODE(flush_tlb_kernel_vm_tt(), +                    flush_tlb_kernel_range_common(start_vm, end_vm)); +} + +void __flush_tlb_one(unsigned long addr) +{ +        CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr); +} + +void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,  +     unsigned long end) +{ +        CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start, +                         end); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ +        CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm); +} + +void force_flush_all(void) +{ +        CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas()); +} + +pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) +{ +        return(pgd_offset(mm, address)); +} + +pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address) +{ +        return(pud_offset(pgd, address)); +} + +pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address) +{ +        return(pmd_offset(pud, address)); +} + +pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) +{ +        return(pte_offset_kernel(pmd, address)); +} + +pte_t *addr_pte(struct task_struct *task, unsigned long addr) +{ +        pgd_t *pgd = pgd_offset(task->mm, addr); +        pud_t *pud = pud_offset(pgd, addr); +        pmd_t *pmd = pmd_offset(pud, addr); + +        return(pte_offset_map(pmd, addr)); +} + +int add_mmap(unsigned long virt, unsigned long phys, unsigned long len, +     int r, int w, int x, struct host_vm_op *ops, int index, +     int last_filled, int data, +     void (*do_ops)(int, struct host_vm_op *, int)) +{ +        __u64 offset; +	struct host_vm_op *last; +	int fd; + +	fd = phys_mapping(phys, &offset); +	if(index != -1){ +		last = &ops[index]; +		if((last->type == MMAP) && +		   (last->u.mmap.addr + last->u.mmap.len == virt) && +		   (last->u.mmap.r == r) && (last->u.mmap.w == w) && +		   (last->u.mmap.x == x) && (last->u.mmap.fd == fd) && +		   (last->u.mmap.offset + last->u.mmap.len == offset)){ +			last->u.mmap.len += len; +			return(index); +		} +	} + +	if(index == last_filled){ +		(*do_ops)(data, ops, last_filled); +		index = -1; +	} + +	ops[++index] = ((struct host_vm_op) { .type	= MMAP, +					      .u = { .mmap = { +						      .addr	= virt, +						      .len	= len, +						      .r	= r, +						      .w	= w, +						      .x	= x, +						      .fd	= fd, +						      .offset	= offset } +					      } }); +	return(index); +} + +int add_munmap(unsigned long addr, unsigned long len, struct host_vm_op *ops, +	       int index, int last_filled, int data, +	       void (*do_ops)(int, struct host_vm_op *, int)) +{ +	struct host_vm_op *last; + +	if(index != -1){ +		last = &ops[index]; +		if((last->type == MUNMAP) && +		   (last->u.munmap.addr + last->u.mmap.len == addr)){ +			last->u.munmap.len += len; +			return(index); +		} +	} + +	if(index == last_filled){ +		(*do_ops)(data, ops, last_filled); +		index = -1; +	} + +	ops[++index] = ((struct host_vm_op) { .type	= MUNMAP, +					      .u = { .munmap = { +						      .addr	= addr, +						      .len	= len } } }); +	return(index); +} + +int add_mprotect(unsigned long addr, unsigned long len, int r, int w, int x, +		 struct host_vm_op *ops, int index, int last_filled, int data, +		 void (*do_ops)(int, struct host_vm_op *, int)) +{ +	struct host_vm_op *last; + +	if(index != -1){ +		last = &ops[index]; +		if((last->type == MPROTECT) && +		   (last->u.mprotect.addr + last->u.mprotect.len == addr) && +		   (last->u.mprotect.r == r) && (last->u.mprotect.w == w) && +		   (last->u.mprotect.x == x)){ +			last->u.mprotect.len += len; +			return(index); +		} +	} + +	if(index == last_filled){ +		(*do_ops)(data, ops, last_filled); +		index = -1; +	} + +	ops[++index] = ((struct host_vm_op) { .type	= MPROTECT, +					      .u = { .mprotect = { +						      .addr	= addr, +						      .len	= len, +						      .r	= r, +						      .w	= w, +						      .x	= x } } }); +	return(index); +} diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c new file mode 100644 index 00000000000..47e766e6ba1 --- /dev/null +++ b/arch/um/kernel/trap_kern.c @@ -0,0 +1,251 @@ +/*  + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "asm/errno.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "linux/spinlock.h" +#include "linux/config.h" +#include "linux/init.h" +#include "linux/ptrace.h" +#include "asm/semaphore.h" +#include "asm/pgtable.h" +#include "asm/pgalloc.h" +#include "asm/tlbflush.h" +#include "asm/a.out.h" +#include "asm/current.h" +#include "asm/irq.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "chan_kern.h" +#include "mconsole_kern.h" +#include "2_5compat.h" +#include "mem.h" +#include "mem_kern.h" + +int handle_page_fault(unsigned long address, unsigned long ip,  +		      int is_write, int is_user, int *code_out) +{ +	struct mm_struct *mm = current->mm; +	struct vm_area_struct *vma; +	pgd_t *pgd; +	pud_t *pud; +	pmd_t *pmd; +	pte_t *pte; +	unsigned long page; +	int err = -EFAULT; + +	*code_out = SEGV_MAPERR; +	down_read(&mm->mmap_sem); +	vma = find_vma(mm, address); +	if(!vma)  +		goto out; +	else if(vma->vm_start <= address)  +		goto good_area; +	else if(!(vma->vm_flags & VM_GROWSDOWN))  +		goto out; +	else if(!ARCH_IS_STACKGROW(address)) +		goto out; +	else if(expand_stack(vma, address))  +		goto out; + + good_area: +	*code_out = SEGV_ACCERR; +	if(is_write && !(vma->vm_flags & VM_WRITE))  +		goto out; +	page = address & PAGE_MASK; +	pgd = pgd_offset(mm, page); +	pud = pud_offset(pgd, page); +	pmd = pmd_offset(pud, page); +	do { + survive: +		switch (handle_mm_fault(mm, vma, address, is_write)){ +		case VM_FAULT_MINOR: +			current->min_flt++; +			break; +		case VM_FAULT_MAJOR: +			current->maj_flt++; +			break; +		case VM_FAULT_SIGBUS: +			err = -EACCES; +			goto out; +		case VM_FAULT_OOM: +			err = -ENOMEM; +			goto out_of_memory; +		default: +			BUG(); +		} +		pgd = pgd_offset(mm, page); +		pud = pud_offset(pgd, page); +		pmd = pmd_offset(pud, page); +		pte = pte_offset_kernel(pmd, page); +	} while(!pte_present(*pte)); +	err = 0; +	*pte = pte_mkyoung(*pte); +	if(pte_write(*pte)) *pte = pte_mkdirty(*pte); +	flush_tlb_page(vma, page); + out: +	up_read(&mm->mmap_sem); +	return(err); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: +	if (current->pid == 1) { +		up_read(&mm->mmap_sem); +		yield(); +		down_read(&mm->mmap_sem); +		goto survive; +	} +	goto out; +} + +LIST_HEAD(physmem_remappers); + +void register_remapper(struct remapper *info) +{ +	list_add(&info->list, &physmem_remappers); +} + +static int check_remapped_addr(unsigned long address, int is_write) +{ +	struct remapper *remapper; +	struct list_head *ele; +	__u64 offset; +	int fd; + +	fd = phys_mapping(__pa(address), &offset); +	if(fd == -1) +		return(0); + +	list_for_each(ele, &physmem_remappers){ +		remapper = list_entry(ele, struct remapper, list); +		if((*remapper->proc)(fd, address, is_write, offset)) +			return(1); +	} + +	return(0); +} + +unsigned long segv(unsigned long address, unsigned long ip, int is_write,  +		   int is_user, void *sc) +{ +	struct siginfo si; +	void *catcher; +	int err; + +        if(!is_user && (address >= start_vm) && (address < end_vm)){ +                flush_tlb_kernel_vm(); +                return(0); +        } +	else if(check_remapped_addr(address & PAGE_MASK, is_write)) +		return(0); +	else if(current->mm == NULL) +		panic("Segfault with no mm"); +	err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); + +	catcher = current->thread.fault_catcher; +	if(!err) +		return(0); +	else if(catcher != NULL){ +		current->thread.fault_addr = (void *) address; +		do_longjmp(catcher, 1); +	}  +	else if(current->thread.fault_addr != NULL) +		panic("fault_addr set but no fault catcher"); +	else if(arch_fixup(ip, sc)) +		return(0); + + 	if(!is_user)  +		panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",  +		      address, ip); + +	if(err == -EACCES){ +		si.si_signo = SIGBUS; +		si.si_errno = 0; +		si.si_code = BUS_ADRERR; +		si.si_addr = (void *)address; +		force_sig_info(SIGBUS, &si, current); +	} +	else if(err == -ENOMEM){ +		printk("VM: killing process %s\n", current->comm); +		do_exit(SIGKILL); +	} +	else { +		si.si_signo = SIGSEGV; +		si.si_addr = (void *) address; +		current->thread.cr2 = address; +		current->thread.err = is_write; +		force_sig_info(SIGSEGV, &si, current); +	} +	return(0); +} + +void bad_segv(unsigned long address, unsigned long ip, int is_write) +{ +	struct siginfo si; + +	si.si_signo = SIGSEGV; +	si.si_code = SEGV_ACCERR; +	si.si_addr = (void *) address; +	current->thread.cr2 = address; +	current->thread.err = is_write; +	force_sig_info(SIGSEGV, &si, current); +} + +void relay_signal(int sig, union uml_pt_regs *regs) +{ +	if(arch_handle_signal(sig, regs)) return; +	if(!UPT_IS_USER(regs)) +		panic("Kernel mode signal %d", sig); +	force_sig(sig, current); +} + +void bus_handler(int sig, union uml_pt_regs *regs) +{ +	if(current->thread.fault_catcher != NULL) +		do_longjmp(current->thread.fault_catcher, 1); +	else relay_signal(sig, regs); +} + +void winch(int sig, union uml_pt_regs *regs) +{ +	do_IRQ(WINCH_IRQ, regs); +} + +void trap_init(void) +{ +} + +DEFINE_SPINLOCK(trap_lock); + +static int trap_index = 0; + +int next_trap_index(int limit) +{ +	int ret; + +	spin_lock(&trap_lock); +	ret = trap_index; +	if(++trap_index == limit) +		trap_index = 0; +	spin_unlock(&trap_lock); +	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/trap_user.c b/arch/um/kernel/trap_user.c new file mode 100644 index 00000000000..50a4042a509 --- /dev/null +++ b/arch/um/kernel/trap_user.c @@ -0,0 +1,120 @@ +/*  + * 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); +} + +/* Unlocked - don't care if this is a bit off */ +int nsegfaults = 0; + +struct { +	unsigned long address; +	int is_write; +	int pid; +	unsigned long sp; +	int is_user; +} segfault_record[1024]; + +void segv_handler(int sig, union uml_pt_regs *regs) +{ +	int index, max; + +	if(UPT_IS_USER(regs) && !UPT_SEGV_IS_FIXABLE(regs)){ +		bad_segv(UPT_FAULT_ADDR(regs), UPT_IP(regs),  +			 UPT_FAULT_WRITE(regs)); +		return; +	} +	max = sizeof(segfault_record)/sizeof(segfault_record[0]); +	index = next_trap_index(max); + +	nsegfaults++; +	segfault_record[index].address = UPT_FAULT_ADDR(regs); +	segfault_record[index].pid = os_getpid(); +	segfault_record[index].is_write = UPT_FAULT_WRITE(regs); +	segfault_record[index].sp = UPT_SP(regs); +	segfault_record[index].is_user = UPT_IS_USER(regs); +	segv(UPT_FAULT_ADDR(regs), UPT_IP(regs), UPT_FAULT_WRITE(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/Makefile b/arch/um/kernel/tt/Makefile new file mode 100644 index 00000000000..3d5177df350 --- /dev/null +++ b/arch/um/kernel/tt/Makefile @@ -0,0 +1,28 @@ +#  +# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) +# Licensed under the GPL +# + +extra-y := unmap_fin.o +clean-files := unmap_tmp.o + +obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \ +	syscall_kern.o syscall_user.o time.o tlb.o tracer.o trap_user.o \ +	uaccess.o uaccess_user.o + +obj-$(CONFIG_PT_PROXY) += gdb_kern.o ptproxy/ + +USER_OBJS := gdb.o time.o tracer.o + +include arch/um/scripts/Makefile.rules + +UNMAP_CFLAGS := $(patsubst -pg -DPROFILING,,$(USER_CFLAGS)) +UNMAP_CFLAGS := $(patsubst -fprofile-arcs -ftest-coverage,,$(UNMAP_CFLAGS)) + +#XXX: partially copied from arch/um/scripts/Makefile.rules +$(obj)/unmap.o: c_flags = -Wp,-MD,$(depfile) $(UNMAP_CFLAGS) + +$(obj)/unmap_fin.o : $(obj)/unmap.o +	$(LD) -r -o $(obj)/unmap_tmp.o $< $(shell $(CC) -print-file-name=libc.a) +	$(OBJCOPY) $(obj)/unmap_tmp.o $@ -G switcheroo + diff --git a/arch/um/kernel/tt/exec_kern.c b/arch/um/kernel/tt/exec_kern.c new file mode 100644 index 00000000000..065b504a653 --- /dev/null +++ b/arch/um/kernel/tt/exec_kern.c @@ -0,0 +1,87 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/mm.h" +#include "asm/signal.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "asm/pgalloc.h" +#include "asm/tlbflush.h" +#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" +#include "mode.h" + +static int exec_tramp(void *sig_stack) +{ +	init_new_thread_stack(sig_stack, NULL); +	init_new_thread_signals(1); +	os_stop_process(os_getpid()); +	return(0); +} + +void flush_thread_tt(void) +{ +	unsigned long stack; +	int new_pid; + +	stack = alloc_stack(0, 0); +	if(stack == 0){ +		printk(KERN_ERR  +		       "flush_thread : failed to allocate temporary stack\n"); +		do_exit(SIGKILL); +	} +		 +	new_pid = start_fork_tramp(current->thread_info, stack, 0, exec_tramp); +	if(new_pid < 0){ +		printk(KERN_ERR  +		       "flush_thread : new thread failed, errno = %d\n", +		       -new_pid); +		do_exit(SIGKILL); +	} + +	if(current_thread->cpu == 0) +		forward_interrupts(new_pid); +	current->thread.request.op = OP_EXEC; +	current->thread.request.u.exec.pid = new_pid; +	unprotect_stack((unsigned long) current_thread); +	os_usr1_process(os_getpid()); +	change_sig(SIGUSR1, 1); + +	change_sig(SIGUSR1, 0); +	enable_timer(); +	free_page(stack); +	protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); +	task_protections((unsigned long) current_thread); +	force_flush_all(); +	unblock_signals(); +} + +void start_thread_tt(struct pt_regs *regs, unsigned long eip,  +		     unsigned long esp) +{ +	set_fs(USER_DS); +	flush_tlb_mm(current->mm); +	PT_REGS_IP(regs) = eip; +	PT_REGS_SP(regs) = esp; +	PT_FIX_EXEC_STACK(esp); +} + +/* + * 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_user.c b/arch/um/kernel/tt/exec_user.c new file mode 100644 index 00000000000..a92c02ff2ce --- /dev/null +++ b/arch/um/kernel/tt/exec_user.c @@ -0,0 +1,57 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sched.h> +#include <errno.h> +#include <sys/wait.h> +#include <signal.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "ptrace_user.h" +#include "os.h" + +void do_exec(int old_pid, int new_pid) +{ +	unsigned long regs[FRAME_SIZE]; +	int err; + +	if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || +	   (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) +		tracer_panic("do_exec failed to attach proc - errno = %d", +			     errno); + +	CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED)); +	if (err < 0) +		tracer_panic("do_exec failed to attach proc in waitpid - errno = %d", +			     errno); + +	if(ptrace_getregs(old_pid, regs) < 0) +		tracer_panic("do_exec failed to get registers - errno = %d", +			     errno); + +	os_kill_ptraced_process(old_pid, 0); + +	if (ptrace(PTRACE_OLDSETOPTIONS, new_pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) +		tracer_panic("do_exec: PTRACE_SETOPTIONS failed, errno = %d", errno); + +	if(ptrace_setregs(new_pid, regs) < 0) +		tracer_panic("do_exec failed to start new proc - errno = %d", +			     errno); +} + +/* + * 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/gdb.c b/arch/um/kernel/tt/gdb.c new file mode 100644 index 00000000000..19a0ad7b35b --- /dev/null +++ b/arch/um/kernel/tt/gdb.c @@ -0,0 +1,278 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <sys/types.h> +#include "ptrace_user.h" +#include "uml-config.h" +#include "kern_constants.h" +#include "chan_user.h" +#include "init.h" +#include "user.h" +#include "debug.h" +#include "kern_util.h" +#include "user_util.h" +#include "tt.h" +#include "sysdep/thread.h" + +extern int debugger_pid; +extern int debugger_fd; +extern int debugger_parent; + +int detach(int pid, int sig) +{ +	return(ptrace(PTRACE_DETACH, pid, 0, sig)); +} + +int attach(int pid) +{ +	int err; + +	err = ptrace(PTRACE_ATTACH, pid, 0, 0); +	if(err < 0) return(-errno); +	else return(err); +} + +int cont(int pid) +{ +	return(ptrace(PTRACE_CONT, pid, 0, 0)); +} + +#ifdef UML_CONFIG_PT_PROXY + +int debugger_signal(int status, pid_t pid) +{ +	return(debugger_proxy(status, pid)); +} + +void child_signal(pid_t pid, int status) +{ +	child_proxy(pid, status); +} + +static void gdb_announce(char *dev_name, int dev) +{ +	printf("gdb assigned device '%s'\n", dev_name); +} + +static struct chan_opts opts = { +	.announce  	= gdb_announce, +	.xterm_title 	= "UML kernel debugger", +	.raw 		= 0, +	.tramp_stack 	= 0, +	.in_kernel  	= 0, +}; + +/* Accessed by the tracing thread, which automatically serializes access */ +static void *xterm_data; +static int xterm_fd; + +extern void *xterm_init(char *, int, struct chan_opts *); +extern int xterm_open(int, int, int, void *, char **); +extern void xterm_close(int, void *); + +int open_gdb_chan(void) +{ +	char stack[UM_KERN_PAGE_SIZE], *dummy; + +	opts.tramp_stack = (unsigned long) stack; +	xterm_data = xterm_init("", 0, &opts); +	xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy); +	return(xterm_fd); +} + +static void exit_debugger_cb(void *unused) +{ +	if(debugger_pid != -1){ +		if(gdb_pid != -1){ +			fake_child_exit(); +			gdb_pid = -1; +		} +		else kill_child_dead(debugger_pid); +		debugger_pid = -1; +		if(debugger_parent != -1) +			detach(debugger_parent, SIGINT); +	} +	if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data); +} + +static void exit_debugger(void) +{ +	initial_thread_cb(exit_debugger_cb, NULL); +} + +__uml_exitcall(exit_debugger); + +struct gdb_data { +	char *str; +	int err; +}; + +static void config_gdb_cb(void *arg) +{ +	struct gdb_data *data = arg; +	void *task; +	int pid; + +	data->err = -1; +	if(debugger_pid != -1) exit_debugger_cb(NULL); +	if(!strncmp(data->str, "pid,", strlen("pid,"))){ +		data->str += strlen("pid,"); +		pid = strtoul(data->str, NULL, 0); +		task = cpu_tasks[0].task; +		debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0); +		if(debugger_pid != -1){ +			data->err = 0; +			gdb_pid = pid; +		} +		return; +	} +	data->err = 0; +	debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); +	init_proxy(debugger_pid, 0, 0); +} + +int gdb_config(char *str) +{ +	struct gdb_data data; + +	if(*str++ != '=') return(-1); +	data.str = str; +	initial_thread_cb(config_gdb_cb, &data); +	return(data.err); +} + +void remove_gdb_cb(void *unused) +{ +	exit_debugger_cb(NULL); +} + +int gdb_remove(char *unused) +{ +	initial_thread_cb(remove_gdb_cb, NULL); +	return(0); +} + +void signal_usr1(int sig) +{ +	if(debugger_pid != -1){ +		printf("The debugger is already running\n"); +		return; +	} +	debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); +	init_proxy(debugger_pid, 0, 0); +} + +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ +	int pid, status; + +	pid = start_debugger(linux_prog, startup, stop, &debugger_fd); +	status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL); + 	if(pid < 0){ +		cont(idle_pid); +		return(-1); +	} +	init_proxy(pid, 1, status); +	return(pid); +} + +int attach_debugger(int idle_pid, int pid, int stop) +{ +	int status = 0, err; + +	err = attach(pid); +	if(err < 0){ +		printf("Failed to attach pid %d, errno = %d\n", pid, -err); +		return(-1); +	} +	if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL); +	init_proxy(pid, 1, status); +	return(pid); +} + +#ifdef notdef /* Put this back in when it does something useful */ +static int __init uml_gdb_init_setup(char *line, int *add) +{ +	gdb_init = uml_strdup(line); +	return 0; +} + +__uml_setup("gdb=", uml_gdb_init_setup,  +"gdb=<channel description>\n\n" +); +#endif + +static int __init uml_gdb_pid_setup(char *line, int *add) +{ +	gdb_pid = strtoul(line, NULL, 0); +	*add = 0; +	return 0; +} + +__uml_setup("gdb-pid=", uml_gdb_pid_setup,  +"gdb-pid=<pid>\n" +"    gdb-pid is used to attach an external debugger to UML.  This may be\n" +"    an already-running gdb or a debugger-like process like strace.\n\n" +); + +#else + +int debugger_signal(int status, pid_t pid){ return(0); } +void child_signal(pid_t pid, int status){ } +int init_ptrace_proxy(int idle_pid, int startup, int stop) +{ +	printf("debug requested when CONFIG_PT_PROXY is off\n"); +	kill_child_dead(idle_pid); +	exit(1); +} + +void signal_usr1(int sig) +{ +	printf("debug requested when CONFIG_PT_PROXY is off\n"); +} + +int attach_debugger(int idle_pid, int pid, int stop) +{ +	printf("attach_debugger called when CONFIG_PT_PROXY " +	       "is off\n"); +	return(-1); +} + +int config_gdb(char *str) +{ +	return(-1); +} + +int remove_gdb(void) +{ +	return(-1); +} + +int init_parent_proxy(int pid) +{ +	return(-1); +} + +void debugger_parent_signal(int status, int pid) +{ +} + +#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/gdb_kern.c b/arch/um/kernel/tt/gdb_kern.c new file mode 100644 index 00000000000..93fb121f86a --- /dev/null +++ b/arch/um/kernel/tt/gdb_kern.c @@ -0,0 +1,40 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/config.h" +#include "mconsole_kern.h" + +#ifdef CONFIG_MCONSOLE + +extern int gdb_config(char *str); +extern int gdb_remove(char *unused); + +static struct mc_device gdb_mc = { +	.name		= "gdb", +	.config		= gdb_config, +	.remove		= gdb_remove, +}; + +int gdb_mc_init(void) +{ +	mconsole_register_dev(&gdb_mc); +	return(0); +} + +__initcall(gdb_mc_init); + +#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/debug.h b/arch/um/kernel/tt/include/debug.h new file mode 100644 index 00000000000..8eff674107c --- /dev/null +++ b/arch/um/kernel/tt/include/debug.h @@ -0,0 +1,29 @@ +/*  + * Copyright (C) 2000, 2001, 2002  Jeff Dike (jdike@karaya.com) and + * Lars Brinkhoff. + * Licensed under the GPL + */ + +#ifndef __DEBUG_H +#define __DEBUG_H + +extern int debugger_proxy(int status, pid_t pid); +extern void child_proxy(pid_t pid, int status); +extern void init_proxy (pid_t pid, int waiting, int status); +extern int start_debugger(char *prog, int startup, int stop, int *debugger_fd); +extern void fake_child_exit(void); +extern int gdb_config(char *str); +extern int gdb_remove(char *unused); + +#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/mmu-tt.h b/arch/um/kernel/tt/include/mmu-tt.h new file mode 100644 index 00000000000..0440510ab3f --- /dev/null +++ b/arch/um/kernel/tt/include/mmu-tt.h @@ -0,0 +1,23 @@ +/* + * 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/include/mode-tt.h b/arch/um/kernel/tt/include/mode-tt.h new file mode 100644 index 00000000000..efe46201906 --- /dev/null +++ b/arch/um/kernel/tt/include/mode-tt.h @@ -0,0 +1,35 @@ +/* + * 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 user_time_init_tt(void); +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 + +/* + * 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/mode_kern-tt.h b/arch/um/kernel/tt/include/mode_kern-tt.h new file mode 100644 index 00000000000..28aaab3448f --- /dev/null +++ b/arch/um/kernel/tt/include/mode_kern-tt.h @@ -0,0 +1,53 @@ +/* + * 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 exit_thread_tt(void); +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 + +/* + * 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/tt.h b/arch/um/kernel/tt/include/tt.h new file mode 100644 index 00000000000..c667b67af40 --- /dev/null +++ b/arch/um/kernel/tt/include/tt.h @@ -0,0 +1,46 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TT_H__ +#define __TT_H__ + +#include "sysdep/ptrace.h" + +extern int gdb_pid; +extern int debug; +extern int debug_stop; +extern int debug_trace; + +extern int honeypot; + +extern int fork_tramp(void *sig_stack); +extern int do_proc_op(void *t, int proc_id); +extern int tracer(int (*init_proc)(void *), void *sp); +extern void attach_process(int pid); +extern void tracer_panic(char *format, ...); +extern void set_init_pid(int pid); +extern int set_user_mode(void *task); +extern void set_tracing(void *t, int tracing); +extern int is_tracing(void *task); +extern void syscall_handler(int sig, union uml_pt_regs *regs); +extern void exit_kernel(int pid, void *task); +extern void do_syscall(void *task, int pid, int local_using_sysemu); +extern void do_sigtrap(void *task); +extern int is_valid_pid(int pid); +extern void remap_data(void *segment_start, void *segment_end, int w); +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/kernel/tt/include/uaccess-tt.h new file mode 100644 index 00000000000..f0bad010ceb --- /dev/null +++ b/arch/um/kernel/tt/include/uaccess-tt.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __TT_UACCESS_H +#define __TT_UACCESS_H + +#include "linux/string.h" +#include "linux/sched.h" +#include "asm/processor.h" +#include "asm/errno.h" +#include "asm/current.h" +#include "asm/a.out.h" +#include "uml_uaccess.h" + +#define ABOVE_KMEM (16 * 1024 * 1024) + +extern unsigned long end_vm; +extern unsigned long uml_physmem; + +#define under_task_size(addr, size) \ +	(((unsigned long) (addr) < TASK_SIZE) && \ +         (((unsigned long) (addr) + (size)) < TASK_SIZE)) + +#define is_stack(addr, size) \ +	(((unsigned long) (addr) < STACK_TOP) && \ +	 ((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \ +	 (((unsigned long) (addr) + (size)) <= STACK_TOP)) + +#define access_ok_tt(type, addr, size) \ +	((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \ +         (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \ +          (under_task_size(addr, size) || is_stack(addr, size)))) + +static inline int verify_area_tt(int type, const void * addr, +				 unsigned long size) +{ +	return(access_ok_tt(type, addr, size) ? 0 : -EFAULT); +} + +extern unsigned long get_fault_addr(void); + +extern int __do_copy_from_user(void *to, const void *from, int n, +			       void **fault_addr, void **fault_catcher); +extern int __do_strncpy_from_user(char *dst, const char *src, size_t n, +				  void **fault_addr, void **fault_catcher); +extern int __do_clear_user(void *mem, size_t len, void **fault_addr, +			   void **fault_catcher); +extern int __do_strnlen_user(const char *str, unsigned long n, +			     void **fault_addr, void **fault_catcher); + +extern int copy_from_user_tt(void *to, const void *from, int n); +extern int copy_to_user_tt(void *to, const void *from, int n); +extern int strncpy_from_user_tt(char *dst, const char *src, int count); +extern int __clear_user_tt(void *mem, int len); +extern int clear_user_tt(void *mem, int len); +extern int strnlen_user_tt(const void *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/kernel/tt/ksyms.c b/arch/um/kernel/tt/ksyms.c new file mode 100644 index 00000000000..92ec85d67c7 --- /dev/null +++ b/arch/um/kernel/tt/ksyms.c @@ -0,0 +1,28 @@ +/*  + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" +#include "asm/uaccess.h" +#include "mode.h" + +EXPORT_SYMBOL(__do_copy_from_user); +EXPORT_SYMBOL(__do_copy_to_user); +EXPORT_SYMBOL(__do_strncpy_from_user); +EXPORT_SYMBOL(__do_strnlen_user);  +EXPORT_SYMBOL(__do_clear_user); + +EXPORT_SYMBOL(tracing_pid); +EXPORT_SYMBOL(honeypot); + +/* + * 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/mem.c b/arch/um/kernel/tt/mem.c new file mode 100644 index 00000000000..74346a04a2b --- /dev/null +++ b/arch/um/kernel/tt/mem.c @@ -0,0 +1,51 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/config.h" +#include "linux/mm.h" +#include "asm/uaccess.h" +#include "mem_user.h" +#include "kern_util.h" +#include "user_util.h" +#include "kern.h" +#include "tt.h" + +void before_mem_tt(unsigned long brk_start) +{ +	if(debug) +		remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1); +	remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1); +	remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1); +} + +#ifdef CONFIG_HOST_2G_2G +#define TOP 0x80000000 +#else +#define TOP 0xc0000000 +#endif + +#define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000) +#define START (TOP - SIZE) + +unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,  +				unsigned long *task_size_out) +{ +	/* Round up to the nearest 4M */ +	*host_size_out = ROUND_4M((unsigned long) &arg); +	*task_size_out = START; +	return(START); +} + +/* + * 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/mem_user.c b/arch/um/kernel/tt/mem_user.c new file mode 100644 index 00000000000..3085267459b --- /dev/null +++ b/arch/um/kernel/tt/mem_user.c @@ -0,0 +1,49 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/mman.h> +#include "tt.h" +#include "mem_user.h" +#include "user_util.h" + +void remap_data(void *segment_start, void *segment_end, int w) +{ +	void *addr; +	unsigned long size; +	int data, prot; + +	if(w) prot = PROT_WRITE; +	else prot = 0; +	prot |= PROT_READ | PROT_EXEC; +	size = (unsigned long) segment_end -  +		(unsigned long) segment_start; +	data = create_mem_file(size); +	addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0); +	if(addr == MAP_FAILED){ +		perror("mapping new data segment"); +		exit(1); +	} +	memcpy(addr, segment_start, size); +	if(switcheroo(data, prot, addr, segment_start, size) < 0){ +		printf("switcheroo failed\n"); +		exit(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: + */ diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c new file mode 100644 index 00000000000..f19f7c18feb --- /dev/null +++ b/arch/um/kernel/tt/process_kern.c @@ -0,0 +1,476 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/signal.h" +#include "linux/kernel.h" +#include "linux/interrupt.h" +#include "linux/ptrace.h" +#include "asm/system.h" +#include "asm/pgalloc.h" +#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" +#include "init.h" +#include "tt.h" + +void *switch_to_tt(void *prev, void *next, void *last) +{ +	struct task_struct *from, *to, *prev_sched; +	unsigned long flags; +	int err, vtalrm, alrm, prof, cpu; +	char c; +	/* jailing and SMP are incompatible, so this doesn't need to be  +	 * made per-cpu  +	 */ +	static int reading; + +	from = prev; +	to = next; + +	to->thread.prev_sched = from; + +	cpu = from->thread_info->cpu; +	if(cpu == 0) +		forward_interrupts(to->thread.mode.tt.extern_pid); +#ifdef CONFIG_SMP +	forward_ipi(cpu_data[cpu].ipi_pipe[0], to->thread.mode.tt.extern_pid); +#endif +	local_irq_save(flags); + +	vtalrm = change_sig(SIGVTALRM, 0); +	alrm = change_sig(SIGALRM, 0); +	prof = change_sig(SIGPROF, 0); + +	forward_pending_sigio(to->thread.mode.tt.extern_pid); + +	c = 0; +	set_current(to); + +	reading = 0; +	err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c)); +	if(err != sizeof(c)) +		panic("write of switch_pipe failed, err = %d", -err); + +	reading = 1; +	if((from->exit_state == EXIT_ZOMBIE) || +	   (from->exit_state == EXIT_DEAD)) +		os_kill_process(os_getpid(), 0); + +	err = os_read_file(from->thread.mode.tt.switch_pipe[0], &c, sizeof(c)); +	if(err != sizeof(c)) +		panic("read of switch_pipe failed, errno = %d", -err); + +	/* If the process that we have just scheduled away from has exited, +	 * then it needs to be killed here.  The reason is that, even though +	 * it will kill itself when it next runs, that may be too late.  Its +	 * stack will be freed, possibly before then, and if that happens, +	 * we have a use-after-free situation.  So, it gets killed here +	 * in case it has not already killed itself. +	 */ +	prev_sched = current->thread.prev_sched; +	if((prev_sched->exit_state == EXIT_ZOMBIE) || +	   (prev_sched->exit_state == EXIT_DEAD)) +		os_kill_process(prev_sched->thread.mode.tt.extern_pid, 1); + +	change_sig(SIGVTALRM, vtalrm); +	change_sig(SIGALRM, alrm); +	change_sig(SIGPROF, prof); + +	arch_switch(); + +	flush_tlb_all(); +	local_irq_restore(flags); + +	return(current->thread.prev_sched); +} + +void release_thread_tt(struct task_struct *task) +{ +	int pid = task->thread.mode.tt.extern_pid; + +	if(os_getpid() != pid) +		os_kill_process(pid, 0); +} + +void exit_thread_tt(void) +{ +	os_close_file(current->thread.mode.tt.switch_pipe[0]); +	os_close_file(current->thread.mode.tt.switch_pipe[1]); +} + +void suspend_new_thread(int fd) +{ +	int err; +	char c; + +	os_stop_process(os_getpid()); +	err = os_read_file(fd, &c, sizeof(c)); +	if(err != sizeof(c)) +		panic("read failed in suspend_new_thread, err = %d", -err); +} + +void schedule_tail(task_t *prev); + +static void new_thread_handler(int sig) +{ +	unsigned long disable; +	int (*fn)(void *); +	void *arg; + +	fn = current->thread.request.u.thread.proc; +	arg = current->thread.request.u.thread.arg; + +	UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1); +	disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) | +		(1 << (SIGIO - 1)) | (1 << (SIGPROF - 1)); +	SC_SIGMASK(UPT_SC(¤t->thread.regs.regs)) &= ~disable; + +	suspend_new_thread(current->thread.mode.tt.switch_pipe[0]); + +	force_flush_all(); +	if(current->thread.prev_sched != NULL) +		schedule_tail(current->thread.prev_sched); +	current->thread.prev_sched = NULL; + +	init_new_thread_signals(1); +	enable_timer(); +	free_page(current->thread.temp_stack); +	set_cmdline("(kernel thread)"); + +	change_sig(SIGUSR1, 1); +	change_sig(SIGVTALRM, 1); +	change_sig(SIGPROF, 1); +	local_irq_enable(); +	if(!run_kernel_thread(fn, arg, ¤t->thread.exec_buf)) +		do_exit(0); + +	/* XXX No set_user_mode here because a newly execed process will +	 * immediately segfault on its non-existent IP, coming straight back +	 * to the signal handler, which will call set_user_mode on its way +	 * out.  This should probably change since it's confusing. +	 */ +} + +static int new_thread_proc(void *stack) +{ +	/* local_irq_disable is needed to block out signals until this thread is +	 * properly scheduled.  Otherwise, the tracing thread will get mighty +	 * upset about any signals that arrive before that. +	 * This has the complication that it sets the saved signal mask in +	 * the sigcontext to block signals.  This gets restored when this +	 * thread (or a descendant, since they get a copy of this sigcontext) +	 * returns to userspace. +	 * So, this is compensated for elsewhere. +	 * XXX There is still a small window until local_irq_disable() actually +	 * finishes where signals are possible - shouldn't be a problem in +	 * practice since SIGIO hasn't been forwarded here yet, and the +	 * local_irq_disable should finish before a SIGVTALRM has time to be +	 * delivered. +	 */ + +	local_irq_disable(); +	init_new_thread_stack(stack, new_thread_handler); +	os_usr1_process(os_getpid()); +	change_sig(SIGUSR1, 1); +	return(0); +} + +/* Signal masking - signals are blocked at the start of fork_tramp.  They + * are re-enabled when finish_fork_handler is entered by fork_tramp hitting + * itself with a SIGUSR1.  set_user_mode has to be run with SIGUSR1 off, + * so it is blocked before it's called.  They are re-enabled on sigreturn + * despite the fact that they were blocked when the SIGUSR1 was issued because + * copy_thread copies the parent's sigcontext, including the signal mask + * onto the signal frame. + */ + +void finish_fork_handler(int sig) +{ + 	UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1); +	suspend_new_thread(current->thread.mode.tt.switch_pipe[0]); + +	force_flush_all(); +	if(current->thread.prev_sched != NULL) +		schedule_tail(current->thread.prev_sched); +	current->thread.prev_sched = NULL; + +	enable_timer(); +	change_sig(SIGVTALRM, 1); +	local_irq_enable(); +	if(current->mm != current->parent->mm) +		protect_memory(uml_reserved, high_physmem - uml_reserved, 1,  +			       1, 0, 1); +	task_protections((unsigned long) current_thread); + +	free_page(current->thread.temp_stack); +	local_irq_disable(); +	change_sig(SIGUSR1, 0); +	set_user_mode(current); +} + +int fork_tramp(void *stack) +{ +	local_irq_disable(); +	arch_init_thread(); +	init_new_thread_stack(stack, finish_fork_handler); + +	os_usr1_process(os_getpid()); +	change_sig(SIGUSR1, 1); +	return(0); +} + +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) +{ +	int (*tramp)(void *); +	int new_pid, err; +	unsigned long stack; +	 +	if(current->thread.forking) +		tramp = fork_tramp; +	else { +		tramp = new_thread_proc; +		p->thread.request.u.thread = current->thread.request.u.thread; +	} + +	err = os_pipe(p->thread.mode.tt.switch_pipe, 1, 1); +	if(err < 0){ +		printk("copy_thread : pipe failed, err = %d\n", -err); +		return(err); +	} + +	stack = alloc_stack(0, 0); +	if(stack == 0){ +		printk(KERN_ERR "copy_thread : failed to allocate " +		       "temporary stack\n"); +		return(-ENOMEM); +	} + +	clone_flags &= CLONE_VM; +	p->thread.temp_stack = stack; +	new_pid = start_fork_tramp(p->thread_info, stack, clone_flags, tramp); +	if(new_pid < 0){ +		printk(KERN_ERR "copy_thread : clone failed - errno = %d\n",  +		       -new_pid); +		return(new_pid); +	} + +	if(current->thread.forking){ +		sc_to_sc(UPT_SC(&p->thread.regs.regs),  +			 UPT_SC(¤t->thread.regs.regs)); +		SC_SET_SYSCALL_RETURN(UPT_SC(&p->thread.regs.regs), 0); +		if(sp != 0) SC_SP(UPT_SC(&p->thread.regs.regs)) = sp; +	} +	p->thread.mode.tt.extern_pid = new_pid; + +	current->thread.request.op = OP_FORK; +	current->thread.request.u.fork.pid = new_pid; +	os_usr1_process(os_getpid()); + +	/* Enable the signal and then disable it to ensure that it is handled +	 * here, and nowhere else. +	 */ +	change_sig(SIGUSR1, 1); + +	change_sig(SIGUSR1, 0); +	err = 0; +	return(err); +} + +void reboot_tt(void) +{ +	current->thread.request.op = OP_REBOOT; +	os_usr1_process(os_getpid()); +	change_sig(SIGUSR1, 1); +} + +void halt_tt(void) +{ +	current->thread.request.op = OP_HALT; +	os_usr1_process(os_getpid()); +	change_sig(SIGUSR1, 1); +} + +void kill_off_processes_tt(void) +{ +	struct task_struct *p; +	int me; + +	me = os_getpid(); +        for_each_process(p){ +		if(p->thread.mode.tt.extern_pid != me)  +			os_kill_process(p->thread.mode.tt.extern_pid, 0); +	} +	if(init_task.thread.mode.tt.extern_pid != me)  +		os_kill_process(init_task.thread.mode.tt.extern_pid, 0); +} + +void initial_thread_cb_tt(void (*proc)(void *), void *arg) +{ +	if(os_getpid() == tracing_pid){ +		(*proc)(arg); +	} +	else { +		current->thread.request.op = OP_CB; +		current->thread.request.u.cb.proc = proc; +		current->thread.request.u.cb.arg = arg; +		os_usr1_process(os_getpid()); +		change_sig(SIGUSR1, 1); + +		change_sig(SIGUSR1, 0); +	} +} + +int do_proc_op(void *t, int proc_id) +{ +	struct task_struct *task; +	struct thread_struct *thread; +	int op, pid; + +	task = t; +	thread = &task->thread; +	op = thread->request.op; +	switch(op){ +	case OP_NONE: +	case OP_TRACE_ON: +		break; +	case OP_EXEC: +		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; +		break; +	case OP_FORK: +		attach_process(thread->request.u.fork.pid); +		break; +	case OP_CB: +		(*thread->request.u.cb.proc)(thread->request.u.cb.arg); +		break; +	case OP_REBOOT: +	case OP_HALT: +		break; +	default: +		tracer_panic("Bad op in do_proc_op"); +		break; +	} +	thread->request.op = OP_NONE; +	return(op); +} + +void init_idle_tt(void) +{ +	default_idle(); +} + +extern void start_kernel(void); + +static int start_kernel_proc(void *unused) +{ +	int pid; + +	block_signals(); +	pid = os_getpid(); + +	cpu_tasks[0].pid = pid; +	cpu_tasks[0].task = current; +#ifdef CONFIG_SMP + 	cpu_online_map = cpumask_of_cpu(0); +#endif +	if(debug) os_stop_process(pid); +	start_kernel(); +	return(0); +} + +void set_tracing(void *task, int tracing) +{ +	((struct task_struct *) task)->thread.mode.tt.tracing = tracing; +} + +int is_tracing(void *t) +{ +	return (((struct task_struct *) t)->thread.mode.tt.tracing); +} + +int set_user_mode(void *t) +{ +	struct task_struct *task; + +	task = t ? t : current; +	if(task->thread.mode.tt.tracing)  +		return(1); +	task->thread.request.op = OP_TRACE_ON; +	os_usr1_process(os_getpid()); +	return(0); +} + +void set_init_pid(int pid) +{ +	int err; + +	init_task.thread.mode.tt.extern_pid = pid; +	err = os_pipe(init_task.thread.mode.tt.switch_pipe, 1, 1); +	if(err) +		panic("Can't create switch pipe for init_task, errno = %d", +		      -err); +} + +int start_uml_tt(void) +{ +	void *sp; +	int pages; + +	pages = (1 << CONFIG_KERNEL_STACK_ORDER); +	sp = (void *) ((unsigned long) init_task.thread_info) + +		pages * PAGE_SIZE - sizeof(unsigned long); +	return(tracer(start_kernel_proc, sp)); +} + +int external_pid_tt(struct task_struct *task) +{ +	return(task->thread.mode.tt.extern_pid); +} + +int thread_pid_tt(struct task_struct *task) +{ +	return(task->thread.mode.tt.extern_pid); +} + +int is_valid_pid(int pid) +{ +	struct task_struct *task; + +        read_lock(&tasklist_lock); +        for_each_process(task){ +                if(task->thread.mode.tt.extern_pid == pid){ +			read_unlock(&tasklist_lock); +			return(1); +                } +        } +	read_unlock(&tasklist_lock); +	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/kernel/tt/ptproxy/Makefile b/arch/um/kernel/tt/ptproxy/Makefile new file mode 100644 index 00000000000..3ad5b774de5 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/Makefile @@ -0,0 +1,10 @@ +#  +# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +obj-y = proxy.o ptrace.o sysdep.o wait.o + +USER_OBJS := $(obj-y) + +include arch/um/scripts/Makefile.rules diff --git a/arch/um/kernel/tt/ptproxy/proxy.c b/arch/um/kernel/tt/ptproxy/proxy.c new file mode 100644 index 00000000000..58800c50b10 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/proxy.c @@ -0,0 +1,377 @@ +/********************************************************************** +proxy.c + +Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing +terms and conditions. + +Jeff Dike (jdike@karaya.com) : Modified for integration into uml +**********************************************************************/ + +/* XXX This file shouldn't refer to CONFIG_* */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <termios.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <asm/unistd.h> +#include "ptrace_user.h" + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" + +#include "user_util.h" +#include "user.h" +#include "os.h" +#include "tempfile.h" + +static int debugger_wait(debugger_state *debugger, int *status, int options, +			 int (*syscall)(debugger_state *debugger, pid_t child), +			 int (*normal_return)(debugger_state *debugger,  +					      pid_t unused), +			 int (*wait_return)(debugger_state *debugger,  +					    pid_t unused)) +{ +	if(debugger->real_wait){ +		debugger->handle_trace = normal_return; +		syscall_continue(debugger->pid); +		debugger->real_wait = 0; +		return(1); +	} +	debugger->wait_status_ptr = status; +	debugger->wait_options = options; +	if((debugger->debugee != NULL) && debugger->debugee->event){ +		syscall_continue(debugger->pid); +		wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL, +			      NULL); +		(*wait_return)(debugger, -1); +		return(0); +	} +	else if(debugger->wait_options & WNOHANG){ +		syscall_cancel(debugger->pid, 0); +		debugger->handle_trace = syscall; +		return(0); +	} +	else { +		syscall_pause(debugger->pid); +		debugger->handle_trace = wait_return; +		debugger->waiting = 1; +	} +	return(1); +} + +/* + * Handle debugger trap, i.e. syscall. + */ + +int debugger_syscall(debugger_state *debugger, pid_t child) +{ +	long arg1, arg2, arg3, arg4, arg5, result; +	int syscall, ret = 0; + +	syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4,  +			      &arg5); + +	switch(syscall){ +	case __NR_execve: +		/* execve never returns */ +		debugger->handle_trace = debugger_syscall;  +		break; + +	case __NR_ptrace: +		if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; +		if(!debugger->debugee->in_context)  +			child = debugger->debugee->pid; +		result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child, +				      &ret); +		syscall_cancel(debugger->pid, result); +		debugger->handle_trace = debugger_syscall; +		return(ret); + +#ifdef __NR_waitpid +	case __NR_waitpid: +#endif +	case __NR_wait4: +		if(!debugger_wait(debugger, (int *) arg2, arg3,  +				  debugger_syscall, debugger_normal_return,  +				  proxy_wait_return)) +			return(0); +		break; + +	case __NR_kill: +		if(!debugger->debugee->in_context)  +			child = debugger->debugee->pid; +		if(arg1 == debugger->debugee->pid){ +			result = kill(child, arg2); +			syscall_cancel(debugger->pid, result); +			debugger->handle_trace = debugger_syscall; +			return(0); +		} +		else debugger->handle_trace = debugger_normal_return; +		break; + +	default: +		debugger->handle_trace = debugger_normal_return; +	} + +	syscall_continue(debugger->pid); +	return(0); +} + +/* Used by the tracing thread */ +static debugger_state parent; +static int parent_syscall(debugger_state *debugger, int pid); + +int init_parent_proxy(int pid) +{ +	parent = ((debugger_state) { .pid 		= pid, +				     .wait_options 	= 0, +				     .wait_status_ptr 	= NULL, +				     .waiting 		= 0, +				     .real_wait 	= 0, +				     .expecting_child 	= 0, +				     .handle_trace  	= parent_syscall, +				     .debugee 		= NULL } ); +	return(0); +} + +int parent_normal_return(debugger_state *debugger, pid_t unused) +{ +	debugger->handle_trace = parent_syscall; +	syscall_continue(debugger->pid); +	return(0); +} + +static int parent_syscall(debugger_state *debugger, int pid) +{ +	long arg1, arg2, arg3, arg4, arg5; +	int syscall; + +	syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5); +		 +	if((syscall == __NR_wait4) +#ifdef __NR_waitpid +	   || (syscall == __NR_waitpid) +#endif +	){ +		debugger_wait(&parent, (int *) arg2, arg3, parent_syscall, +			      parent_normal_return, parent_wait_return); +	} +	else ptrace(PTRACE_SYSCALL, pid, 0, 0); +	return(0); +} + +int debugger_normal_return(debugger_state *debugger, pid_t unused) +{ +	debugger->handle_trace = debugger_syscall; +	syscall_continue(debugger->pid); +	return(0); +} + +void debugger_cancelled_return(debugger_state *debugger, int result) +{ +	debugger->handle_trace = debugger_syscall; +	syscall_set_result(debugger->pid, result); +	syscall_continue(debugger->pid); +} + +/* Used by the tracing thread */ +static debugger_state debugger; +static debugee_state debugee; + +void init_proxy (pid_t debugger_pid, int stopped, int status) +{ +	debugger.pid = debugger_pid; +	debugger.handle_trace = debugger_syscall; +	debugger.debugee = &debugee; +	debugger.waiting = 0; +	debugger.real_wait = 0; +	debugger.expecting_child = 0; + +	debugee.pid = 0; +	debugee.traced = 0; +	debugee.stopped = stopped; +	debugee.event = 0; +	debugee.zombie = 0; +	debugee.died = 0; +	debugee.wait_status = status; +	debugee.in_context = 1; +} + +int debugger_proxy(int status, int pid) +{ +	int ret = 0, sig; + +	if(WIFSTOPPED(status)){ +		sig = WSTOPSIG(status); +		if (sig == SIGTRAP) +			ret = (*debugger.handle_trace)(&debugger, pid); +						        +		else if(sig == SIGCHLD){ +			if(debugger.expecting_child){ +				ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); +				debugger.expecting_child = 0; +			} +			else if(debugger.waiting) +				real_wait_return(&debugger); +			else { +				ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); +				debugger.real_wait = 1; +			} +		} +		else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig); +	} +	else if(WIFEXITED(status)){ +		tracer_panic("debugger (pid %d) exited with status %d",  +			     debugger.pid, WEXITSTATUS(status)); +	} +	else if(WIFSIGNALED(status)){ +		tracer_panic("debugger (pid %d) exited with signal %d",  +			     debugger.pid, WTERMSIG(status)); +	} +	else { +		tracer_panic("proxy got unknown status (0x%x) on debugger " +			     "(pid %d)", status, debugger.pid); +	} +	return(ret); +} + +void child_proxy(pid_t pid, int status) +{ +	debugee.event = 1; +	debugee.wait_status = status; + +	if(WIFSTOPPED(status)){ +		debugee.stopped = 1; +		debugger.expecting_child = 1; +		kill(debugger.pid, SIGCHLD); +	} +	else if(WIFEXITED(status) || WIFSIGNALED(status)){ +		debugee.zombie = 1; +		debugger.expecting_child = 1; +		kill(debugger.pid, SIGCHLD); +	} +	else panic("proxy got unknown status (0x%x) on child (pid %d)",  +		   status, pid); +} + +void debugger_parent_signal(int status, int pid) +{ +	int sig; + +	if(WIFSTOPPED(status)){ +		sig = WSTOPSIG(status); +		if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid); +		else ptrace(PTRACE_SYSCALL, pid, 0, sig); +	} +} + +void fake_child_exit(void) +{ +	int status, pid; + +	child_proxy(1, W_EXITCODE(0, 0)); +	while(debugger.waiting == 1){ +		CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); +		if(pid != debugger.pid){ +			printk("fake_child_exit - waitpid failed, " +			       "errno = %d\n", errno); +			return; +		} +		debugger_proxy(status, debugger.pid); +	} +	CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); +	if(pid != debugger.pid){ +		printk("fake_child_exit - waitpid failed, " +		       "errno = %d\n", errno); +		return; +	} +	if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0) +		printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n", +		       errno); +} + +char gdb_init_string[] =  +"att 1 \n\ +b panic \n\ +b stop \n\ +handle SIGWINCH nostop noprint pass \n\ +"; + +int start_debugger(char *prog, int startup, int stop, int *fd_out) +{ +	int slave, child; + +	slave = open_gdb_chan(); +	child = fork(); +	if(child == 0){ +		char *tempname = NULL; +		int fd; + +	        if(setsid() < 0) perror("setsid"); +		if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) ||  +		   (dup2(slave, 2) < 0)){ +			printk("start_debugger : dup2 failed, errno = %d\n", +			       errno); +			exit(1); +		} +		if(ioctl(0, TIOCSCTTY, 0) < 0){ +			printk("start_debugger : TIOCSCTTY failed, " +			       "errno = %d\n", errno); +			exit(1); +		} +		if(tcsetpgrp (1, os_getpid()) < 0){ +			printk("start_debugger : tcsetpgrp failed, " +			       "errno = %d\n", errno); +#ifdef notdef +			exit(1); +#endif +		} +		fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0); +		if(fd < 0){ +			printk("start_debugger : make_tempfile failed," +			       "err = %d\n", -fd); +			exit(1); +		} +		os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1); +		if(startup){ +			if(stop){ +				os_write_file(fd, "b start_kernel\n", +				      strlen("b start_kernel\n")); +			} +			os_write_file(fd, "c\n", strlen("c\n")); +		} +		if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ +			printk("start_debugger :  PTRACE_TRACEME failed, " +			       "errno = %d\n", errno); +			exit(1); +		} +		execlp("gdb", "gdb", "--command", tempname, prog, NULL); +		printk("start_debugger : exec of gdb failed, errno = %d\n", +		       errno); +	} +	if(child < 0){ +		printk("start_debugger : fork for gdb failed, errno = %d\n", +		       errno); +		return(-1); +	} +	*fd_out = slave; +	return(child); +} + +/* + * 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/ptproxy/ptproxy.h b/arch/um/kernel/tt/ptproxy/ptproxy.h new file mode 100644 index 00000000000..5eb0285b196 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/ptproxy.h @@ -0,0 +1,61 @@ +/********************************************************************** +ptproxy.h + +Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#ifndef __PTPROXY_H +#define __PTPROXY_H + +#include <sys/types.h> + +typedef struct debugger debugger_state; +typedef struct debugee debugee_state; + +struct debugger +{ +	pid_t pid; +	int wait_options; +	int *wait_status_ptr; +	unsigned int waiting : 1; +	unsigned int real_wait : 1; +	unsigned int expecting_child : 1; +	int (*handle_trace) (debugger_state *, pid_t); + +	debugee_state *debugee; +}; + +struct debugee +{ +	pid_t pid; +	int wait_status; +	unsigned int died : 1; +	unsigned int event : 1; +	unsigned int stopped : 1; +	unsigned int trace_singlestep : 1; +	unsigned int trace_syscall : 1; +	unsigned int traced : 1; +	unsigned int zombie : 1; +	unsigned int in_context : 1; +}; + +extern int debugger_syscall(debugger_state *debugger, pid_t pid); +extern int debugger_normal_return (debugger_state *debugger, pid_t unused); + +extern long proxy_ptrace (struct debugger *, int, pid_t, long, long, pid_t, +			  int *strace_out); +extern void debugger_cancelled_return(debugger_state *debugger, int result); + +#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/ptproxy/ptrace.c b/arch/um/kernel/tt/ptproxy/ptrace.c new file mode 100644 index 00000000000..528a5fc8d88 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/ptrace.c @@ -0,0 +1,237 @@ +/********************************************************************** +ptrace.c + +Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing +terms and conditions. + +Jeff Dike (jdike@karaya.com) : Modified for integration into uml +**********************************************************************/ + +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/wait.h> + +#include "ptproxy.h" +#include "debug.h" +#include "user_util.h" +#include "kern_util.h" +#include "ptrace_user.h" +#include "tt.h" + +long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2, +		  long arg3, long arg4, pid_t child, int *ret) +{ +	sigset_t relay; +	long result; +	int status; + +	*ret = 0; +	if(debugger->debugee->died) return(-ESRCH); + +	switch(arg1){ +	case PTRACE_ATTACH: +		if(debugger->debugee->traced) return(-EPERM); + +		debugger->debugee->pid = arg2; +		debugger->debugee->traced = 1; + +		if(is_valid_pid(arg2) && (arg2 != child)){ +			debugger->debugee->in_context = 0; +			kill(arg2, SIGSTOP); +			debugger->debugee->event = 1; +			debugger->debugee->wait_status = W_STOPCODE(SIGSTOP); +		} +		else { +			debugger->debugee->in_context = 1; +			if(debugger->debugee->stopped)  +				child_proxy(child, W_STOPCODE(SIGSTOP)); +			else kill(child, SIGSTOP); +		} + +		return(0); + +	case PTRACE_DETACH: +		if(!debugger->debugee->traced) return(-EPERM); +		 +		debugger->debugee->traced = 0; +		debugger->debugee->pid = 0; +		if(!debugger->debugee->in_context) +			kill(child, SIGCONT); + +		return(0); + +	case PTRACE_CONT: +		if(!debugger->debugee->in_context) return(-EPERM); +		*ret = PTRACE_CONT; +		return(ptrace(PTRACE_CONT, child, arg3, arg4)); + +#ifdef UM_HAVE_GETFPREGS +	case PTRACE_GETFPREGS: +	{ +		long regs[FP_FRAME_SIZE]; +		int i, result; + +		result = ptrace(PTRACE_GETFPREGS, child, 0, regs); +		if(result == -1) return(-errno); +		 +		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) +			ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, +			       regs[i]); +		return(result); +	} +#endif + +#ifdef UM_HAVE_GETFPXREGS +	case PTRACE_GETFPXREGS: +	{ +		long regs[FPX_FRAME_SIZE]; +		int i, result; + +		result = ptrace(PTRACE_GETFPXREGS, child, 0, regs); +		if(result == -1) return(-errno); +		 +		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) +			ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, +			       regs[i]); +		return(result); +	} +#endif + +#ifdef UM_HAVE_GETREGS +	case PTRACE_GETREGS: +	{ +		long regs[FRAME_SIZE]; +		int i, result; + +		result = ptrace(PTRACE_GETREGS, child, 0, regs); +		if(result == -1) return(-errno); + +		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) +			ptrace (PTRACE_POKEDATA, debugger->pid, +				arg4 + 4 * i, regs[i]); +		return(result); +	} +	break; +#endif + +	case PTRACE_KILL: +		result = ptrace(PTRACE_KILL, child, arg3, arg4); +		if(result == -1) return(-errno); + +		return(result); + +	case PTRACE_PEEKDATA: +	case PTRACE_PEEKTEXT: +	case PTRACE_PEEKUSR: +		/* The value being read out could be -1, so we have to  +		 * check errno to see if there's an error, and zero it +		 * beforehand so we're not faked out by an old error +		 */ + +		errno = 0; +		result = ptrace(arg1, child, arg3, 0); +		if((result == -1) && (errno != 0)) return(-errno); + +		result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result); +		if(result == -1) return(-errno); +			 +		return(result); + +	case PTRACE_POKEDATA: +	case PTRACE_POKETEXT: +	case PTRACE_POKEUSR: +		result = ptrace(arg1, child, arg3, arg4); +		if(result == -1) return(-errno); + +		if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4); +		return(result); + +#ifdef UM_HAVE_SETFPREGS +	case PTRACE_SETFPREGS: +	{ +		long regs[FP_FRAME_SIZE]; +		int i; + +		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) +			regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, +					  arg4 + 4 * i, 0); +		result = ptrace(PTRACE_SETFPREGS, child, 0, regs); +		if(result == -1) return(-errno); + +		return(result); +	} +#endif + +#ifdef UM_HAVE_SETFPXREGS +	case PTRACE_SETFPXREGS: +	{ +		long regs[FPX_FRAME_SIZE]; +		int i; + +		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) +			regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, +					  arg4 + 4 * i, 0); +		result = ptrace(PTRACE_SETFPXREGS, child, 0, regs); +		if(result == -1) return(-errno); + +		return(result); +	} +#endif + +#ifdef UM_HAVE_SETREGS +	case PTRACE_SETREGS: +	{ +		long regs[FRAME_SIZE]; +		int i; + +		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) +			regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid, +					 arg4 + 4 * i, 0); +		result = ptrace(PTRACE_SETREGS, child, 0, regs); +		if(result == -1) return(-errno); + +		return(result); +	} +#endif + +	case PTRACE_SINGLESTEP: +		if(!debugger->debugee->in_context) return(-EPERM); +		sigemptyset(&relay); +		sigaddset(&relay, SIGSEGV); +		sigaddset(&relay, SIGILL); +		sigaddset(&relay, SIGBUS); +		result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4); +		if(result == -1) return(-errno); +		 +		status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP, +				       &relay); +		child_proxy(child, status); +		return(result); + +	case PTRACE_SYSCALL: +		if(!debugger->debugee->in_context) return(-EPERM); +		result = ptrace(PTRACE_SYSCALL, child, arg3, arg4); +		if(result == -1) return(-errno); + +		*ret = PTRACE_SYSCALL; +		return(result); + +	case PTRACE_TRACEME: +	default: +		return(-EINVAL); +	} +} + +/* + * 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/ptproxy/sysdep.c b/arch/um/kernel/tt/ptproxy/sysdep.c new file mode 100644 index 00000000000..a5f0e01e214 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/sysdep.c @@ -0,0 +1,70 @@ +/********************************************************************** +sysdep.c + +Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <linux/unistd.h> +#include "ptrace_user.h" +#include "user_util.h" +#include "user.h" + +int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3, long *arg4,  +		long *arg5) +{ +	*arg1 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG1_OFFSET, 0); +	*arg2 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG2_OFFSET, 0); +	*arg3 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG3_OFFSET, 0); +	*arg4 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG4_OFFSET, 0); +	*arg5 = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_ARG5_OFFSET, 0); +	return(ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET, 0)); +} + +void syscall_cancel(pid_t pid, int result) +{ +	if((ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, +		   __NR_getpid) < 0) || +	   (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) || +	   (wait_for_stop(pid, SIGTRAP, PTRACE_SYSCALL, NULL) < 0) || +	   (ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result) < 0) || +	   (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)) +		printk("ptproxy: couldn't cancel syscall: errno = %d\n",  +		       errno); +} + +void syscall_set_result(pid_t pid, long result) +{ +	ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, result); +} + +void syscall_continue(pid_t pid) +{ +	ptrace(PTRACE_SYSCALL, pid, 0, 0); +} + +int syscall_pause(pid_t pid)  +{ +	if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, __NR_pause) < 0){ +		printk("syscall_change - ptrace failed, errno = %d\n", errno); +		return(-1); +	} +	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/kernel/tt/ptproxy/sysdep.h b/arch/um/kernel/tt/ptproxy/sysdep.h new file mode 100644 index 00000000000..735f488049a --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/sysdep.h @@ -0,0 +1,25 @@ +/********************************************************************** +sysdep.h + +Copyright (C) 1999 Lars Brinkhoff. +Copyright (C) 2001 Jeff Dike (jdike@karaya.com) +See the file COPYING for licensing terms and conditions. +**********************************************************************/ + +extern int get_syscall(pid_t pid, long *arg1, long *arg2, long *arg3,  +		       long *arg4, long *arg5); +extern void syscall_cancel (pid_t pid, long result); +extern void syscall_set_result (pid_t pid, long result); +extern void syscall_continue (pid_t pid); +extern int syscall_pause(pid_t pid); + +/* + * 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/ptproxy/wait.c b/arch/um/kernel/tt/ptproxy/wait.c new file mode 100644 index 00000000000..12f6319d8d7 --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/wait.c @@ -0,0 +1,86 @@ +/********************************************************************** +wait.c + +Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing +terms and conditions. + +**********************************************************************/ + +#include <errno.h> +#include <signal.h> +#include <sys/wait.h> + +#include "ptproxy.h" +#include "sysdep.h" +#include "wait.h" +#include "user_util.h" +#include "ptrace_user.h" +#include "sysdep/ptrace.h" +#include "sysdep/sigcontext.h" + +int proxy_wait_return(struct debugger *debugger, pid_t unused) +{ +	debugger->waiting = 0; + +	if(debugger->debugee->died || (debugger->wait_options & __WCLONE)){ +		debugger_cancelled_return(debugger, -ECHILD); +		return(0); +	} + +	if(debugger->debugee->zombie && debugger->debugee->event) +		debugger->debugee->died = 1; + +	if(debugger->debugee->event){ +		debugger->debugee->event = 0; +		ptrace(PTRACE_POKEDATA, debugger->pid, +		       debugger->wait_status_ptr,  +		       debugger->debugee->wait_status); +		/* if (wait4) +		   ptrace (PTRACE_POKEDATA, pid, rusage_ptr, ...); */ +		debugger_cancelled_return(debugger, debugger->debugee->pid); +		return(0); +	} + +	/* pause will return -EINTR, which happens to be right for wait */ +	debugger_normal_return(debugger, -1); +	return(0); +} + +int parent_wait_return(struct debugger *debugger, pid_t unused) +{ +	return(debugger_normal_return(debugger, -1)); +} + +int real_wait_return(struct debugger *debugger) +{ +	unsigned long ip; +	int pid; + +	pid = debugger->pid; + +	ip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0); +	IP_RESTART_SYSCALL(ip); + +	if(ptrace(PTRACE_POKEUSR, pid, PT_IP_OFFSET, ip) < 0) +		tracer_panic("real_wait_return : Failed to restart system " +			     "call, errno = %d\n", errno); + +	if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) || +	   (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) || +	   (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) || +	   debugger_normal_return(debugger, -1)) +		tracer_panic("real_wait_return : gdb failed to wait, " +			     "errno = %d\n", errno); +	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/kernel/tt/ptproxy/wait.h b/arch/um/kernel/tt/ptproxy/wait.h new file mode 100644 index 00000000000..542e73ee2ce --- /dev/null +++ b/arch/um/kernel/tt/ptproxy/wait.h @@ -0,0 +1,15 @@ +/********************************************************************** +wait.h + +Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing +terms and conditions. +**********************************************************************/ + +#ifndef __PTPROXY_WAIT_H +#define __PTPROXY_WAIT_H + +extern int proxy_wait_return(struct debugger *debugger, pid_t unused); +extern int real_wait_return(struct debugger *debugger); +extern int parent_wait_return(struct debugger *debugger, pid_t unused); + +#endif diff --git a/arch/um/kernel/tt/syscall_kern.c b/arch/um/kernel/tt/syscall_kern.c new file mode 100644 index 00000000000..2650a628719 --- /dev/null +++ b/arch/um/kernel/tt/syscall_kern.c @@ -0,0 +1,47 @@ +/*  + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/types.h" +#include "linux/utime.h" +#include "linux/sys.h" +#include "linux/ptrace.h" +#include "asm/unistd.h" +#include "asm/ptrace.h" +#include "asm/uaccess.h" +#include "asm/stat.h" +#include "sysdep/syscalls.h" +#include "kern_util.h" + +extern syscall_handler_t *sys_call_table[]; + +long execute_syscall_tt(void *r) +{ +	struct pt_regs *regs = r; +	long res; +	int syscall; + +#ifdef CONFIG_SYSCALL_DEBUG +	current->thread.nsyscalls++; +	nsyscalls++; +#endif +	syscall = UPT_SYSCALL_NR(®s->regs); + +	if((syscall >= NR_syscalls) || (syscall < 0)) +		res = -ENOSYS; +	else res = EXECUTE_SYSCALL(syscall, regs); + +	return(res); +} + +/* + * 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/syscall_user.c b/arch/um/kernel/tt/syscall_user.c new file mode 100644 index 00000000000..e4e7e9c2224 --- /dev/null +++ b/arch/um/kernel/tt/syscall_user.c @@ -0,0 +1,90 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <asm/unistd.h> +#include "sysdep/ptrace.h" +#include "sigcontext.h" +#include "ptrace_user.h" +#include "task.h" +#include "user_util.h" +#include "kern_util.h" +#include "syscall_user.h" +#include "tt.h" + + +void syscall_handler_tt(int sig, union uml_pt_regs *regs) +{ +	void *sc; +	long result; +	int syscall; +#ifdef UML_CONFIG_DEBUG_SYSCALL +	int index; +#endif + +	syscall = UPT_SYSCALL_NR(regs); +	sc = UPT_SC(regs); +	SC_START_SYSCALL(sc); + +#ifdef UML_CONFIG_DEBUG_SYSCALL +  	index = record_syscall_start(syscall); +#endif +	syscall_trace(regs, 0); +	result = execute_syscall_tt(regs); + +	/* regs->sc may have changed while the system call ran (there may +	 * have been an interrupt or segfault), so it needs to be refreshed. +	 */ +	UPT_SC(regs) = sc; + +	SC_SET_SYSCALL_RETURN(sc, result); + +	syscall_trace(regs, 1); +#ifdef UML_CONFIG_DEBUG_SYSCALL +  	record_syscall_end(index, result); +#endif +} + +void do_sigtrap(void *task) +{ +	UPT_SYSCALL_NR(TASK_REGS(task)) = -1; +} + +void do_syscall(void *task, int pid, int local_using_sysemu) +{ +	unsigned long proc_regs[FRAME_SIZE]; + +	if(ptrace_getregs(pid, proc_regs) < 0) +		tracer_panic("Couldn't read registers"); + +	UPT_SYSCALL_NR(TASK_REGS(task)) = PT_SYSCALL_NR(proc_regs); + +	if(((unsigned long *) PT_IP(proc_regs) >= &_stext) && +	   ((unsigned long *) PT_IP(proc_regs) <= &_etext)) +		tracer_panic("I'm tracing myself and I can't get out"); + +	/* advanced sysemu mode set syscall number to -1 automatically */ +	if (local_using_sysemu==2) +		return; + +	/* syscall number -1 in sysemu skips syscall restarting in host */ +	if(ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET, +		  local_using_sysemu ? -1 : __NR_getpid) < 0) +		tracer_panic("do_syscall : Nullifying syscall failed, " +			     "errno = %d", errno); +} + +/* + * 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/time.c b/arch/um/kernel/tt/time.c new file mode 100644 index 00000000000..8565b71b07c --- /dev/null +++ b/arch/um/kernel/tt/time.c @@ -0,0 +1,28 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <signal.h> +#include <sys/time.h> +#include <time_user.h> +#include "process.h" +#include "user.h" + +void user_time_init_tt(void) +{ +	if(signal(SIGVTALRM, (__sighandler_t) alarm_handler) == SIG_ERR) +		panic("Couldn't set SIGVTALRM handler"); +	set_interval(ITIMER_VIRTUAL); +} + +/* + * 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/tlb.c b/arch/um/kernel/tt/tlb.c new file mode 100644 index 00000000000..203216ad86f --- /dev/null +++ b/arch/um/kernel/tt/tlb.c @@ -0,0 +1,149 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright 2003 PathScale, Inc. + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/mm.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/uaccess.h" +#include "asm/tlbflush.h" +#include "user_util.h" +#include "mem_user.h" +#include "os.h" +#include "tlb.h" + +static void do_ops(int unused, struct host_vm_op *ops, int last) +{ +	struct host_vm_op *op; +	int i; + +	for(i = 0; i <= last; i++){ +		op = &ops[i]; +		switch(op->type){ +		case MMAP: +                        os_map_memory((void *) op->u.mmap.addr, op->u.mmap.fd, +				      op->u.mmap.offset, op->u.mmap.len, +				      op->u.mmap.r, op->u.mmap.w, +				      op->u.mmap.x); +			break; +		case MUNMAP: +			os_unmap_memory((void *) op->u.munmap.addr, +					op->u.munmap.len); +			break; +		case MPROTECT: +			protect_memory(op->u.mprotect.addr, op->u.munmap.len, +				       op->u.mprotect.r, op->u.mprotect.w, +				       op->u.mprotect.x, 1); +			break; +		default: +			printk("Unknown op type %d in do_ops\n", op->type); +			break; +		} +	} +} + +static void fix_range(struct mm_struct *mm, unsigned long start_addr,  +		      unsigned long end_addr, int force) +{ +        if((current->thread.mode.tt.extern_pid != -1) && +           (current->thread.mode.tt.extern_pid != os_getpid())) +                panic("fix_range fixing wrong address space, current = 0x%p", +                      current); + +        fix_range_common(mm, start_addr, end_addr, force, 0, do_ops); +} + +atomic_t vmchange_seq = ATOMIC_INIT(1); + +void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end) +{ +        if(flush_tlb_kernel_range_common(start, end)) +                atomic_inc(&vmchange_seq); +} + +static void protect_vm_page(unsigned long addr, int w, int must_succeed) +{ +	int err; + +	err = protect_memory(addr, PAGE_SIZE, 1, w, 1, must_succeed); +	if(err == 0) return; +	else if((err == -EFAULT) || (err == -ENOMEM)){ +		flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +		protect_vm_page(addr, w, 1); +	} +	else panic("protect_vm_page : protect failed, errno = %d\n", err); +} + +void mprotect_kernel_vm(int w) +{ +	struct mm_struct *mm; +	pgd_t *pgd; +	pud_t *pud; +	pmd_t *pmd; +	pte_t *pte; +	unsigned long addr; +	 +	mm = &init_mm; +	for(addr = start_vm; addr < end_vm;){ +		pgd = pgd_offset(mm, addr); +		pud = pud_offset(pgd, addr); +		pmd = pmd_offset(pud, addr); +		if(pmd_present(*pmd)){ +			pte = pte_offset_kernel(pmd, addr); +			if(pte_present(*pte)) protect_vm_page(addr, w, 0); +			addr += PAGE_SIZE; +		} +		else addr += PMD_SIZE; +	} +} + +void flush_tlb_kernel_vm_tt(void) +{ +        flush_tlb_kernel_range(start_vm, end_vm); +} + +void __flush_tlb_one_tt(unsigned long addr) +{ +        flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +} +   +void flush_tlb_range_tt(struct vm_area_struct *vma, unsigned long start,  +		     unsigned long end) +{ +	if(vma->vm_mm != current->mm) return; + +	/* Assumes that the range start ... end is entirely within +	 * either process memory or kernel vm +	 */ +	if((start >= start_vm) && (start < end_vm)){ +		if(flush_tlb_kernel_range_common(start, end)) +			atomic_inc(&vmchange_seq); +	} +	else fix_range(vma->vm_mm, start, end, 0); +} + +void flush_tlb_mm_tt(struct mm_struct *mm) +{ +	unsigned long seq; + +	if(mm != current->mm) return; + +	fix_range(mm, 0, STACK_TOP, 0); + +	seq = atomic_read(&vmchange_seq); +	if(current->thread.mode.tt.vm_seq == seq) +		return; +	current->thread.mode.tt.vm_seq = seq; +	flush_tlb_kernel_range_common(start_vm, end_vm); +} + +void force_flush_all_tt(void) +{ +	fix_range(current->mm, 0, STACK_TOP, 1); +	flush_tlb_kernel_range_common(start_vm, end_vm); +} diff --git a/arch/um/kernel/tt/tracer.c b/arch/um/kernel/tt/tracer.c new file mode 100644 index 00000000000..7b5d937e595 --- /dev/null +++ b/arch/um/kernel/tt/tracer.c @@ -0,0 +1,480 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <sched.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/wait.h> +#include "user.h" +#include "sysdep/ptrace.h" +#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" +#include "kern_util.h" +#include "chan_user.h" +#include "ptrace_user.h" +#include "mode.h" +#include "tt.h" + +static int tracer_winch[2]; + +int is_tracer_winch(int pid, int fd, void *data) +{ +	if(pid != tracing_pid) +		return(0); + +	register_winch_irq(tracer_winch[0], fd, -1, data); +	return(1); +} + +static void tracer_winch_handler(int sig) +{ +	int n; +	char c = 1; + +	n = os_write_file(tracer_winch[1], &c, sizeof(c)); +	if(n != sizeof(c)) +		printk("tracer_winch_handler - write failed, err = %d\n", -n); +} + +/* Called only by the tracing thread during initialization */ + +static void setup_tracer_winch(void) +{ +	int err; + +	err = os_pipe(tracer_winch, 1, 1); +	if(err < 0){ +		printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err); +		return; +	} +	signal(SIGWINCH, tracer_winch_handler); +} + +void attach_process(int pid) +{ +	if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) || +	   (ptrace(PTRACE_CONT, pid, 0, 0) < 0)) +		tracer_panic("OP_FORK failed to attach pid"); +	wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL); +	if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) +		tracer_panic("OP_FORK: PTRACE_SETOPTIONS failed, errno = %d", errno); +	if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) +		tracer_panic("OP_FORK failed to continue process"); +} + +void tracer_panic(char *format, ...) +{ +	va_list ap; + +	va_start(ap, format); +	vprintf(format, ap); +	va_end(ap); +	printf("\n"); +	while(1) pause(); +} + +static void tracer_segv(int sig, struct sigcontext sc) +{ +	printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n", +	       SC_FAULT_ADDR(&sc), SC_IP(&sc)); +	while(1) +		pause(); +} + +/* Changed early in boot, and then only read */ +int debug = 0; +int debug_stop = 1; +int debug_parent = 0; +int honeypot = 0; + +static int signal_tramp(void *arg) +{ +	int (*proc)(void *); + +	if(honeypot && munmap((void *) (host_task_size - 0x10000000), +			      0x10000000))  +		panic("Unmapping stack failed"); +	if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) +		panic("ptrace PTRACE_TRACEME failed"); +	os_stop_process(os_getpid()); +	change_sig(SIGWINCH, 0); +	signal(SIGUSR1, SIG_IGN); +	change_sig(SIGCHLD, 0); +	signal(SIGSEGV, (__sighandler_t) sig_handler); +	set_cmdline("(idle thread)"); +	set_init_pid(os_getpid()); +	proc = arg; +	return((*proc)(NULL)); +} + +static void sleeping_process_signal(int pid, int sig) +{ +	switch(sig){ +	/* These two result from UML being ^Z-ed and bg-ed.  PTRACE_CONT is +	 * right because the process must be in the kernel already. +	 */ +	case SIGCONT: +	case SIGTSTP: +		if(ptrace(PTRACE_CONT, pid, 0, sig) < 0) +			tracer_panic("sleeping_process_signal : Failed to " +				     "continue pid %d, signal = %d, " +				     "errno = %d\n", pid, sig, errno); +		break; + +	/* This happens when the debugger (e.g. strace) is doing system call  +	 * tracing on the kernel.  During a context switch, the current task +	 * will be set to the incoming process and the outgoing process will +	 * hop into write and then read.  Since it's not the current process +	 * any more, the trace of those will land here.  So, we need to just  +	 * PTRACE_SYSCALL it. +	 */ +	case (SIGTRAP + 0x80): +		if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) +			tracer_panic("sleeping_process_signal : Failed to " +				     "PTRACE_SYSCALL pid %d, errno = %d\n", +				     pid, errno); +		break; +	case SIGSTOP: +		break; +	default: +		tracer_panic("sleeping process %d got unexpected " +			     "signal : %d\n", pid, sig); +		break; +	} +} + +/* Accessed only by the tracing thread */ +int debugger_pid = -1; +int debugger_parent = -1; +int debugger_fd = -1; +int gdb_pid = -1; + +struct { +	int pid; +	int signal; +	unsigned long addr; +	struct timeval time; +} signal_record[1024][32]; + +int signal_index[32]; +int nsignals = 0; +int debug_trace = 0; +extern int io_nsignals, io_count, intr_count; + +extern void signal_usr1(int sig); + +int tracing_pid = -1; + +int tracer(int (*init_proc)(void *), void *sp) +{ +	void *task = NULL; +	int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0; +	int proc_id = 0, n, err, old_tracing = 0, strace = 0; +	int local_using_sysemu = 0; +#ifdef UML_CONFIG_SYSCALL_DEBUG +	unsigned long eip = 0; +	int last_index; +#endif +	signal(SIGPIPE, SIG_IGN); +	setup_tracer_winch(); +	tracing_pid = os_getpid(); +	printf("tracing thread pid = %d\n", tracing_pid); + +	pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc); +	CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); +	if(n < 0){ +		printf("waitpid on idle thread failed, errno = %d\n", errno); +		exit(1); +	} +	if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) { +		printf("Failed to PTRACE_SETOPTIONS for idle thread, errno = %d\n", errno); +		exit(1); +	} +	if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){ +		printf("Failed to continue idle thread, errno = %d\n", errno); +		exit(1); +	} + +	signal(SIGSEGV, (sighandler_t) tracer_segv); +	signal(SIGUSR1, signal_usr1); +	if(debug_trace){ +		printf("Tracing thread pausing to be attached\n"); +		stop(); +	} +	if(debug){ +		if(gdb_pid != -1)  +			debugger_pid = attach_debugger(pid, gdb_pid, 1); +		else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop); +		if(debug_parent){ +			debugger_parent = os_process_parent(debugger_pid); +			init_parent_proxy(debugger_parent); +			err = attach(debugger_parent); +			if(err){ +				printf("Failed to attach debugger parent %d, " +				       "errno = %d\n", debugger_parent, -err); +				debugger_parent = -1; +			} +			else { +				if(ptrace(PTRACE_SYSCALL, debugger_parent,  +					  0, 0) < 0){ +					printf("Failed to continue debugger " +					       "parent, errno = %d\n", errno); +					debugger_parent = -1; +				} +			} +		} +	} +	set_cmdline("(tracing thread)"); +	while(1){ +		CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED)); +		if(pid <= 0){ +			if(errno != ECHILD){ +				printf("wait failed - errno = %d\n", errno); +			} +			continue; +		} +		if(pid == debugger_pid){ +			int cont = 0; + +			if(WIFEXITED(status) || WIFSIGNALED(status)) +				debugger_pid = -1; +			/* XXX Figure out how to deal with gdb and SMP */ +			else cont = debugger_signal(status, cpu_tasks[0].pid); +			if(cont == PTRACE_SYSCALL) strace = 1; +			continue; +		} +		else if(pid == debugger_parent){ +			debugger_parent_signal(status, pid); +			continue; +		} +		nsignals++; +		if(WIFEXITED(status)) ; +#ifdef notdef +		{ +			printf("Child %d exited with status %d\n", pid,  +			       WEXITSTATUS(status)); +		} +#endif +		else if(WIFSIGNALED(status)){ +			sig = WTERMSIG(status); +			if(sig != 9){ +				printf("Child %d exited with signal %d\n", pid, +				       sig); +			} +		} +		else if(WIFSTOPPED(status)){ +			proc_id = pid_to_processor_id(pid); +			sig = WSTOPSIG(status); +#ifdef UML_CONFIG_SYSCALL_DEBUG +			if(signal_index[proc_id] == 1024){ +				signal_index[proc_id] = 0; +				last_index = 1023; +			} +			else last_index = signal_index[proc_id] - 1; +			if(((sig == SIGPROF) || (sig == SIGVTALRM) || +			    (sig == SIGALRM)) && +			   (signal_record[proc_id][last_index].signal == sig)&& +			   (signal_record[proc_id][last_index].pid == pid)) +				signal_index[proc_id] = last_index; +			signal_record[proc_id][signal_index[proc_id]].pid = pid; +			gettimeofday(&signal_record[proc_id][signal_index[proc_id]].time, NULL); +			eip = ptrace(PTRACE_PEEKUSR, pid, PT_IP_OFFSET, 0); +			signal_record[proc_id][signal_index[proc_id]].addr = eip; +			signal_record[proc_id][signal_index[proc_id]++].signal = sig; +#endif +			if(proc_id == -1){ +				sleeping_process_signal(pid, sig); +				continue; +			} + +			task = cpu_tasks[proc_id].task; +			tracing = is_tracing(task); +			old_tracing = tracing; + +			/* Assume: no syscall, when coming from user */ +			if ( tracing ) +				do_sigtrap(task); + +			switch(sig){ +			case SIGUSR1: +				sig = 0; +				op = do_proc_op(task, proc_id); +				switch(op){ +				/* +				 * This is called when entering user mode; after +				 * this, we start intercepting syscalls. +				 * +				 * In fact, a process is started in kernel mode, +				 * so with is_tracing() == 0 (and that is reset +				 * when executing syscalls, since UML kernel has +				 * the right to do syscalls); +				 */ +				case OP_TRACE_ON: +					arch_leave_kernel(task, pid); +					tracing = 1; +					break; +				case OP_REBOOT: +				case OP_HALT: +					unmap_physmem(); +					kmalloc_ok = 0; +					os_kill_ptraced_process(pid, 0); +					/* Now let's reap remaining zombies */ +					errno = 0; +					do { +						waitpid(-1, &status, +							WUNTRACED); +					} while (errno != ECHILD); +					return(op == OP_REBOOT); +				case OP_NONE: +					printf("Detaching pid %d\n", pid); +					detach(pid, SIGSTOP); +					continue; +				default: +					break; +				} +				/* OP_EXEC switches host processes on us, +				 * we want to continue the new one. +				 */ +				pid = cpu_tasks[proc_id].pid; +				break; +			case (SIGTRAP + 0x80): +				if(!tracing && (debugger_pid != -1)){ +					child_signal(pid, status & 0x7fff); +					continue; +				} +				tracing = 0; +				/* local_using_sysemu has been already set +				 * below, since if we are here, is_tracing() on +				 * the traced task was 1, i.e. the process had +				 * already run through one iteration of the +				 * loop which executed a OP_TRACE_ON request.*/ +				do_syscall(task, pid, local_using_sysemu); +				sig = SIGUSR2; +				break; +			case SIGTRAP: +				if(!tracing && (debugger_pid != -1)){ +					child_signal(pid, status); +					continue; +				} +				tracing = 0; +				break; +			case SIGPROF: +				if(tracing) sig = 0; +				break; +			case SIGCHLD: +			case SIGHUP: +				sig = 0; +				break; +			case SIGSEGV: +			case SIGIO: +			case SIGALRM: +			case SIGVTALRM: +			case SIGFPE: +			case SIGBUS: +			case SIGILL: +			case SIGWINCH: + +			default: +				tracing = 0; +				break; +			} +			set_tracing(task, tracing); + +			if(!tracing && old_tracing) +				arch_enter_kernel(task, pid); + +			if(!tracing && (debugger_pid != -1) && (sig != 0) && +				(sig != SIGALRM) && (sig != SIGVTALRM) && +				(sig != SIGSEGV) && (sig != SIGTRAP) && +				(sig != SIGUSR2) && (sig != SIGIO) && +				(sig != SIGFPE)){ +				child_signal(pid, status); +				continue; +			} + +			local_using_sysemu = get_using_sysemu(); + +			if(tracing) +				cont_type = SELECT_PTRACE_OPERATION(local_using_sysemu, +				                                    singlestepping(task)); +			else if((debugger_pid != -1) && strace) +				cont_type = PTRACE_SYSCALL; +			else +				cont_type = PTRACE_CONT; + +			if(ptrace(cont_type, pid, 0, sig) != 0){ +				tracer_panic("ptrace failed to continue " +					     "process - errno = %d\n",  +					     errno); +			} +		} +	} +	return(0); +} + +static int __init uml_debug_setup(char *line, int *add) +{ +	char *next; + +	debug = 1; +	*add = 0; +	if(*line != '=') return(0); +	line++; + +	while(line != NULL){ +		next = strchr(line, ','); +		if(next) *next++ = '\0'; +		 +		if(!strcmp(line, "go"))	debug_stop = 0; +		else if(!strcmp(line, "parent")) debug_parent = 1; +		else printf("Unknown debug option : '%s'\n", line); + +		line = next; +	} +	return(0); +} + +__uml_setup("debug", uml_debug_setup, +"debug\n" +"    Starts up the kernel under the control of gdb. See the \n" +"    kernel debugging tutorial and the debugging session pages\n" +"    at http://user-mode-linux.sourceforge.net/ for more information.\n\n" +); + +static int __init uml_debugtrace_setup(char *line, int *add) +{ +	debug_trace = 1; +	return 0; +} +__uml_setup("debugtrace", uml_debugtrace_setup, +"debugtrace\n" +"    Causes the tracing thread to pause until it is attached by a\n" +"    debugger and continued.  This is mostly for debugging crashes\n" +"    early during boot, and should be pretty much obsoleted by\n" +"    the debug switch.\n\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/kernel/tt/trap_user.c b/arch/um/kernel/tt/trap_user.c new file mode 100644 index 00000000000..92a3820ca54 --- /dev/null +++ b/arch/um/kernel/tt/trap_user.c @@ -0,0 +1,60 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <errno.h> +#include <signal.h> +#include "sysdep/ptrace.h" +#include "signal_user.h" +#include "user_util.h" +#include "kern_util.h" +#include "task.h" +#include "tt.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; + +	/* This is done because to allow SIGSEGV to be delivered inside a SEGV +	 * handler.  This can happen in copy_user, and if SEGV is disabled, +	 * the process will die. +	 */ +	if(sig == SIGSEGV) +		change_sig(SIGSEGV, 1); + +	r = &TASK_REGS(get_current())->tt; +	save_regs = *r; +	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(); + +	(*info->handler)(sig, (union uml_pt_regs *) r); + +	if(is_user){ +		interrupt_end(); +		block_signals(); +		set_user_mode(NULL); +	} +	*r = save_regs; +	errno = save_errno; +} + +/* + * 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/uaccess.c b/arch/um/kernel/tt/uaccess.c new file mode 100644 index 00000000000..a72aa632972 --- /dev/null +++ b/arch/um/kernel/tt/uaccess.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "asm/uaccess.h" + +int copy_from_user_tt(void *to, const void __user *from, int n) +{ +	if(!access_ok_tt(VERIFY_READ, from, n)) +		return(n); + +	return(__do_copy_from_user(to, from, n, ¤t->thread.fault_addr, +				   ¤t->thread.fault_catcher)); +} + +int copy_to_user_tt(void __user *to, const void *from, int n) +{ +	if(!access_ok_tt(VERIFY_WRITE, to, n)) +		return(n); + +	return(__do_copy_to_user(to, from, n, ¤t->thread.fault_addr, +				 ¤t->thread.fault_catcher)); +} + +int strncpy_from_user_tt(char *dst, const char __user *src, int count) +{ +	int n; + +	if(!access_ok_tt(VERIFY_READ, src, 1)) +		return(-EFAULT); + +	n = __do_strncpy_from_user(dst, src, count, +				   ¤t->thread.fault_addr, +				   ¤t->thread.fault_catcher); +	if(n < 0) return(-EFAULT); +	return(n); +} + +int __clear_user_tt(void __user *mem, int len) +{ +	return(__do_clear_user(mem, len, +			       ¤t->thread.fault_addr, +			       ¤t->thread.fault_catcher)); +} + +int clear_user_tt(void __user *mem, int len) +{ +	if(!access_ok_tt(VERIFY_WRITE, mem, len)) +		return(len); + +	return(__do_clear_user(mem, len, ¤t->thread.fault_addr, +			       ¤t->thread.fault_catcher)); +} + +int strnlen_user_tt(const void __user *str, int len) +{ +	return(__do_strnlen_user(str, len, +				 ¤t->thread.fault_addr, +				 ¤t->thread.fault_catcher)); +} + +/* + * 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/uaccess_user.c b/arch/um/kernel/tt/uaccess_user.c new file mode 100644 index 00000000000..f01475512ec --- /dev/null +++ b/arch/um/kernel/tt/uaccess_user.c @@ -0,0 +1,98 @@ +/*  + * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <setjmp.h> +#include <string.h> +#include "user_util.h" +#include "uml_uaccess.h" +#include "task.h" +#include "kern_util.h" + +int __do_copy_from_user(void *to, const void *from, int n, +			void **fault_addr, void **fault_catcher) +{ +	struct tt_regs save = TASK_REGS(get_current())->tt; +	unsigned long fault; +	int faulted; + +	fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, +			       __do_copy, &faulted); +	TASK_REGS(get_current())->tt = save; + +	if(!faulted) return(0); +	else return(n - (fault - (unsigned long) from)); +} + +static void __do_strncpy(void *dst, const void *src, int count) +{ +	strncpy(dst, src, count); +}	 + +int __do_strncpy_from_user(char *dst, const char *src, unsigned long count, +			   void **fault_addr, void **fault_catcher) +{ +	struct tt_regs save = TASK_REGS(get_current())->tt; +	unsigned long fault; +	int faulted; + +	fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher, +			       __do_strncpy, &faulted); +	TASK_REGS(get_current())->tt = save; + +	if(!faulted) return(strlen(dst)); +	else return(-1); +} + +static void __do_clear(void *to, const void *from, int n) +{ +	memset(to, 0, n); +}	 + +int __do_clear_user(void *mem, unsigned long len, +		    void **fault_addr, void **fault_catcher) +{ +	struct tt_regs save = TASK_REGS(get_current())->tt; +	unsigned long fault; +	int faulted; + +	fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher, +			       __do_clear, &faulted); +	TASK_REGS(get_current())->tt = save; + +	if(!faulted) return(0); +	else return(len - (fault - (unsigned long) mem)); +} + +int __do_strnlen_user(const char *str, unsigned long n, +		      void **fault_addr, void **fault_catcher) +{ +	struct tt_regs save = TASK_REGS(get_current())->tt; +	int ret; +	unsigned long *faddrp = (unsigned long *)fault_addr; +	sigjmp_buf jbuf; + +	*fault_catcher = &jbuf; +	if(sigsetjmp(jbuf, 1) == 0) +		ret = strlen(str) + 1; +	else ret = *faddrp - (unsigned long) str; + +	*fault_addr = NULL; +	*fault_catcher = NULL; + +	TASK_REGS(get_current())->tt = save; +	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/tt/unmap.c b/arch/um/kernel/tt/unmap.c new file mode 100644 index 00000000000..3f7aecdbe53 --- /dev/null +++ b/arch/um/kernel/tt/unmap.c @@ -0,0 +1,31 @@ +/*  + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <sys/mman.h> + +int switcheroo(int fd, int prot, void *from, void *to, int size) +{ +	if(munmap(to, size) < 0){ +		return(-1); +	} +	if(mmap(to, size, prot,	MAP_SHARED | MAP_FIXED, fd, 0) != to){ +		return(-1); +	} +	if(munmap(from, size) < 0){ +		return(-1); +	} +	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/kernel/tty_log.c b/arch/um/kernel/tty_log.c new file mode 100644 index 00000000000..9ada656f68c --- /dev/null +++ b/arch/um/kernel/tty_log.c @@ -0,0 +1,230 @@ +/*  + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) and  + * geoffrey hing <ghing@net.ohio-state.edu> + * Licensed under the GPL + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include "init.h" +#include "user.h" +#include "kern_util.h" +#include "os.h" + +#define TTY_LOG_DIR "./" + +/* Set early in boot and then unchanged */ +static char *tty_log_dir = TTY_LOG_DIR; +static int tty_log_fd = -1; + +#define TTY_LOG_OPEN 1 +#define TTY_LOG_CLOSE 2 +#define TTY_LOG_WRITE 3 +#define TTY_LOG_EXEC 4 + +#define TTY_READ 1 +#define TTY_WRITE 2 + +struct tty_log_buf { +	int what; +	unsigned long tty; +	int len; +	int direction; +	unsigned long sec; +	unsigned long usec; +}; + +int open_tty_log(void *tty, void *current_tty) +{ +	struct timeval tv; +	struct tty_log_buf data; +	char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")]; +	int fd; + +	gettimeofday(&tv, NULL); +	if(tty_log_fd != -1){ +		data = ((struct tty_log_buf) { .what 	= TTY_LOG_OPEN, +					       .tty  = (unsigned long) tty, +					       .len  = sizeof(current_tty), +					       .direction = 0, +					       .sec = tv.tv_sec, +					       .usec = tv.tv_usec } ); +		os_write_file(tty_log_fd, &data, sizeof(data)); +		os_write_file(tty_log_fd, ¤t_tty, data.len); +		return(tty_log_fd); +	} + +	sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec,  + 		(unsigned int) tv.tv_usec); + +	fd = os_open_file(buf, of_append(of_create(of_rdwr(OPENFLAGS()))), +			  0644); +	if(fd < 0){ +		printk("open_tty_log : couldn't open '%s', errno = %d\n", +		       buf, -fd); +	} +	return(fd); +} + +void close_tty_log(int fd, void *tty) +{ +	struct tty_log_buf data; +	struct timeval tv; + +	if(tty_log_fd != -1){ +		gettimeofday(&tv, NULL); +		data = ((struct tty_log_buf) { .what 	= TTY_LOG_CLOSE, +					       .tty  = (unsigned long) tty, +					       .len  = 0, +					       .direction = 0, +					       .sec = tv.tv_sec, +					       .usec = tv.tv_usec } ); +		os_write_file(tty_log_fd, &data, sizeof(data)); +		return; +	} +	os_close_file(fd); +} + +static int log_chunk(int fd, const char *buf, int len) +{ +	int total = 0, try, missed, n; +	char chunk[64]; + +	while(len > 0){ +		try = (len > sizeof(chunk)) ? sizeof(chunk) : len; +		missed = copy_from_user_proc(chunk, (char *) buf, try); +		try -= missed; +		n = os_write_file(fd, chunk, try); +		if(n != try) { +			if(n < 0) +				return(n); +			return(-EIO); +		} +		if(missed != 0) +			return(-EFAULT); + +		len -= try; +		total += try; +		buf += try; +	} + +	return(total); +} + +int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read) +{ +	struct timeval tv; +	struct tty_log_buf data; +	int direction; + +	if(fd == tty_log_fd){ +		gettimeofday(&tv, NULL); +		direction = is_read ? TTY_READ : TTY_WRITE; +		data = ((struct tty_log_buf) { .what 	= TTY_LOG_WRITE, +					       .tty  = (unsigned long) tty, +					       .len  = len, +					       .direction = direction, +					       .sec = tv.tv_sec, +					       .usec = tv.tv_usec } ); +		os_write_file(tty_log_fd, &data, sizeof(data)); +	} + +	return(log_chunk(fd, buf, len)); +} + +void log_exec(char **argv, void *tty) +{ +	struct timeval tv; +	struct tty_log_buf data; +	char **ptr,*arg; +	int len; + +	if(tty_log_fd == -1) return; + +	gettimeofday(&tv, NULL); + +	len = 0; +	for(ptr = argv; ; ptr++){ +		if(copy_from_user_proc(&arg, ptr, sizeof(arg))) +			return; +		if(arg == NULL) break; +		len += strlen_user_proc(arg); +	} + +	data = ((struct tty_log_buf) { .what 	= TTY_LOG_EXEC, +				       .tty  = (unsigned long) tty, +				       .len  = len, +				       .direction = 0, +				       .sec = tv.tv_sec, +				       .usec = tv.tv_usec } ); +	os_write_file(tty_log_fd, &data, sizeof(data)); + +	for(ptr = argv; ; ptr++){ +		if(copy_from_user_proc(&arg, ptr, sizeof(arg))) +			return; +		if(arg == NULL) break; +		log_chunk(tty_log_fd, arg, strlen_user_proc(arg)); +	} +} + +extern void register_tty_logger(int (*opener)(void *, void *), +				int (*writer)(int, const char *, int, +					      void *, int), +				void (*closer)(int, void *)); + +static int register_logger(void) +{ +	register_tty_logger(open_tty_log, write_tty_log, close_tty_log); +	return(0); +} + +__uml_initcall(register_logger); + +static int __init set_tty_log_dir(char *name, int *add) +{ +	tty_log_dir = name; +	return 0; +} + +__uml_setup("tty_log_dir=", set_tty_log_dir, +"tty_log_dir=<directory>\n" +"    This is used to specify the directory where the logs of all pty\n" +"    data from this UML machine will be written.\n\n" +); + +static int __init set_tty_log_fd(char *name, int *add) +{ +	char *end; + +	tty_log_fd = strtoul(name, &end, 0); +	if((*end != '\0') || (end == name)){ +		printf("set_tty_log_fd - strtoul failed on '%s'\n", name); +		tty_log_fd = -1; +	} + +	*add = 0; +	return 0; +} + +__uml_setup("tty_log_fd=", set_tty_log_fd, +"tty_log_fd=<fd>\n" +"    This is used to specify a preconfigured file descriptor to which all\n" +"    tty data will be written.  Preconfigure the descriptor with something\n" +"    like '10>tty_log tty_log_fd=10'.\n\n" +); + + +/* + * 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/uaccess_user.c b/arch/um/kernel/uaccess_user.c new file mode 100644 index 00000000000..d035257ed0a --- /dev/null +++ b/arch/um/kernel/uaccess_user.c @@ -0,0 +1,64 @@ +/*  + * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <setjmp.h> +#include <string.h> + +/* These are here rather than tt/uaccess.c because skas mode needs them in + * order to do SIGBUS recovery when a tmpfs mount runs out of room. + */ + +unsigned long __do_user_copy(void *to, const void *from, int n, +			     void **fault_addr, void **fault_catcher, +			     void (*op)(void *to, const void *from, +					int n), int *faulted_out) +{ +	unsigned long *faddrp = (unsigned long *) fault_addr, ret; + +	sigjmp_buf jbuf; +	*fault_catcher = &jbuf; +	if(sigsetjmp(jbuf, 1) == 0){ +		(*op)(to, from, n); +		ret = 0; +		*faulted_out = 0; +	}  +	else { +		ret = *faddrp; +		*faulted_out = 1; +	} +	*fault_addr = NULL; +	*fault_catcher = NULL; +	return ret; +} + +void __do_copy(void *to, const void *from, int n) +{ +	memcpy(to, from, n); +}	 + + +int __do_copy_to_user(void *to, const void *from, int n, +		      void **fault_addr, void **fault_catcher) +{ +	unsigned long fault; +	int faulted; + +	fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, +			       __do_copy, &faulted); +	if(!faulted) return(0); +	else return(n - (fault - (unsigned long) to)); +} + +/* + * 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/um_arch.c b/arch/um/kernel/um_arch.c new file mode 100644 index 00000000000..5c49d88eed3 --- /dev/null +++ b/arch/um/kernel/um_arch.c @@ -0,0 +1,467 @@ +/*  + * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/notifier.h" +#include "linux/mm.h" +#include "linux/types.h" +#include "linux/tty.h" +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/spinlock.h" +#include "linux/utsname.h" +#include "linux/sysrq.h" +#include "linux/seq_file.h" +#include "linux/delay.h" +#include "linux/module.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/ptrace.h" +#include "asm/elf.h" +#include "asm/user.h" +#include "ubd_user.h" +#include "asm/current.h" +#include "asm/setup.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mem_user.h" +#include "mem.h" +#include "umid.h" +#include "initrd.h" +#include "init.h" +#include "os.h" +#include "choose-mode.h" +#include "mode_kern.h" +#include "mode.h" + +#define DEFAULT_COMMAND_LINE "root=98:0" + +/* Changed in linux_main and setup_arch, which run before SMP is started */ +char command_line[COMMAND_LINE_SIZE] = { 0 }; + +void add_arg(char *arg) +{ +	if (strlen(command_line) + strlen(arg) + 1 > COMMAND_LINE_SIZE) { +		printf("add_arg: Too many command line arguments!\n"); +		exit(1); +	} +	if(strlen(command_line) > 0) +		strcat(command_line, " "); +	strcat(command_line, arg); +} + +struct cpuinfo_um boot_cpu_data = {  +	.loops_per_jiffy	= 0, +	.ipi_pipe		= { -1, -1 } +}; + +unsigned long thread_saved_pc(struct task_struct *task) +{ +	return(os_process_pc(CHOOSE_MODE_PROC(thread_pid_tt, thread_pid_skas, +					      task))); +} + +static int show_cpuinfo(struct seq_file *m, void *v) +{ +	int index = 0; + +#ifdef CONFIG_SMP +	index = (struct cpuinfo_um *) v - cpu_data; +	if (!cpu_online(index)) +		return 0; +#endif + +	seq_printf(m, "processor\t: %d\n", index); +	seq_printf(m, "vendor_id\t: User Mode Linux\n"); +	seq_printf(m, "model name\t: UML\n"); +	seq_printf(m, "mode\t\t: %s\n", CHOOSE_MODE("tt", "skas")); +	seq_printf(m, "host\t\t: %s\n", host_info); +	seq_printf(m, "bogomips\t: %lu.%02lu\n\n", +		   loops_per_jiffy/(500000/HZ), +		   (loops_per_jiffy/(5000/HZ)) % 100); + +	return(0); +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ +	return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ +	++*pos; +	return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { +	.start	= c_start, +	.next	= c_next, +	.stop	= c_stop, +	.show	= show_cpuinfo, +}; + +pte_t * __bad_pagetable(void) +{ +	panic("Someone should implement __bad_pagetable"); +	return(NULL); +} + +/* Set in linux_main */ +unsigned long host_task_size; +unsigned long task_size; + +unsigned long uml_start; + +/* Set in early boot */ +unsigned long uml_physmem; +unsigned long uml_reserved; +unsigned long start_vm; +unsigned long end_vm; +int ncpus = 1; + +#ifdef CONFIG_MODE_TT +/* Pointer set in linux_main, the array itself is private to each thread, + * and changed at address space creation time so this poses no concurrency + * problems. + */ +static char *argv1_begin = NULL; +static char *argv1_end = NULL; +#endif + +/* Set in early boot */ +static int have_root __initdata = 0; +long physmem_size = 32 * 1024 * 1024; + +void set_cmdline(char *cmd) +{ +#ifdef CONFIG_MODE_TT +	char *umid, *ptr; + +	if(CHOOSE_MODE(honeypot, 0)) return; + +	umid = get_umid(1); +	if(umid != NULL){ +		snprintf(argv1_begin,  +			 (argv1_end - argv1_begin) * sizeof(*ptr),  +			 "(%s) ", umid); +		ptr = &argv1_begin[strlen(argv1_begin)]; +	} +	else ptr = argv1_begin; + +	snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), "[%s]", cmd); +	memset(argv1_begin + strlen(argv1_begin), '\0',  +	       argv1_end - argv1_begin - strlen(argv1_begin)); +#endif +} + +static char *usage_string =  +"User Mode Linux v%s\n" +"	available at http://user-mode-linux.sourceforge.net/\n\n"; + +static int __init uml_version_setup(char *line, int *add) +{ +	printf("%s\n", system_utsname.release); +	exit(0); + +	return 0; +} + +__uml_setup("--version", uml_version_setup, +"--version\n" +"    Prints the version number of the kernel.\n\n" +); + +static int __init uml_root_setup(char *line, int *add) +{ +	have_root = 1; +	return 0; +} + +__uml_setup("root=", uml_root_setup, +"root=<file containing the root fs>\n" +"    This is actually used by the generic kernel in exactly the same\n" +"    way as in any other kernel. If you configure a number of block\n" +"    devices and want to boot off something other than ubd0, you \n" +"    would use something like:\n" +"        root=/dev/ubd5\n\n" +); + +#ifdef CONFIG_SMP +static int __init uml_ncpus_setup(char *line, int *add) +{ +       if (!sscanf(line, "%d", &ncpus)) { +               printf("Couldn't parse [%s]\n", line); +               return -1; +       } + +       return 0; +} + +__uml_setup("ncpus=", uml_ncpus_setup, +"ncpus=<# of desired CPUs>\n" +"    This tells an SMP kernel how many virtual processors to start.\n\n"  +); +#endif + +static int force_tt = 0; + +#if defined(CONFIG_MODE_TT) && defined(CONFIG_MODE_SKAS) +#define DEFAULT_TT 0 + +static int __init mode_tt_setup(char *line, int *add) +{ +	force_tt = 1; +	return(0); +} + +#else +#ifdef CONFIG_MODE_SKAS + +#define DEFAULT_TT 0 + +static int __init mode_tt_setup(char *line, int *add) +{ +	printf("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n"); +	return(0); +} + +#else +#ifdef CONFIG_MODE_TT + +#define DEFAULT_TT 1 + +static int __init mode_tt_setup(char *line, int *add) +{ +	printf("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n"); +	return(0); +} + +#else + +#error Either CONFIG_MODE_TT or CONFIG_MODE_SKAS must be enabled + +#endif +#endif +#endif + +__uml_setup("mode=tt", mode_tt_setup, +"mode=tt\n" +"    When both CONFIG_MODE_TT and CONFIG_MODE_SKAS are enabled, this option\n" +"    forces UML to run in tt (tracing thread) mode.  It is not the default\n" +"    because it's slower and less secure than skas mode.\n\n" +); + +int mode_tt = DEFAULT_TT; + +static int __init Usage(char *line, int *add) +{ + 	const char **p; + +	printf(usage_string, system_utsname.release); + 	p = &__uml_help_start; + 	while (p < &__uml_help_end) { + 		printf("%s", *p); + 		p++; + 	} +	exit(0); + +	return 0; +} + +__uml_setup("--help", Usage, +"--help\n" +"    Prints this message.\n\n" +); + +static int __init uml_checksetup(char *line, int *add) +{ +	struct uml_param *p; + +	p = &__uml_setup_start; +	while(p < &__uml_setup_end) { +		int n; + +		n = strlen(p->str); +		if(!strncmp(line, p->str, n)){ +			if (p->setup_func(line + n, add)) return 1; +		} +		p++; +	} +	return 0; +} + +static void __init uml_postsetup(void) +{ +	initcall_t *p; + +	p = &__uml_postsetup_start; +	while(p < &__uml_postsetup_end){ +		(*p)(); +		p++; +	} +	return; +} + +/* Set during early boot */ +unsigned long brk_start; +unsigned long end_iomem; +EXPORT_SYMBOL(end_iomem); + +#define MIN_VMALLOC (32 * 1024 * 1024) + +int linux_main(int argc, char **argv) +{ +	unsigned long avail, diff; +	unsigned long virtmem_size, max_physmem; +	unsigned int i, add; + +	for (i = 1; i < argc; i++){ +		if((i == 1) && (argv[i][0] == ' ')) continue; +		add = 1; +		uml_checksetup(argv[i], &add); +		if (add) +			add_arg(argv[i]); +	} +	if(have_root == 0) +		add_arg(DEFAULT_COMMAND_LINE); + +	mode_tt = force_tt ? 1 : !can_do_skas(); +#ifndef CONFIG_MODE_TT +	if (mode_tt) { +		/*Since CONFIG_MODE_TT is #undef'ed, force_tt cannot be 1. So, +		 * can_do_skas() returned 0, and the message is correct. */ +		printf("Support for TT mode is disabled, and no SKAS support is present on the host.\n"); +		exit(1); +	} +#endif +	uml_start = CHOOSE_MODE_PROC(set_task_sizes_tt, set_task_sizes_skas, 0, +				     &host_task_size, &task_size); + +	/* Need to check this early because mmapping happens before the +	 * kernel is running. +	 */ +	check_tmpexec(); + +	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 +	so they actually get what they asked for. This should +	add zero for non-exec shield users */ + +	diff = UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); +	if(diff > 1024 * 1024){ +		printf("Adding %ld bytes to physical memory to account for " +		       "exec-shield gap\n", diff); +		physmem_size += UML_ROUND_UP(brk_start) - UML_ROUND_UP(&_end); +	} + +	uml_physmem = uml_start; + +	/* Reserve up to 4M after the current brk */ +	uml_reserved = ROUND_4M(brk_start) + (1 << 22); + +	setup_machinename(system_utsname.machine); + +#ifdef CONFIG_MODE_TT +	argv1_begin = argv[1]; +	argv1_end = &argv[1][strlen(argv[1])]; +#endif +   +	highmem = 0; +	iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; +	max_physmem = get_kmem_end() - uml_physmem - iomem_size - MIN_VMALLOC; + +	/* Zones have to begin on a 1 << MAX_ORDER page boundary, +	 * so this makes sure that's true for highmem +	 */ +	max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1); +	if(physmem_size + iomem_size > max_physmem){ +		highmem = physmem_size + iomem_size - max_physmem; +		physmem_size -= highmem; +#ifndef CONFIG_HIGHMEM +		highmem = 0; +		printf("CONFIG_HIGHMEM not enabled - physical memory shrunk " +		       "to %ld bytes\n", physmem_size); +#endif +	} + +	high_physmem = uml_physmem + physmem_size; +	end_iomem = high_physmem + iomem_size; +	high_memory = (void *) end_iomem; + +	start_vm = VMALLOC_START; + +	setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); +	if(init_maps(physmem_size, iomem_size, highmem)){ +		printf("Failed to allocate mem_map for %ld bytes of physical " +		       "memory and %ld bytes of highmem\n", physmem_size, +		       highmem); +		exit(1); +	} + +	virtmem_size = physmem_size; +	avail = get_kmem_end() - start_vm; +	if(physmem_size > avail) virtmem_size = avail; +	end_vm = start_vm + virtmem_size; + +	if(virtmem_size < physmem_size) +		printf("Kernel virtual memory size shrunk to %ld bytes\n", +		       virtmem_size); + +  	uml_postsetup(); + +	task_protections((unsigned long) &init_thread_info); +	os_flush_stdout(); + +	return(CHOOSE_MODE(start_uml_tt(), start_uml_skas())); +} + +extern int uml_exitcode; + +static int panic_exit(struct notifier_block *self, unsigned long unused1, +		      void *unused2) +{ +	bust_spinlocks(1); +	show_regs(&(current->thread.regs)); +	bust_spinlocks(0); +	uml_exitcode = 1; +	machine_halt(); +	return(0); +} + +static struct notifier_block panic_exit_notifier = { +	.notifier_call 		= panic_exit, +	.next 			= NULL, +	.priority 		= 0 +}; + +void __init setup_arch(char **cmdline_p) +{ +	notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); +	paging_init(); + 	strlcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); + 	*cmdline_p = command_line; +	setup_hostinfo(); +} + +void __init check_bugs(void) +{ +	arch_check_bugs(); +	check_ptrace(); +	check_sigio(); +	check_devanon(); +} + +void apply_alternatives(void *start, void *end) +{ +} diff --git a/arch/um/kernel/umid.c b/arch/um/kernel/umid.c new file mode 100644 index 00000000000..186c2888501 --- /dev/null +++ b/arch/um/kernel/umid.c @@ -0,0 +1,325 @@ +/*  + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * 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 "init.h" +#include "os.h" +#include "user_util.h" +#include "choose-mode.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; +static int umid_inited = 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, ...)) +{ +	if(umid_inited){ +		(*printer)("Unique machine name can't be set twice\n"); +		return(-1); +	} + +	if(strlen(name) > UMID_LEN - 1) +		(*printer)("Unique machine name is being truncated to %d " +			   "characters\n", UMID_LEN); +	strlcpy(umid, name, sizeof(umid)); + +	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)); +} + +__uml_setup("umid=", set_umid_arg, +"umid=<name>\n" +"    This is used to assign a unique identity to this UML machine and\n" +"    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 int __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 0; + +	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 0; +	} + +	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); +	return 0; +} + +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_inited) 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); +} + +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++; +	} +	len = strlen(dir); +	strncat(dir, uml_dir, sizeof(dir) - len); +	len = strlen(dir); +	if((len > 0) && (len < sizeof(dir) - 1) && (dir[len - 1] != '/')){ +		dir[len] = '/'; +		dir[len + 1] = '\0'; +	} + +	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); +				return(-1); +			} +			err = mkdir(tmp, 0777); +		} +	} +	if(err < 0){ +		(*printer)("Failed to create %s - errno = %d\n", umid, errno); +		return(-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); +	return create_pid_file(); +} +__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/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S new file mode 100644 index 00000000000..76eadb30918 --- /dev/null +++ b/arch/um/kernel/uml.lds.S @@ -0,0 +1,106 @@ +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT(ELF_FORMAT) +OUTPUT_ARCH(ELF_ARCH) +ENTRY(_start) +jiffies = jiffies_64; + +SECTIONS +{ +  /*This must contain the right address - not quite the default ELF one.*/ +  PROVIDE (__executable_start = START); +  . = START + SIZEOF_HEADERS; + +  /* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start +   * is remapped.*/ +  __binary_start = .; +#ifdef MODE_TT +  .thread_private : { +    __start_thread_private = .; +    errno = .; +    . += 4; +    arch/um/kernel/tt/unmap_fin.o (.data) +    __end_thread_private = .; +  } +  . = ALIGN(4096); +  .remap : { arch/um/kernel/tt/unmap_fin.o (.text) } + +  /* We want it only if we are in MODE_TT. In both cases, however, when MODE_TT +   * is off the resulting binary segfaults.*/ + +  . = ALIGN(4096);		/* Init code and data */ +#endif + +  _stext = .; +  __init_begin = .; +  .init.text : { +	_sinittext = .; +	*(.init.text) +	_einittext = .; +  } +  . = ALIGN(4096); +  .text      : +  { +    *(.text) +    SCHED_TEXT +    LOCK_TEXT +    *(.fixup) +    /* .gnu.warning sections are handled specially by elf32.em.  */ +    *(.gnu.warning) +    *(.gnu.linkonce.t*) +  } + +  #include "asm/common.lds.S" + +  init.data : { *(init.data) } +  .data    : +  { +    . = ALIGN(KERNEL_STACK_SIZE);		/* init_task */ +    *(.data.init_task) +    *(.data) +    *(.gnu.linkonce.d*) +    CONSTRUCTORS +  } +  .data1   : { *(.data1) } +  .ctors         : +  { +    *(.ctors) +  } +  .dtors         : +  { +    *(.dtors) +  } + +  .got           : { *(.got.plt) *(.got) } +  .dynamic       : { *(.dynamic) } +  /* We want the small data sections together, so single-instruction offsets +     can access them all, and initialized data all before uninitialized, so +     we can shorten the on-disk segment size.  */ +  .sdata     : { *(.sdata) } +  _edata  =  .; +  PROVIDE (edata = .); +  . = ALIGN(0x1000); +  .sbss      : +  { +   __bss_start = .; +   PROVIDE(_bss_start = .); +   *(.sbss) +   *(.scommon) +  } +  .bss       : +  { +   *(.dynbss) +   *(.bss) +   *(COMMON) +  } +  _end = . ; +  PROVIDE (end = .); +  /* Stabs debugging sections.  */ +  .stab 0 : { *(.stab) } +  .stabstr 0 : { *(.stabstr) } +  .stab.excl 0 : { *(.stab.excl) } +  .stab.exclstr 0 : { *(.stab.exclstr) } +  .stab.index 0 : { *(.stab.index) } +  .stab.indexstr 0 : { *(.stab.indexstr) } +  .comment 0 : { *(.comment) } +} diff --git a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c new file mode 100644 index 00000000000..954ff67cc8b --- /dev/null +++ b/arch/um/kernel/user_util.c @@ -0,0 +1,173 @@ +/*  + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <setjmp.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <sys/param.h> +#include <sys/time.h> +#include "asm/types.h" +#include <ctype.h> +#include <signal.h> +#include <wait.h> +#include <errno.h> +#include <stdarg.h> +#include <sched.h> +#include <termios.h> +#include <string.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "mem_user.h" +#include "init.h" +#include "helper.h" +#include "ptrace_user.h" +#include "uml-config.h" + +void stop(void) +{ +	while(1) sleep(1000000); +} + +void stack_protections(unsigned long address) +{ +	int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + +        if(mprotect((void *) address, page_size(), prot) < 0) +		panic("protecting stack failed, errno = %d", errno); +} + +void task_protections(unsigned long address) +{ +	unsigned long guard = address + page_size(); +	unsigned long stack = guard + page_size(); +	int prot = 0, pages; + +#ifdef notdef +	if(mprotect((void *) stack, page_size(), prot) < 0) +		panic("protecting guard page failed, errno = %d", errno); +#endif +	pages = (1 << UML_CONFIG_KERNEL_STACK_ORDER) - 2; +	prot = PROT_READ | PROT_WRITE | PROT_EXEC; +	if(mprotect((void *) stack, pages * page_size(), prot) < 0) +		panic("protecting stack failed, errno = %d", errno); +} + +int 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; +	int err; + +	CATCH_EINTR(err = tcgetattr(fd, &tt)); +	if (err < 0) { +			printk("tcgetattr failed, errno = %d\n", errno); +		return(-errno); +	} + +	cfmakeraw(&tt); + + 	CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); +	if (err < 0) { +			printk("tcsetattr failed, errno = %d\n", errno); +		return(-errno); +	} + +	/* XXX tcsetattr could have applied only some changes +	 * (and cfmakeraw() is a set of changes) */ +	return(0); +} + +void setup_machinename(char *machine_out) +{ +	struct utsname host; + +	uname(&host); +	strcpy(machine_out, host.machine); +} + +char host_info[(_UTSNAME_LENGTH + 1) * 4 + _UTSNAME_NODENAME_LENGTH + 1]; + +void setup_hostinfo(void) +{ +	struct utsname host; + +	uname(&host); +	sprintf(host_info, "%s %s %s %s %s", host.sysname, host.nodename, +		host.release, host.version, host.machine); +} + +int setjmp_wrapper(void (*proc)(void *, void *), ...) +{ +        va_list args; +	sigjmp_buf buf; +	int n; + +	n = sigsetjmp(buf, 1); +	if(n == 0){ +		va_start(args, proc); +		(*proc)(&buf, &args); +	} +	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: + */  |