kernel: complete redesign of signal handling
This commit is contained in:
parent
9f495ac854
commit
bbebc7c128
@ -115,7 +115,6 @@ typedef struct process {
|
||||
list_t * shm_mappings;
|
||||
list_t * node_waits;
|
||||
list_t * signal_queue;
|
||||
char * signal_kstack;
|
||||
|
||||
node_t sched_node;
|
||||
node_t sleep_node;
|
||||
@ -269,6 +268,7 @@ extern void arch_save_floating(process_t * proc);
|
||||
extern void arch_set_kernel_stack(uintptr_t);
|
||||
extern void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[], uintptr_t stack);
|
||||
__attribute__((noreturn))
|
||||
extern void arch_enter_signal_handler(uintptr_t,int);
|
||||
extern void arch_enter_signal_handler(uintptr_t,int,struct regs*);
|
||||
extern void arch_wakeup_others(void);
|
||||
extern void arch_return_from_signal_handler(struct regs *r);
|
||||
|
||||
|
@ -20,8 +20,6 @@ typedef struct {
|
||||
extern void fix_signal_stacks(void);
|
||||
extern int send_signal(pid_t process, int signal, int force_root);
|
||||
extern int group_send_signal(pid_t group, int signal, int force_root);
|
||||
extern void handle_signal(process_t * proc, signal_t * sig);
|
||||
|
||||
__attribute__((noreturn))
|
||||
extern void return_from_signal_handler(void);
|
||||
extern void handle_signal(process_t * proc, signal_t * sig, struct regs *r);
|
||||
extern void process_check_signals(struct regs*);
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <kernel/string.h>
|
||||
#include <kernel/printf.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include <kernel/mmu.h>
|
||||
|
||||
#include <kernel/arch/aarch64/regs.h>
|
||||
#include <kernel/arch/aarch64/gic.h>
|
||||
@ -48,6 +49,40 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void _kill_it(uintptr_t addr, const char * action, const char * desc, size_t size) {
|
||||
dprintf("core %d (pid=%d %s): invalid stack for signal %s (%#zx '%s' %zu)\n",
|
||||
this_core->cpu_id, this_core->current_process->id, this_core->current_process->name, action, addr, desc, size);
|
||||
task_exit(((128 + SIGSEGV) << 8) | SIGSEGV);
|
||||
}
|
||||
|
||||
#define PUSH(stack, type, item) do { \
|
||||
stack -= sizeof(type); \
|
||||
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),MMU_PTR_WRITE)) \
|
||||
_kill_it((uintptr_t)stack,"entry",#item,sizeof(type)); \
|
||||
*((volatile type *) stack) = item; \
|
||||
} while (0)
|
||||
|
||||
#define POP(stack, type, item) do { \
|
||||
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),0)) \
|
||||
_kill_it((uintptr_t)stack,"return",#item,sizeof(type)); \
|
||||
item = *((volatile type *) stack); \
|
||||
stack += sizeof(type); \
|
||||
} while (0)
|
||||
|
||||
void arch_return_from_signal_handler(struct regs *r) {
|
||||
uintptr_t elr;
|
||||
uintptr_t sp = r->user_sp;
|
||||
POP(sp, uintptr_t, this_core->current_process->thread.context.saved[13]);
|
||||
POP(sp, uintptr_t, this_core->current_process->thread.context.saved[12]);
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
POP(sp, uint64_t, this_core->current_process->thread.fp_regs[63-i]);
|
||||
}
|
||||
|
||||
POP(sp, struct regs, *r);
|
||||
POP(sp, uint64_t, elr);
|
||||
asm volatile ("msr ELR_EL1, %0" : "=r"(elr));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enter a userspace signal handler.
|
||||
*
|
||||
@ -61,14 +96,30 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
|
||||
* @param entrypoint Userspace address of the signal handler, set by the process.
|
||||
* @param signum Signal number that caused this entry.
|
||||
*/
|
||||
void arch_enter_signal_handler(uintptr_t entrypoint, int signum) {
|
||||
void arch_enter_signal_handler(uintptr_t entrypoint, int signum, struct regs *r) {
|
||||
|
||||
uintptr_t elr;
|
||||
uintptr_t sp = (r->user_sp - 128) & 0xFFFFFFFFFFFFFFF0;
|
||||
|
||||
asm volatile ("mrs %0, ELR_EL1" : "=r"(elr));
|
||||
|
||||
PUSH(sp, uint64_t, elr);
|
||||
PUSH(sp, struct regs, *r);
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
PUSH(sp, uint64_t, this_core->current_process->thread.fp_regs[i]);
|
||||
}
|
||||
|
||||
PUSH(sp, uintptr_t, this_core->current_process->thread.context.saved[12]);
|
||||
PUSH(sp, uintptr_t, this_core->current_process->thread.context.saved[13]);
|
||||
|
||||
asm volatile(
|
||||
"msr ELR_EL1, %0\n" /* entrypoint */
|
||||
"msr SP_EL0, %1\n" /* stack */
|
||||
"msr SPSR_EL1, %2\n" /* spsr from context */
|
||||
::
|
||||
"r"(entrypoint),
|
||||
"r"((this_core->current_process->syscall_registers->user_sp - 128) & 0xFFFFFFFFFFFFFFF0),
|
||||
"r"(sp),
|
||||
"r"(this_core->current_process->thread.context.saved[11]));
|
||||
|
||||
register uint64_t x0 __asm__("x0") = signum;
|
||||
|
@ -294,7 +294,7 @@ void aarch64_sync_enter(struct regs * r) {
|
||||
ptrace_signal(SIGTRAP, PTRACE_EVENT_SINGLESTEP);
|
||||
}
|
||||
|
||||
return;
|
||||
goto _resume_user;
|
||||
}
|
||||
|
||||
/* Magic thread exit */
|
||||
@ -305,8 +305,8 @@ void aarch64_sync_enter(struct regs * r) {
|
||||
|
||||
/* Magic signal return */
|
||||
if (elr == 0x8DEADBEEF && far == 0x8DEADBEEF) {
|
||||
return_from_signal_handler();
|
||||
return;
|
||||
arch_return_from_signal_handler(r);
|
||||
goto _resume_user;
|
||||
}
|
||||
|
||||
/* System call */
|
||||
@ -315,13 +315,13 @@ void aarch64_sync_enter(struct regs * r) {
|
||||
// this_core->current_process->id, r->x0, elr);
|
||||
extern void syscall_handler(struct regs *);
|
||||
syscall_handler(r);
|
||||
return;
|
||||
goto _resume_user;
|
||||
}
|
||||
|
||||
/* KVM is mad at us; usually means our code is broken or we neglected a cache. */
|
||||
if (far == 0x1de7ec7edbadc0de) {
|
||||
printf("kvm: blip (esr=%#zx, elr=%#zx; pid=%d [%s])\n", esr, elr, this_core->current_process->id, this_core->current_process->name);
|
||||
return;
|
||||
goto _resume_user;
|
||||
}
|
||||
|
||||
/* Unexpected fault, eg. page fault. */
|
||||
@ -333,6 +333,9 @@ void aarch64_sync_enter(struct regs * r) {
|
||||
dprintf(" TPIDR_EL0=%#zx\n", tpidr_el0);
|
||||
|
||||
send_signal(this_core->current_process->id, SIGSEGV, 1);
|
||||
|
||||
_resume_user:
|
||||
process_check_signals(r);
|
||||
}
|
||||
|
||||
static void spin(void) {
|
||||
@ -406,6 +409,8 @@ void aarch64_irq_enter(struct regs * r) {
|
||||
}
|
||||
|
||||
aarch64_interrupt_dispatch(0);
|
||||
|
||||
process_check_signals(r);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -510,7 +510,7 @@ static void _page_fault(struct regs * r) {
|
||||
|
||||
/* 8DEADBEEFh is the magic ret-from-sig address. */
|
||||
if (faulting_address == 0x8DEADBEEF) {
|
||||
return_from_signal_handler();
|
||||
arch_return_from_signal_handler(r);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -613,13 +613,7 @@ static void _handle_irq(struct regs * r, int irq) {
|
||||
#define EXC(i,n) case i: _exception(r, n); break;
|
||||
#define IRQ(i) case i: _handle_irq(r,i-32); break;
|
||||
|
||||
struct regs * isr_handler(struct regs * r) {
|
||||
this_core->interrupt_registers = r;
|
||||
|
||||
if (r->cs != 0x08 && this_core->current_process) {
|
||||
this_core->current_process->time_switch = arch_perf_timer();
|
||||
}
|
||||
|
||||
struct regs * isr_handler_inner(struct regs * r) {
|
||||
switch (r->int_no) {
|
||||
EXC(0,"divide-by-zero");
|
||||
case 1: return _debug_int(r);
|
||||
@ -690,3 +684,20 @@ struct regs * isr_handler(struct regs * r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
struct regs * isr_handler(struct regs * r) {
|
||||
int from_userspace = r->cs != 0x08;
|
||||
this_core->interrupt_registers = r;
|
||||
|
||||
if (from_userspace && this_core->current_process) {
|
||||
this_core->current_process->time_switch = arch_perf_timer();
|
||||
}
|
||||
|
||||
struct regs * out = isr_handler_inner(r);
|
||||
|
||||
if (from_userspace && this_core->current_process) {
|
||||
process_check_signals(out);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
}
|
||||
|
@ -46,6 +46,48 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
|
||||
"D"(argc), "S"(argv), "d"(envp));
|
||||
}
|
||||
|
||||
static void _kill_it(void) {
|
||||
dprintf("core %d (pid=%d %s): invalid stack for signal return\n",
|
||||
this_core->cpu_id, this_core->current_process->id, this_core->current_process->name);
|
||||
task_exit(((128 + SIGSEGV) << 8) | SIGSEGV);
|
||||
}
|
||||
|
||||
#define PUSH(stack, type, item) do { \
|
||||
stack -= sizeof(type); \
|
||||
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),MMU_PTR_WRITE)) \
|
||||
_kill_it(); \
|
||||
*((volatile type *) stack) = item; \
|
||||
} while (0)
|
||||
|
||||
#define POP(stack, type, item) do { \
|
||||
if (!mmu_validate_user_pointer((void*)(uintptr_t)stack,sizeof(type),0)) \
|
||||
_kill_it(); \
|
||||
item = *((volatile type *) stack); \
|
||||
stack += sizeof(type); \
|
||||
} while (0)
|
||||
|
||||
void arch_return_from_signal_handler(struct regs *r) {
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
POP(r->rsp, uint64_t, this_core->current_process->thread.fp_regs[63-i]);
|
||||
}
|
||||
|
||||
struct regs out;
|
||||
POP(r->rsp, struct regs, out);
|
||||
|
||||
#define R(n) r-> n = out. n
|
||||
|
||||
R(r15); R(r14); R(r13); R(r12);
|
||||
R(r11); R(r10); R(r9); R(r8);
|
||||
R(rbp); R(rdi); R(rsi); R(rdx); R(rcx); R(rbx); R(rax);
|
||||
|
||||
R(rip);
|
||||
R(rsp);
|
||||
|
||||
r->rflags = (out.rflags & 0xcd5) | (1 << 21) | (1 << 9) | ((r->rflags & (1 << 8)) ? (1 << 8) : 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Enter a userspace signal handler.
|
||||
*
|
||||
@ -59,14 +101,21 @@ void arch_enter_user(uintptr_t entrypoint, int argc, char * argv[], char * envp[
|
||||
* @param entrypoint Userspace address of the signal handler, set by the process.
|
||||
* @param signum Signal number that caused this entry.
|
||||
*/
|
||||
void arch_enter_signal_handler(uintptr_t entrypoint, int signum) {
|
||||
void arch_enter_signal_handler(uintptr_t entrypoint, int signum, struct regs *r) {
|
||||
struct regs ret;
|
||||
ret.cs = 0x18 | 0x03;
|
||||
ret.ss = 0x20 | 0x03;
|
||||
ret.rip = entrypoint;
|
||||
ret.rflags = (1 << 21) | (1 << 9);
|
||||
ret.rsp = (this_core->current_process->syscall_registers->rsp - 128 - 8) & 0xFFFFFFFFFFFFFFF0; /* ensure considerable alignment */
|
||||
*(uintptr_t*)ret.rsp = 0x00000008DEADBEEF; /* arbitrarily chosen stack return sentinel IP */
|
||||
ret.rsp = (r->rsp - 128) & 0xFFFFFFFFFFFFFFF0; /* ensure considerable alignment */
|
||||
|
||||
PUSH(ret.rsp, struct regs, *r);
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
PUSH(ret.rsp, uint64_t, this_core->current_process->thread.fp_regs[i]);
|
||||
}
|
||||
|
||||
PUSH(ret.rsp, uintptr_t, 0x00000008DEADBEEF);
|
||||
|
||||
asm volatile(
|
||||
"pushq %0\n"
|
||||
|
@ -133,18 +133,6 @@ void switch_next(void) {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
if (this_core->current_process->flags & PROC_FLAG_STARTED) {
|
||||
/* If this process has a signal pending, we save its current context - including
|
||||
* the entire kernel stack - before resuming switch_task. */
|
||||
if (!this_core->current_process->signal_kstack) {
|
||||
if (this_core->current_process->signal_queue->length > 0) {
|
||||
this_core->current_process->signal_kstack = malloc(KERNEL_STACK_SIZE);
|
||||
memcpy(this_core->current_process->signal_kstack, (void*)(this_core->current_process->image.stack - KERNEL_STACK_SIZE), KERNEL_STACK_SIZE);
|
||||
memcpy((thread_t*)&this_core->current_process->signal_state, (thread_t*)&this_core->current_process->thread, sizeof(thread_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark the process as running and started. */
|
||||
__sync_or_and_fetch(&this_core->current_process->flags, PROC_FLAG_STARTED);
|
||||
|
||||
@ -196,17 +184,6 @@ void switch_task(uint8_t reschedule) {
|
||||
* from a task switch and have been awoken if we were sleeping. */
|
||||
if (arch_save_context(&this_core->current_process->thread) == 1) {
|
||||
arch_restore_floating((process_t*)this_core->current_process);
|
||||
|
||||
fix_signal_stacks();
|
||||
if (!(this_core->current_process->flags & PROC_FLAG_FINISHED)) {
|
||||
if (this_core->current_process->signal_queue->length > 0) {
|
||||
node_t * node = list_dequeue(this_core->current_process->signal_queue);
|
||||
signal_t * sig = node->value;
|
||||
free(node);
|
||||
handle_signal((process_t*)this_core->current_process,sig);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -430,7 +407,6 @@ process_t * spawn_init(void) {
|
||||
init->wait_queue = list_create("process wait queue (init)", init);
|
||||
init->shm_mappings = list_create("process shm mapping (init)", init);
|
||||
init->signal_queue = list_create("process signal queue (init)", init);
|
||||
init->signal_kstack = NULL; /* Initialized later */
|
||||
|
||||
init->sched_node.prev = NULL;
|
||||
init->sched_node.next = NULL;
|
||||
@ -538,10 +514,6 @@ process_t * spawn_process(volatile process_t * parent, int flags) {
|
||||
extern void tree_remove_reparent_root(tree_t * tree, tree_node_t * node);
|
||||
|
||||
void process_reap(process_t * proc) {
|
||||
if (proc->signal_kstack) {
|
||||
free(proc->signal_kstack);
|
||||
}
|
||||
|
||||
if (proc->tracees) {
|
||||
while (proc->tracees->length) {
|
||||
free(list_pop(proc->tracees));
|
||||
|
@ -72,7 +72,7 @@ char isdeadly[] = {
|
||||
[SIGCAT ] = 0,
|
||||
};
|
||||
|
||||
void handle_signal(process_t * proc, signal_t * sig) {
|
||||
void handle_signal(process_t * proc, signal_t * sig, struct regs *r) {
|
||||
uintptr_t handler = sig->handler;
|
||||
uintptr_t signum = sig->signum;
|
||||
free(sig);
|
||||
@ -120,58 +120,7 @@ void handle_signal(process_t * proc, signal_t * sig) {
|
||||
return;
|
||||
}
|
||||
|
||||
arch_enter_signal_handler(handler, signum);
|
||||
}
|
||||
|
||||
list_t * rets_from_sig;
|
||||
|
||||
void return_from_signal_handler(void) {
|
||||
if (__builtin_expect(!rets_from_sig, 0)) {
|
||||
rets_from_sig = list_create("global return-from-signal queue",NULL);
|
||||
}
|
||||
|
||||
spin_lock(sig_lock);
|
||||
list_insert(rets_from_sig, (process_t *)this_core->current_process);
|
||||
spin_unlock(sig_lock);
|
||||
|
||||
switch_next();
|
||||
}
|
||||
|
||||
void fix_signal_stacks(void) {
|
||||
uint8_t redo_me = 0;
|
||||
if (rets_from_sig) {
|
||||
spin_lock(sig_lock_b);
|
||||
while (rets_from_sig->head) {
|
||||
spin_lock(sig_lock);
|
||||
node_t * n = list_dequeue(rets_from_sig);
|
||||
spin_unlock(sig_lock);
|
||||
if (!n) {
|
||||
continue;
|
||||
}
|
||||
process_t * p = n->value;
|
||||
free(n);
|
||||
if (p == this_core->current_process) {
|
||||
redo_me = 1;
|
||||
continue;
|
||||
}
|
||||
memcpy(&p->thread, &p->signal_state, sizeof(thread_t));
|
||||
if (!p->signal_kstack) {
|
||||
printf("Cannot restore signal stack for pid=%d - unset?\n", p->id);
|
||||
} else {
|
||||
memcpy((char *)(p->image.stack - KERNEL_STACK_SIZE + 0x1000), p->signal_kstack + 0x1000, KERNEL_STACK_SIZE - 0x1000);
|
||||
free(p->signal_kstack);
|
||||
p->signal_kstack = NULL;
|
||||
}
|
||||
make_process_ready(p);
|
||||
}
|
||||
spin_unlock(sig_lock_b);
|
||||
}
|
||||
if (redo_me) {
|
||||
spin_lock(sig_lock);
|
||||
list_insert(rets_from_sig, (process_t *)this_core->current_process);
|
||||
spin_unlock(sig_lock);
|
||||
switch_next();
|
||||
}
|
||||
arch_enter_signal_handler(handler, signum, r);
|
||||
}
|
||||
|
||||
int send_signal(pid_t process, int signal, int force_root) {
|
||||
@ -230,15 +179,6 @@ int send_signal(pid_t process, int signal, int force_root) {
|
||||
make_process_ready(receiver);
|
||||
}
|
||||
|
||||
if (receiver == this_core->current_process) {
|
||||
/* Forces us to be rescheduled and enter signal handler */
|
||||
if (receiver->signal_kstack) {
|
||||
switch_next();
|
||||
} else {
|
||||
switch_task(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -270,3 +210,18 @@ int group_send_signal(pid_t group, int signal, int force_root) {
|
||||
return !!killed_something;
|
||||
}
|
||||
|
||||
void process_check_signals(struct regs * r) {
|
||||
if (this_core->current_process &&
|
||||
!(this_core->current_process->flags & PROC_FLAG_FINISHED) &&
|
||||
this_core->current_process->signal_queue &&
|
||||
this_core->current_process->signal_queue->length > 0) {
|
||||
|
||||
node_t * node = list_dequeue(this_core->current_process->signal_queue);
|
||||
signal_t * sig = node->value;
|
||||
|
||||
free(node);
|
||||
|
||||
handle_signal((process_t*)this_core->current_process,sig,r);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user