mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Fix signal delivery during context switch.
This commit is contained in:
parent
82775c4803
commit
12b5044bc7
3 changed files with 103 additions and 4 deletions
|
@ -175,6 +175,86 @@ void LoadInterruptedContext(struct interrupt_context* intctx,
|
|||
#endif
|
||||
}
|
||||
|
||||
extern "C" void fake_interrupt(void);
|
||||
|
||||
// Pretend a particular interrupt arrived on another thread's stack. This
|
||||
// assumes we're _not_ on current_thread's stack right now. This sets up the
|
||||
// interrupt context such that the interrupt handler runs in that thread.
|
||||
// The interrupt handler runs with premption enabled. This is used to deliver
|
||||
// signals during context switches, as the signal handler needs to have
|
||||
// preemption enabled.
|
||||
static void FakeInterruptedContext(struct interrupt_context* intctx, int int_no)
|
||||
{
|
||||
#if defined(__i386__)
|
||||
uintptr_t stack = current_thread->kernelstackpos +
|
||||
current_thread->kernelstacksize;
|
||||
stack -= sizeof(struct interrupt_context);
|
||||
struct interrupt_context* fakectx = (struct interrupt_context*) stack;
|
||||
memcpy(fakectx, intctx, sizeof(struct interrupt_context));
|
||||
fakectx->int_no = int_no;
|
||||
fakectx->err_code = 0;
|
||||
stack -= 4;
|
||||
stack &= 0xFFFFFFF0;
|
||||
intctx->signal_pending = intctx->signal_pending;
|
||||
intctx->kerrno = 0;
|
||||
intctx->cr2 = 0;
|
||||
intctx->ds = KDS | KRPL;
|
||||
intctx->edi = intctx->edi;
|
||||
intctx->esi = intctx->esi;
|
||||
intctx->ebp = intctx->signal_pending;
|
||||
intctx->not_esp = intctx->not_esp;
|
||||
intctx->ebx = (uintptr_t) fakectx;
|
||||
intctx->edx = intctx->edx;
|
||||
intctx->ecx = intctx->ecx;
|
||||
intctx->eax = intctx->eax;
|
||||
intctx->int_no = intctx->int_no;
|
||||
intctx->err_code = intctx->err_code;
|
||||
intctx->eip = (uintptr_t) fake_interrupt;
|
||||
intctx->cs = KCS | KRPL;
|
||||
intctx->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
intctx->esp = stack;
|
||||
intctx->ss = KDS | KRPL;
|
||||
#elif defined(__x86_64__)
|
||||
uintptr_t stack = current_thread->kernelstackpos +
|
||||
current_thread->kernelstacksize;
|
||||
stack -= sizeof(struct interrupt_context);
|
||||
struct interrupt_context* fakectx = (struct interrupt_context*) stack;
|
||||
memcpy(fakectx, intctx, sizeof(struct interrupt_context));
|
||||
fakectx->int_no = int_no;
|
||||
fakectx->err_code = 0;
|
||||
stack &= 0xFFFFFFFFFFFFFFF0;
|
||||
intctx->signal_pending = intctx->signal_pending;
|
||||
intctx->kerrno = 0;
|
||||
intctx->cr2 = 0;
|
||||
intctx->ds = KDS | KRPL;
|
||||
intctx->rdi = (uintptr_t) fakectx;
|
||||
intctx->rsi = intctx->rsi;
|
||||
intctx->rbp = intctx->signal_pending;
|
||||
intctx->not_rsp = intctx->not_rsp;
|
||||
intctx->rbx = (uintptr_t) fakectx;
|
||||
intctx->rdx = intctx->rdx;
|
||||
intctx->rcx = intctx->rcx;
|
||||
intctx->rax = intctx->rax;
|
||||
intctx->r8 = intctx->r8;
|
||||
intctx->r9 = intctx->r9;
|
||||
intctx->r10 = intctx->r10;
|
||||
intctx->r11 = intctx->r11;
|
||||
intctx->r12 = intctx->r12;
|
||||
intctx->r13 = intctx->r13;
|
||||
intctx->r14 = intctx->r14;
|
||||
intctx->r15 = intctx->r15;
|
||||
intctx->int_no = intctx->int_no;
|
||||
intctx->err_code = intctx->err_code;
|
||||
intctx->rip = (uintptr_t) fake_interrupt;
|
||||
intctx->cs = KCS | KRPL;
|
||||
intctx->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
||||
intctx->rsp = stack;
|
||||
intctx->ss = KDS | KRPL;
|
||||
#else
|
||||
#warning "You need to implement faking an interrupt"
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
void SwitchThread(struct interrupt_context* intctx, Thread* prev, Thread* next)
|
||||
{
|
||||
|
@ -237,12 +317,25 @@ static Thread* PopNextThread(bool yielded)
|
|||
|
||||
static void RealSwitch(struct interrupt_context* intctx, bool yielded)
|
||||
{
|
||||
SwitchThread(intctx, CurrentThread(), PopNextThread(yielded));
|
||||
|
||||
Thread* old_thread = CurrentThread();
|
||||
Thread* new_thread = PopNextThread(yielded);
|
||||
SwitchThread(intctx, old_thread, new_thread);
|
||||
if ( intctx->signal_pending && InUserspace(intctx) )
|
||||
{
|
||||
Interrupt::Enable();
|
||||
Signal::DispatchHandler(intctx, NULL);
|
||||
// Become the thread for real and run the signal handler.
|
||||
if ( old_thread == new_thread )
|
||||
{
|
||||
// We're already this thread, so run the signal handler.
|
||||
Interrupt::Enable();
|
||||
Signal::DispatchHandler(intctx, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to transfer execution to the correct stack. We know the
|
||||
// the thread is in user-space and isn't using its kernel stack, and
|
||||
// we know we're not using the stack right now.
|
||||
FakeInterruptedContext(intctx, 130);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -388,6 +388,9 @@ interrupt_handler_prepare:
|
|||
movq %rsp, %rdi
|
||||
movq %rsp, %rbx
|
||||
andq $0xFFFFFFFFFFFFFFF0, %rsp
|
||||
.global fake_interrupt
|
||||
.type fake_interrupt, @function
|
||||
fake_interrupt:
|
||||
call interrupt_handler
|
||||
movq %rbx, %rsp
|
||||
|
||||
|
|
|
@ -385,6 +385,9 @@ fixup_relocate_stack_complete:
|
|||
movl %esp, %ebx
|
||||
subl $4, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
.global fake_interrupt
|
||||
.type fake_interrupt, @function
|
||||
fake_interrupt:
|
||||
movl %ebx, (%esp)
|
||||
call interrupt_handler
|
||||
movl %ebx, %esp
|
||||
|
|
Loading…
Add table
Reference in a new issue