Global thread list, proper kill() implementation, proper signal entry in
sched
This commit is contained in:
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user