diff options
Diffstat (limited to 'arch/um')
-rw-r--r-- | arch/um/Kconfig | 3 | ||||
-rw-r--r-- | arch/um/defconfig | 2 | ||||
-rw-r--r-- | arch/um/include/common-offsets.h | 2 | ||||
-rw-r--r-- | arch/um/include/kern_util.h | 3 | ||||
-rw-r--r-- | arch/um/include/os.h | 1 | ||||
-rw-r--r-- | arch/um/kernel/dyn.lds.S | 2 | ||||
-rw-r--r-- | arch/um/kernel/init_task.c | 30 | ||||
-rw-r--r-- | arch/um/kernel/irq.c | 115 | ||||
-rw-r--r-- | arch/um/kernel/skas/process.c | 4 | ||||
-rw-r--r-- | arch/um/kernel/tt/exec_kern.c | 2 | ||||
-rw-r--r-- | arch/um/kernel/tt/process_kern.c | 2 | ||||
-rw-r--r-- | arch/um/kernel/um_arch.c | 2 | ||||
-rw-r--r-- | arch/um/kernel/uml.lds.S | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/signal.c | 50 | ||||
-rw-r--r-- | arch/um/os-Linux/skas/process.c | 13 | ||||
-rw-r--r-- | arch/um/os-Linux/sys-i386/signal.c | 8 | ||||
-rw-r--r-- | arch/um/os-Linux/sys-x86_64/signal.c | 6 | ||||
-rw-r--r-- | arch/um/os-Linux/util.c | 23 |
18 files changed, 206 insertions, 64 deletions
diff --git a/arch/um/Kconfig b/arch/um/Kconfig index b9c0f307a8f..c504312219b 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -277,7 +277,8 @@ config HIGHMEM config KERNEL_STACK_ORDER int "Kernel stack size order" - default 2 + default 1 if 64BIT + default 0 if !64BIT help This option determines the size of UML kernel stacks. They will be 1 << order pages. The default is OK unless you're running Valgrind diff --git a/arch/um/defconfig b/arch/um/defconfig index f938fa82214..a54d0efecae 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig @@ -86,7 +86,7 @@ CONFIG_MCONSOLE=y # CONFIG_MAGIC_SYSRQ is not set CONFIG_NEST_LEVEL=0 # CONFIG_HIGHMEM is not set -CONFIG_KERNEL_STACK_ORDER=2 +CONFIG_KERNEL_STACK_ORDER=0 CONFIG_UML_REAL_TIME_CLOCK=y # diff --git a/arch/um/include/common-offsets.h b/arch/um/include/common-offsets.h index 5593a802708..541f4a8ca51 100644 --- a/arch/um/include/common-offsets.h +++ b/arch/um/include/common-offsets.h @@ -28,3 +28,5 @@ DEFINE(UM_NR_CPUS, NR_CPUS); /* For crypto assembler code. */ DEFINE(crypto_tfm_ctx_offset, offsetof(struct crypto_tfm, __crt_ctx)); + +DEFINE(UM_THREAD_SIZE, THREAD_SIZE); diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h index 50a49691e0e..8d7f7c1cb9c 100644 --- a/arch/um/include/kern_util.h +++ b/arch/um/include/kern_util.h @@ -117,4 +117,7 @@ extern void sigio_handler(int sig, union uml_pt_regs *regs); extern void copy_sc(union uml_pt_regs *regs, void *from); +unsigned long to_irq_stack(int sig, unsigned long *mask_out); +unsigned long from_irq_stack(int nested); + #endif diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 688d181b5f8..4d9fb26387d 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -272,7 +272,6 @@ extern void do_longjmp(void *p, int val); /* util.c */ extern void stack_protections(unsigned long address); -extern void task_protections(unsigned long address); extern int raw(int fd); extern void setup_machinename(char *machine_out); extern void setup_hostinfo(char *buf, int len); diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S index e36f92b463c..87a4e4427d8 100644 --- a/arch/um/kernel/dyn.lds.S +++ b/arch/um/kernel/dyn.lds.S @@ -97,6 +97,8 @@ SECTIONS .data : { . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ *(.data.init_task) + . = ALIGN(KERNEL_STACK_SIZE); + *(.data.init_irqstack) *(.data .data.* .gnu.linkonce.d.*) SORT(CONSTRUCTORS) } diff --git a/arch/um/kernel/init_task.c b/arch/um/kernel/init_task.c index cda91aa8e70..d4f1d1ab252 100644 --- a/arch/um/kernel/init_task.c +++ b/arch/um/kernel/init_task.c @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +/* + * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,intel.linux}.com) * Licensed under the GPL */ @@ -33,28 +33,20 @@ EXPORT_SYMBOL(init_task); /* * Initial thread structure. * - * We need to make sure that this is 16384-byte aligned due to the + * We need to make sure that this is 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) }; +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +union thread_union cpu0_irqstack + __attribute__((__section__(".data.init_irqstack"))) = + { INIT_THREAD_INFO(init_task) }; void unprotect_stack(unsigned long stack) { - os_protect_memory((void *) stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE, - 1, 1, 0); + os_protect_memory((void *) stack, THREAD_SIZE, 1, 1, 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 index 8f2ed369031..dba04d88b43 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) * Licensed under the GPL * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: @@ -32,6 +32,7 @@ #include "sigio.h" #include "um_malloc.h" #include "misc_constants.h" +#include "as-layout.h" /* * Generic, controller-independent functions: @@ -53,7 +54,7 @@ int show_interrupts(struct seq_file *p, void *v) if (i < NR_IRQS) { spin_lock_irqsave(&irq_desc[i].lock, flags); action = irq_desc[i].action; - if (!action) + if (!action) goto skip; seq_printf(p, "%3d: ",i); #ifndef CONFIG_SMP @@ -468,3 +469,113 @@ int init_aio_irq(int irq, char *name, irq_handler_t handler) out: return err; } + +/* + * IRQ stack entry and exit: + * + * Unlike i386, UML doesn't receive IRQs on the normal kernel stack + * and switch over to the IRQ stack after some preparation. We use + * sigaltstack to receive signals on a separate stack from the start. + * These two functions make sure the rest of the kernel won't be too + * upset by being on a different stack. The IRQ stack has a + * thread_info structure at the bottom so that current et al continue + * to work. + * + * to_irq_stack copies the current task's thread_info to the IRQ stack + * thread_info and sets the tasks's stack to point to the IRQ stack. + * + * from_irq_stack copies the thread_info struct back (flags may have + * been modified) and resets the task's stack pointer. + * + * Tricky bits - + * + * What happens when two signals race each other? UML doesn't block + * signals with sigprocmask, SA_DEFER, or sa_mask, so a second signal + * could arrive while a previous one is still setting up the + * thread_info. + * + * There are three cases - + * The first interrupt on the stack - sets up the thread_info and + * handles the interrupt + * A nested interrupt interrupting the copying of the thread_info - + * can't handle the interrupt, as the stack is in an unknown state + * A nested interrupt not interrupting the copying of the + * thread_info - doesn't do any setup, just handles the interrupt + * + * The first job is to figure out whether we interrupted stack setup. + * This is done by xchging the signal mask with thread_info->pending. + * If the value that comes back is zero, then there is no setup in + * progress, and the interrupt can be handled. If the value is + * non-zero, then there is stack setup in progress. In order to have + * the interrupt handled, we leave our signal in the mask, and it will + * be handled by the upper handler after it has set up the stack. + * + * Next is to figure out whether we are the outer handler or a nested + * one. As part of setting up the stack, thread_info->real_thread is + * set to non-NULL (and is reset to NULL on exit). This is the + * nesting indicator. If it is non-NULL, then the stack is already + * set up and the handler can run. + */ + +static unsigned long pending_mask; + +unsigned long to_irq_stack(int sig, unsigned long *mask_out) +{ + struct thread_info *ti; + unsigned long mask, old; + int nested; + + mask = xchg(&pending_mask, 1 << sig); + if(mask != 0){ + /* If any interrupts come in at this point, we want to + * make sure that their bits aren't lost by our + * putting our bit in. So, this loop accumulates bits + * until xchg returns the same value that we put in. + * When that happens, there were no new interrupts, + * and pending_mask contains a bit for each interrupt + * that came in. + */ + old = 1 << sig; + do { + old |= mask; + mask = xchg(&pending_mask, old); + } while(mask != old); + return 1; + } + + ti = current_thread_info(); + nested = (ti->real_thread != NULL); + if(!nested){ + struct task_struct *task; + struct thread_info *tti; + + task = cpu_tasks[ti->cpu].task; + tti = task_thread_info(task); + *ti = *tti; + ti->real_thread = tti; + task->stack = ti; + } + + mask = xchg(&pending_mask, 0); + *mask_out |= mask | nested; + return 0; +} + +unsigned long from_irq_stack(int nested) +{ + struct thread_info *ti, *to; + unsigned long mask; + + ti = current_thread_info(); + + pending_mask = 1; + + to = ti->real_thread; + current->stack = to; + ti->real_thread = NULL; + *to = *ti; + + mask = xchg(&pending_mask, 0); + return mask & ~1; +} + diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index a96ae1a0610..2a69a7ce579 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -163,8 +163,12 @@ static int start_kernel_proc(void *unused) extern int userspace_pid[]; +extern char cpu0_irqstack[]; + int start_uml_skas(void) { + stack_protections((unsigned long) &cpu0_irqstack); + set_sigstack(cpu0_irqstack, THREAD_SIZE); if(proc_mm) userspace_pid[0] = start_userspace(0); diff --git a/arch/um/kernel/tt/exec_kern.c b/arch/um/kernel/tt/exec_kern.c index 98e21743e60..40126cb5180 100644 --- a/arch/um/kernel/tt/exec_kern.c +++ b/arch/um/kernel/tt/exec_kern.c @@ -57,7 +57,7 @@ void flush_thread_tt(void) enable_timer(); free_page(stack); protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); - task_protections((unsigned long) current_thread); + stack_protections((unsigned long) current_thread); force_flush_all(); unblock_signals(); } diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c index c631303cb80..74347adf81b 100644 --- a/arch/um/kernel/tt/process_kern.c +++ b/arch/um/kernel/tt/process_kern.c @@ -209,7 +209,7 @@ void finish_fork_handler(int sig) if(current->mm != current->parent->mm) protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); - task_protections((unsigned long) current_thread); + stack_protections((unsigned long) current_thread); free_page(current->thread.temp_stack); local_irq_disable(); diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 1cf954a47fd..ecc458fe51b 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -459,7 +459,7 @@ int __init linux_main(int argc, char **argv) uml_postsetup(); - task_protections((unsigned long) &init_thread_info); + stack_protections((unsigned long) &init_thread_info); os_flush_stdout(); return CHOOSE_MODE(start_uml_tt(), start_uml_skas()); diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S index f6301274cf3..bc59f97e34d 100644 --- a/arch/um/kernel/uml.lds.S +++ b/arch/um/kernel/uml.lds.S @@ -59,6 +59,8 @@ SECTIONS { . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ *(.data.init_task) + . = ALIGN(KERNEL_STACK_SIZE); + *(.data.init_irqstack) *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 48d49341530..18e5c8b67eb 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -61,15 +61,19 @@ void sig_handler(int sig, struct sigcontext *sc) static void real_alarm_handler(int sig, struct sigcontext *sc) { + union uml_pt_regs regs; + if(sig == SIGALRM) switch_timers(0); - CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas, - sig, sc); + if(sc != NULL) + copy_sc(®s, sc); + regs.skas.is_user = 0; + unblock_signals(); + timer_handler(sig, ®s); if(sig == SIGALRM) switch_timers(1); - } void alarm_handler(int sig, struct sigcontext *sc) @@ -113,6 +117,46 @@ void remove_sigstack(void) void (*handlers[_NSIG])(int sig, struct sigcontext *sc); +void handle_signal(int sig, struct sigcontext *sc) +{ + unsigned long pending = 0; + + do { + int nested, bail; + + /* + * pending comes back with one bit set for each + * interrupt that arrived while setting up the stack, + * plus a bit for this interrupt, plus the zero bit is + * set if this is a nested interrupt. + * If bail is true, then we interrupted another + * handler setting up the stack. In this case, we + * have to return, and the upper handler will deal + * with this interrupt. + */ + bail = to_irq_stack(sig, &pending); + if(bail) + return; + + nested = pending & 1; + pending &= ~1; + + while((sig = ffs(pending)) != 0){ + sig--; + pending &= ~(1 << sig); + (*handlers[sig])(sig, sc); + } + + /* Again, pending comes back with a mask of signals + * that arrived while tearing down the stack. If this + * is non-zero, we just go back, set up the stack + * again, and handle the new interrupts. + */ + if(!nested) + pending = from_irq_stack(nested); + } while(pending); +} + extern void hard_handler(int sig); void set_handler(int sig, void (*handler)(int), int flags, ...) diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index 6a0e466d01e..f9d2f8545af 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -288,7 +288,8 @@ int start_userspace(unsigned long stub_stack) 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.*/ + /* To prevent races if using_sysemu changes under us.*/ + int local_using_sysemu; while(1){ restore_registers(pid, regs); @@ -296,7 +297,8 @@ void userspace(union uml_pt_regs *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)); + op = SELECT_PTRACE_OPERATION(local_using_sysemu, + singlestepping(NULL)); err = ptrace(op, pid, 0, 0); if(err) @@ -490,8 +492,8 @@ void map_stub_pages(int fd, unsigned long code, void new_thread(void *stack, jmp_buf *buf, void (*handler)(void)) { (*buf)[0].JB_IP = (unsigned long) handler; - (*buf)[0].JB_SP = (unsigned long) stack + - (PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) - sizeof(void *); + (*buf)[0].JB_SP = (unsigned long) stack + UM_THREAD_SIZE - + sizeof(void *); } #define INIT_JMP_NEW_THREAD 0 @@ -533,8 +535,7 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf) case INIT_JMP_NEW_THREAD: (*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler; (*switch_buf)[0].JB_SP = (unsigned long) stack + - (PAGE_SIZE << UML_CONFIG_KERNEL_STACK_ORDER) - - sizeof(void *); + UM_THREAD_SIZE - sizeof(void *); break; case INIT_JMP_CALLBACK: (*cb_proc)(cb_arg); diff --git a/arch/um/os-Linux/sys-i386/signal.c b/arch/um/os-Linux/sys-i386/signal.c index 0d3eae51835..f311609f93d 100644 --- a/arch/um/os-Linux/sys-i386/signal.c +++ b/arch/um/os-Linux/sys-i386/signal.c @@ -1,15 +1,13 @@ /* - * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include <signal.h> -extern void (*handlers[])(int sig, struct sigcontext *sc); +extern void handle_signal(int sig, struct sigcontext *sc); void hard_handler(int sig) { - struct sigcontext *sc = (struct sigcontext *) (&sig + 1); - - (*handlers[sig])(sig, sc); + handle_signal(sig, (struct sigcontext *) (&sig + 1)); } diff --git a/arch/um/os-Linux/sys-x86_64/signal.c b/arch/um/os-Linux/sys-x86_64/signal.c index 3f369e5f976..82a388822cd 100644 --- a/arch/um/os-Linux/sys-x86_64/signal.c +++ b/arch/um/os-Linux/sys-x86_64/signal.c @@ -1,16 +1,16 @@ /* - * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) + * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include <signal.h> -extern void (*handlers[])(int sig, struct sigcontext *sc); +extern void handle_signal(int sig, struct sigcontext *sc); void hard_handler(int sig) { struct ucontext *uc; asm("movq %%rdx, %0" : "=r" (uc)); - (*handlers[sig])(sig, (struct sigcontext *) &uc->uc_mcontext); + handle_signal(sig, (struct sigcontext *) &uc->uc_mcontext); } diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c index c307a89ed25..7cbcf484e13 100644 --- a/arch/um/os-Linux/util.c +++ b/arch/um/os-Linux/util.c @@ -33,25 +33,8 @@ void stack_protections(unsigned long address) { - int prot = PROT_READ | PROT_WRITE | PROT_EXEC; - - if(mprotect((void *) address, UM_KERN_PAGE_SIZE, prot) < 0) - panic("protecting stack failed, errno = %d", errno); -} - -void task_protections(unsigned long address) -{ - unsigned long guard = address + UM_KERN_PAGE_SIZE; - unsigned long stack = guard + UM_KERN_PAGE_SIZE; - int prot = 0, pages; - -#ifdef notdef - if(mprotect((void *) stack, UM_KERN_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 * UM_KERN_PAGE_SIZE, prot) < 0) + if(mprotect((void *) address, UM_THREAD_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC) < 0) panic("protecting stack failed, errno = %d", errno); } @@ -72,7 +55,7 @@ int raw(int fd) /* XXX tcsetattr could have applied only some changes * (and cfmakeraw() is a set of changes) */ - return(0); + return 0; } void setup_machinename(char *machine_out) |