diff options
| -rw-r--r-- | arch/um/include/kern_util.h | 3 | ||||
| -rw-r--r-- | arch/um/kernel/dyn.lds.S | 2 | ||||
| -rw-r--r-- | arch/um/kernel/init_task.c | 16 | ||||
| -rw-r--r-- | arch/um/kernel/irq.c | 111 | ||||
| -rw-r--r-- | arch/um/kernel/skas/process.c | 4 | ||||
| -rw-r--r-- | arch/um/kernel/uml.lds.S | 2 | ||||
| -rw-r--r-- | arch/um/os-Linux/signal.c | 40 | ||||
| -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-- | include/asm-um/thread_info.h | 2 |
10 files changed, 180 insertions, 14 deletions
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); | |||
| 117 | 117 | ||
| 118 | extern void copy_sc(union uml_pt_regs *regs, void *from); | 118 | extern void copy_sc(union uml_pt_regs *regs, void *from); |
| 119 | 119 | ||
| 120 | unsigned long to_irq_stack(int sig, unsigned long *mask_out); | ||
| 121 | unsigned long from_irq_stack(int nested); | ||
| 122 | |||
| 120 | #endif | 123 | #endif |
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 | |||
| 97 | .data : { | 97 | .data : { |
| 98 | . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ | 98 | . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ |
| 99 | *(.data.init_task) | 99 | *(.data.init_task) |
| 100 | . = ALIGN(KERNEL_STACK_SIZE); | ||
| 101 | *(.data.init_irqstack) | ||
| 100 | *(.data .data.* .gnu.linkonce.d.*) | 102 | *(.data .data.* .gnu.linkonce.d.*) |
| 101 | SORT(CONSTRUCTORS) | 103 | SORT(CONSTRUCTORS) |
| 102 | } | 104 | } |
diff --git a/arch/um/kernel/init_task.c b/arch/um/kernel/init_task.c index f5385a3799b..d4f1d1ab252 100644 --- a/arch/um/kernel/init_task.c +++ b/arch/um/kernel/init_task.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | 2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,intel.linux}.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| @@ -33,14 +33,18 @@ EXPORT_SYMBOL(init_task); | |||
| 33 | /* | 33 | /* |
| 34 | * Initial thread structure. | 34 | * Initial thread structure. |
| 35 | * | 35 | * |
| 36 | * We need to make sure that this is 16384-byte aligned due to the | 36 | * We need to make sure that this is aligned due to the |
| 37 | * way process stacks are handled. This is done by having a special | 37 | * way process stacks are handled. This is done by having a special |
| 38 | * "init_task" linker map entry.. | 38 | * "init_task" linker map entry.. |
| 39 | */ | 39 | */ |
| 40 | 40 | ||
| 41 | union thread_union init_thread_union | 41 | union thread_union init_thread_union |
| 42 | __attribute__((__section__(".data.init_task"))) = | 42 | __attribute__((__section__(".data.init_task"))) = |
| 43 | { INIT_THREAD_INFO(init_task) }; | 43 | { INIT_THREAD_INFO(init_task) }; |
| 44 | |||
| 45 | union thread_union cpu0_irqstack | ||
| 46 | __attribute__((__section__(".data.init_irqstack"))) = | ||
| 47 | { INIT_THREAD_INFO(init_task) }; | ||
| 44 | 48 | ||
| 45 | void unprotect_stack(unsigned long stack) | 49 | void unprotect_stack(unsigned long stack) |
| 46 | { | 50 | { |
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index a9651a175eb..dba04d88b43 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include "sigio.h" | 32 | #include "sigio.h" |
| 33 | #include "um_malloc.h" | 33 | #include "um_malloc.h" |
| 34 | #include "misc_constants.h" | 34 | #include "misc_constants.h" |
| 35 | #include "as-layout.h" | ||
| 35 | 36 | ||
| 36 | /* | 37 | /* |
| 37 | * Generic, controller-independent functions: | 38 | * Generic, controller-independent functions: |
| @@ -468,3 +469,113 @@ int init_aio_irq(int irq, char *name, irq_handler_t handler) | |||
| 468 | out: | 469 | out: |
| 469 | return err; | 470 | return err; |
| 470 | } | 471 | } |
| 472 | |||
| 473 | /* | ||
| 474 | * IRQ stack entry and exit: | ||
| 475 | * | ||
| 476 | * Unlike i386, UML doesn't receive IRQs on the normal kernel stack | ||
| 477 | * and switch over to the IRQ stack after some preparation. We use | ||
| 478 | * sigaltstack to receive signals on a separate stack from the start. | ||
| 479 | * These two functions make sure the rest of the kernel won't be too | ||
| 480 | * upset by being on a different stack. The IRQ stack has a | ||
| 481 | * thread_info structure at the bottom so that current et al continue | ||
| 482 | * to work. | ||
| 483 | * | ||
| 484 | * to_irq_stack copies the current task's thread_info to the IRQ stack | ||
| 485 | * thread_info and sets the tasks's stack to point to the IRQ stack. | ||
| 486 | * | ||
| 487 | * from_irq_stack copies the thread_info struct back (flags may have | ||
| 488 | * been modified) and resets the task's stack pointer. | ||
| 489 | * | ||
| 490 | * Tricky bits - | ||
| 491 | * | ||
| 492 | * What happens when two signals race each other? UML doesn't block | ||
| 493 | * signals with sigprocmask, SA_DEFER, or sa_mask, so a second signal | ||
| 494 | * could arrive while a previous one is still setting up the | ||
| 495 | * thread_info. | ||
| 496 | * | ||
| 497 | * There are three cases - | ||
| 498 | * The first interrupt on the stack - sets up the thread_info and | ||
| 499 | * handles the interrupt | ||
| 500 | * A nested interrupt interrupting the copying of the thread_info - | ||
| 501 | * can't handle the interrupt, as the stack is in an unknown state | ||
| 502 | * A nested interrupt not interrupting the copying of the | ||
| 503 | * thread_info - doesn't do any setup, just handles the interrupt | ||
| 504 | * | ||
| 505 | * The first job is to figure out whether we interrupted stack setup. | ||
| 506 | * This is done by xchging the signal mask with thread_info->pending. | ||
| 507 | * If the value that comes back is zero, then there is no setup in | ||
| 508 | * progress, and the interrupt can be handled. If the value is | ||
| 509 | * non-zero, then there is stack setup in progress. In order to have | ||
| 510 | * the interrupt handled, we leave our signal in the mask, and it will | ||
| 511 | * be handled by the upper handler after it has set up the stack. | ||
| 512 | * | ||
| 513 | * Next is to figure out whether we are the outer handler or a nested | ||
| 514 | * one. As part of setting up the stack, thread_info->real_thread is | ||
| 515 | * set to non-NULL (and is reset to NULL on exit). This is the | ||
| 516 | * nesting indicator. If it is non-NULL, then the stack is already | ||
| 517 | * set up and the handler can run. | ||
| 518 | */ | ||
| 519 | |||
| 520 | static unsigned long pending_mask; | ||
| 521 | |||
| 522 | unsigned long to_irq_stack(int sig, unsigned long *mask_out) | ||
| 523 | { | ||
| 524 | struct thread_info *ti; | ||
| 525 | unsigned long mask, old; | ||
| 526 | int nested; | ||
| 527 | |||
| 528 | mask = xchg(&pending_mask, 1 << sig); | ||
| 529 | if(mask != 0){ | ||
| 530 | /* If any interrupts come in at this point, we want to | ||
| 531 | * make sure that their bits aren't lost by our | ||
| 532 | * putting our bit in. So, this loop accumulates bits | ||
| 533 | * until xchg returns the same value that we put in. | ||
| 534 | * When that happens, there were no new interrupts, | ||
| 535 | * and pending_mask contains a bit for each interrupt | ||
| 536 | * that came in. | ||
| 537 | */ | ||
| 538 | old = 1 << sig; | ||
| 539 | do { | ||
| 540 | old |= mask; | ||
| 541 | mask = xchg(&pending_mask, old); | ||
| 542 | } while(mask != old); | ||
| 543 | return 1; | ||
| 544 | } | ||
| 545 | |||
| 546 | ti = current_thread_info(); | ||
| 547 | nested = (ti->real_thread != NULL); | ||
| 548 | if(!nested){ | ||
| 549 | struct task_struct *task; | ||
| 550 | struct thread_info *tti; | ||
| 551 | |||
| 552 | task = cpu_tasks[ti->cpu].task; | ||
| 553 | tti = task_thread_info(task); | ||
| 554 | *ti = *tti; | ||
| 555 | ti->real_thread = tti; | ||
| 556 | task->stack = ti; | ||
| 557 | } | ||
| 558 | |||
| 559 | mask = xchg(&pending_mask, 0); | ||
| 560 | *mask_out |= mask | nested; | ||
| 561 | return 0; | ||
| 562 | } | ||
| 563 | |||
| 564 | unsigned long from_irq_stack(int nested) | ||
| 565 | { | ||
| 566 | struct thread_info *ti, *to; | ||
| 567 | unsigned long mask; | ||
| 568 | |||
| 569 | ti = current_thread_info(); | ||
| 570 | |||
| 571 | pending_mask = 1; | ||
| 572 | |||
| 573 | to = ti->real_thread; | ||
| 574 | current->stack = to; | ||
| 575 | ti->real_thread = NULL; | ||
| 576 | *to = *ti; | ||
| 577 | |||
| 578 | mask = xchg(&pending_mask, 0); | ||
| 579 | return mask & ~1; | ||
| 580 | } | ||
| 581 | |||
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) | |||
| 163 | 163 | ||
| 164 | extern int userspace_pid[]; | 164 | extern int userspace_pid[]; |
| 165 | 165 | ||
| 166 | extern char cpu0_irqstack[]; | ||
| 167 | |||
| 166 | int start_uml_skas(void) | 168 | int start_uml_skas(void) |
| 167 | { | 169 | { |
| 170 | stack_protections((unsigned long) &cpu0_irqstack); | ||
| 171 | set_sigstack(cpu0_irqstack, THREAD_SIZE); | ||
| 168 | if(proc_mm) | 172 | if(proc_mm) |
| 169 | userspace_pid[0] = start_userspace(0); | 173 | userspace_pid[0] = start_userspace(0); |
| 170 | 174 | ||
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 | |||
| 59 | { | 59 | { |
| 60 | . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ | 60 | . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ |
| 61 | *(.data.init_task) | 61 | *(.data.init_task) |
| 62 | . = ALIGN(KERNEL_STACK_SIZE); | ||
| 63 | *(.data.init_irqstack) | ||
| 62 | *(.data) | 64 | *(.data) |
| 63 | *(.gnu.linkonce.d*) | 65 | *(.gnu.linkonce.d*) |
| 64 | CONSTRUCTORS | 66 | CONSTRUCTORS |
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 420ee86d0d1..18e5c8b67eb 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c | |||
| @@ -117,6 +117,46 @@ void remove_sigstack(void) | |||
| 117 | 117 | ||
| 118 | void (*handlers[_NSIG])(int sig, struct sigcontext *sc); | 118 | void (*handlers[_NSIG])(int sig, struct sigcontext *sc); |
| 119 | 119 | ||
| 120 | void handle_signal(int sig, struct sigcontext *sc) | ||
| 121 | { | ||
| 122 | unsigned long pending = 0; | ||
| 123 | |||
| 124 | do { | ||
| 125 | int nested, bail; | ||
| 126 | |||
| 127 | /* | ||
| 128 | * pending comes back with one bit set for each | ||
| 129 | * interrupt that arrived while setting up the stack, | ||
| 130 | * plus a bit for this interrupt, plus the zero bit is | ||
| 131 | * set if this is a nested interrupt. | ||
| 132 | * If bail is true, then we interrupted another | ||
| 133 | * handler setting up the stack. In this case, we | ||
| 134 | * have to return, and the upper handler will deal | ||
| 135 | * with this interrupt. | ||
| 136 | */ | ||
| 137 | bail = to_irq_stack(sig, &pending); | ||
| 138 | if(bail) | ||
| 139 | return; | ||
| 140 | |||
| 141 | nested = pending & 1; | ||
| 142 | pending &= ~1; | ||
| 143 | |||
| 144 | while((sig = ffs(pending)) != 0){ | ||
| 145 | sig--; | ||
| 146 | pending &= ~(1 << sig); | ||
| 147 | (*handlers[sig])(sig, sc); | ||
| 148 | } | ||
| 149 | |||
| 150 | /* Again, pending comes back with a mask of signals | ||
| 151 | * that arrived while tearing down the stack. If this | ||
| 152 | * is non-zero, we just go back, set up the stack | ||
| 153 | * again, and handle the new interrupts. | ||
| 154 | */ | ||
| 155 | if(!nested) | ||
| 156 | pending = from_irq_stack(nested); | ||
| 157 | } while(pending); | ||
| 158 | } | ||
| 159 | |||
| 120 | extern void hard_handler(int sig); | 160 | extern void hard_handler(int sig); |
| 121 | 161 | ||
| 122 | void set_handler(int sig, void (*handler)(int), int flags, ...) | 162 | void set_handler(int sig, void (*handler)(int), int flags, ...) |
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 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) | 2 | * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <signal.h> | 6 | #include <signal.h> |
| 7 | 7 | ||
| 8 | extern void (*handlers[])(int sig, struct sigcontext *sc); | 8 | extern void handle_signal(int sig, struct sigcontext *sc); |
| 9 | 9 | ||
| 10 | void hard_handler(int sig) | 10 | void hard_handler(int sig) |
| 11 | { | 11 | { |
| 12 | struct sigcontext *sc = (struct sigcontext *) (&sig + 1); | 12 | handle_signal(sig, (struct sigcontext *) (&sig + 1)); |
| 13 | |||
| 14 | (*handlers[sig])(sig, sc); | ||
| 15 | } | 13 | } |
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 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (C) 2006 Jeff Dike (jdike@addtoit.com) | 2 | * Copyright (C) 2006 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
| 3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <signal.h> | 6 | #include <signal.h> |
| 7 | 7 | ||
| 8 | extern void (*handlers[])(int sig, struct sigcontext *sc); | 8 | extern void handle_signal(int sig, struct sigcontext *sc); |
| 9 | 9 | ||
| 10 | void hard_handler(int sig) | 10 | void hard_handler(int sig) |
| 11 | { | 11 | { |
| 12 | struct ucontext *uc; | 12 | struct ucontext *uc; |
| 13 | asm("movq %%rdx, %0" : "=r" (uc)); | 13 | asm("movq %%rdx, %0" : "=r" (uc)); |
| 14 | 14 | ||
| 15 | (*handlers[sig])(sig, (struct sigcontext *) &uc->uc_mcontext); | 15 | handle_signal(sig, (struct sigcontext *) &uc->uc_mcontext); |
| 16 | } | 16 | } |
diff --git a/include/asm-um/thread_info.h b/include/asm-um/thread_info.h index 261e2f4528f..18a13ba7460 100644 --- a/include/asm-um/thread_info.h +++ b/include/asm-um/thread_info.h | |||
| @@ -22,6 +22,7 @@ struct thread_info { | |||
| 22 | 0-0xBFFFFFFF for user | 22 | 0-0xBFFFFFFF for user |
| 23 | 0-0xFFFFFFFF for kernel */ | 23 | 0-0xFFFFFFFF for kernel */ |
| 24 | struct restart_block restart_block; | 24 | struct restart_block restart_block; |
| 25 | struct thread_info *real_thread; /* Points to non-IRQ stack */ | ||
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 27 | #define INIT_THREAD_INFO(tsk) \ | 28 | #define INIT_THREAD_INFO(tsk) \ |
| @@ -35,6 +36,7 @@ struct thread_info { | |||
| 35 | .restart_block = { \ | 36 | .restart_block = { \ |
| 36 | .fn = do_no_restart_syscall, \ | 37 | .fn = do_no_restart_syscall, \ |
| 37 | }, \ | 38 | }, \ |
| 39 | .real_thread = NULL, \ | ||
| 38 | } | 40 | } |
| 39 | 41 | ||
| 40 | #define init_thread_info (init_thread_union.thread_info) | 42 | #define init_thread_info (init_thread_union.thread_info) |
