Global thread list, proper kill() implementation, proper signal entry in

sched
This commit is contained in:
Mark
2020-01-31 23:35:46 +02:00
parent 0df6daa87f
commit 6089df1556
6 changed files with 129 additions and 22 deletions
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "sys/types.h"
struct thread;
// Enter a newly-created task
extern void context_enter(struct thread *thr);
// Enter a task from exec
extern void context_exec_enter(void *arg, struct thread *thr, uintptr_t stack3, uintptr_t entry);
// Stores current task context, loads new one's
extern void context_switch_to(struct thread *thr, struct thread *from);
// No current task, only load the first task to begin execution
extern void context_switch_first(struct thread *thr);
extern void context_sigenter(uintptr_t entry, uintptr_t stack, int signum);
extern void context_sigreturn(void);
-9
View File
@@ -2,15 +2,6 @@
enum thread_state;
struct thread;
// Enter a newly-created task
extern void context_enter(struct thread *thr);
// Enter a task from exec
extern void context_exec_enter(void *arg, struct thread *thr, uintptr_t stack3, uintptr_t entry);
// Stores current task context, loads new one's
extern void context_switch_to(struct thread *thr, struct thread *from);
// No current task, only load the first task to begin execution
extern void context_switch_first(struct thread *thr);
void sched_queue(struct thread *thr);
void sched_unqueue(struct thread *thr, enum thread_state new_state);
+9 -2
View File
@@ -30,6 +30,7 @@ struct thread {
// Signal
uintptr_t signal_entry;
uint64_t sigq;
// State
pid_t pid;
@@ -38,12 +39,18 @@ struct thread {
struct thread *first_child;
struct thread *next_child;
// Global thread list (for stuff like finding by PID)
struct thread *g_prev, *g_next;
// Scheduler
struct thread *prev, *next;
};
pid_t thread_alloc_pid(int is_user);
void thread_sleep(struct thread *thr, uint64_t deadline);
void thread_ioctx_fork(struct thread *dst, struct thread *src);
int thread_init(struct thread *thr, uintptr_t entry, void *arg, int user);
struct thread *thread_find(pid_t pid);
void thread_sleep(struct thread *thr, uint64_t deadline);
void thread_sigenter(int signum);
+10 -3
View File
@@ -115,11 +115,11 @@ context_sigreturn:
ret
.global context_sigenter
context_sigenter:
// %rdi - entry
// %rsi - stack
// %rdx - argument
pushq %r15
pushq %r14
pushq %r13
@@ -128,14 +128,18 @@ context_sigenter:
pushq %rbx
// Use alternate TSS.RSP0 so syscall frame does not get overwritten
// Current stack pointer becomes a new "top" of the stack, essentially
// making it impossible for those scenarios to happen:
// 1. userspace -> kernel ISR transition via TSS starts rewriting syscall/old ISR context
// 2. syscalls issued from signal context overwriting normal context
movq get_cpu(CPU_THREAD), %rbx
movq %rsp, THREAD_RSP0_SIGENTER(%rbx)
movq %rsp, THREAD_RSP0(%rbx)
movq %rsp, THREAD_RSP0_TOP(%rbx)
movq THREAD_RSP0_TOP(%rbx), %rbx
movq get_cpu(CPU_TSS), %r12
movq %rbx, TSS_RSP0(%r12)
movq %rsp, TSS_RSP0(%r12)
// iretq frame
pushq $0x1B
pushq %rsi
@@ -144,4 +148,7 @@ context_sigenter:
pushq $0x23
pushq %rdi
// Move argument
movq %rdx, %rdi
iretq
+23
View File
@@ -1,3 +1,4 @@
#include "sys/amd64/context.h"
#include "sys/amd64/hw/irq.h"
#include "sys/amd64/hw/idt.h"
#include "sys/amd64/cpu.h"
@@ -168,7 +169,29 @@ void yield(void) {
to->state = THREAD_RUNNING;
get_cpu()->thread = to;
context_switch_to(to, from);
// Check if instead of switching to a proper thread context we
// have to use signal handling
if (to->sigq) {
// Pick one signal to handle at a time
int signum = 0;
for (int i = 0; i < 64; ++i) {
if (to->sigq & (1ULL << i)) {
to->sigq &= ~(1ULL << i);
signum = i + 1;
break;
}
}
_assert(signum);
thread_sigenter(signum);
// Theoretically, a rogue thread could steal all the CPU time by sending itself signals
// in normal context, as after returning from thread_sigenter() this code will return
// to a normal execution
// XXX: Maybe makes sense to just yield() here
}
}
void sched_init(void) {
+71 -8
View File
@@ -1,6 +1,7 @@
#include "sys/amd64/hw/timer.h"
#include "sys/amd64/mm/phys.h"
#include "sys/amd64/mm/pool.h"
#include "sys/amd64/context.h"
#include "sys/user/fcntl.h"
#include "sys/binfmt_elf.h"
#include "sys/user/errno.h"
@@ -32,6 +33,7 @@ struct sys_fork_frame {
////
static struct thread *threads_all_head = NULL;
static pid_t last_kernel_pid = 0;
static pid_t last_user_pid = 0;
@@ -43,6 +45,15 @@ pid_t thread_alloc_pid(int is_user) {
}
}
static void thread_add(struct thread *thr) {
if (threads_all_head) {
threads_all_head->g_prev = thr;
}
thr->g_next = threads_all_head;
thr->g_prev = NULL;
threads_all_head = thr;
}
////
static void thread_ioctx_empty(struct thread *thr) {
@@ -68,6 +79,15 @@ void thread_ioctx_fork(struct thread *dst, struct thread *src) {
////
struct thread *thread_find(pid_t pid) {
for (struct thread *thr = threads_all_head; thr; thr = thr->g_next) {
if (thr->pid == pid) {
return thr;
}
}
return NULL;
}
int thread_init(struct thread *thr, uintptr_t entry, void *arg, int user) {
uintptr_t stack_pages = amd64_phys_alloc_page();
_assert(stack_pages != MM_NADDR);
@@ -184,10 +204,17 @@ int thread_init(struct thread *thr, uintptr_t entry, void *arg, int user) {
// - step one
thr->data.rsp0 = (uintptr_t) stack;
thr->sigq = 0;
thread_add(thr);
return 0;
}
// TODO: support kthread forking()
// (Although I don't really think it's very useful -
// threads can just be created by thread_init() and
// sched_queue())
int sys_fork(struct sys_fork_frame *frame) {
struct thread *dst = kmalloc(sizeof(struct thread));
_assert(dst);
@@ -209,6 +236,8 @@ int sys_fork(struct sys_fork_frame *frame) {
dst->data.cr3 = MM_PHYS(space);
dst->space = space;
dst->signal_entry = src->signal_entry;
thread_ioctx_fork(dst, src);
dst->state = THREAD_READY;
@@ -273,7 +302,9 @@ int sys_fork(struct sys_fork_frame *frame) {
// Allocate a new PID for userspace thread
dst->pid = thread_alloc_pid(1);
dst->sigq = 0;
thread_add(dst);
sched_queue(dst);
return dst->pid;
@@ -303,6 +334,7 @@ int sys_execve(const char *path, const char **argp, const char **envp) {
thr->first_child = NULL;
thr->next_child = NULL;
thr->parent = NULL;
thr->sigq = 0;
thr->space = amd64_mm_pool_alloc();
_assert(thr->space);
@@ -319,6 +351,7 @@ int sys_execve(const char *path, const char **argp, const char **envp) {
vfs_close(&thr->ioctx, &fd);
thr->signal_entry = 0;
thr->data.rsp0 = thr->data.rsp0_top;
// Allocate a new user stack
@@ -331,6 +364,17 @@ int sys_execve(const char *path, const char **argp, const char **envp) {
panic("This code shouldn't run\n");
}
void thread_sigenter(int signum) {
struct thread *thr = thread_self;
uintptr_t old_rsp0_top = thr->data.rsp0_top;
// XXX: Either use a separate stack or ensure stuff doesn't get overwritten
uintptr_t signal_rsp3 = thr->data.rsp3_base + 0x800;
context_sigenter(thr->signal_entry, signal_rsp3, signum);
thr->data.rsp0_top = old_rsp0_top;
}
__attribute__((noreturn)) void sys_exit(int status) {
struct thread *thr = thread_self;
kdebug("Thread %d exited with status %d\n", thr->pid, status);
@@ -345,21 +389,40 @@ void thread_sleep(struct thread *thr, uint64_t deadline) {
sched_unqueue(thr, THREAD_WAITING);
}
extern void context_sigenter(uintptr_t entry, uintptr_t stack);
extern void context_sigreturn(void);
void sys_sigreturn(void) {
context_sigreturn();
}
int sys_kill(pid_t pid, int signum) {
struct thread *thr = thread_self;
_assert(thr);
uintptr_t old_rsp0_top = thr->data.rsp0_top;
struct thread *thr;
context_sigenter(thr->signal_entry, 0x100500);
if (pid > 0) {
thr = thread_find(pid);
} else if (pid == 0) {
thr = thread_self;
} else {
// Not implemented
thr = NULL;
}
thr->data.rsp0_top = old_rsp0_top;
if (!thr) {
return -ESRCH;
}
if (signum <= 0 || signum >= 64) {
return -EINVAL;
}
if (!thr) {
// No such process
return -ESRCH;
}
if (thr == thread_self) {
thread_sigenter(signum);
} else {
thr->sigq |= 1ULL << (1 - signum);
}
return 0;
}