summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/asm.s1
-rw-r--r--kernel/boot.s32
-rw-r--r--kernel/memory.c1
-rw-r--r--kernel/sched.c19
-rw-r--r--kernel/sys.c21
-rw-r--r--kernel/timer.s3
-rw-r--r--kernel/traps.s146
7 files changed, 201 insertions, 22 deletions
diff --git a/kernel/asm.s b/kernel/asm.s
index aced9b4..8b5c231 100644
--- a/kernel/asm.s
+++ b/kernel/asm.s
@@ -15,6 +15,7 @@ switch_to:
mov ebx, [ctask]
cmp ebx, [ebp+12]
jne .skip
+ pop ebx
pop ebp
ret
.skip:
diff --git a/kernel/boot.s b/kernel/boot.s
index 96f0e43..21b248c 100644
--- a/kernel/boot.s
+++ b/kernel/boot.s
@@ -11,6 +11,8 @@ extern syscall_init
extern timer_init
extern traps_init
+%define NRTASKS 64
+
; Multiboot header
section .mboothdr
mboot_hdr:
@@ -80,16 +82,22 @@ gdt:
db 0xCF
db 0x00
; empty TSS slots for our C scheduler
- times 64 dq 0
+ times NRTASKS dq 0
+ ; garbage TSS descriptor
+ dq 0
.end:
gdtp:
dw (gdt.end-gdt-1)
dd gdt
+garbage_tss:
+ times 104 db 0
+
flush_gdt:
push ebp
mov ebp, esp
+ push ebx
; now, load the GDT
lgdt [gdtp]
mov ax, 0x10
@@ -100,6 +108,28 @@ flush_gdt:
mov ss, ax
jmp 0x08:.end
.end:
+ ; finally, we load the task register with
+ ; a pointer to our dummy TSS. this is
+ ; necessary since the CPU *must* save the
+ ; previous task state *somewhere* before
+ ; switching tasks. to avoid overwriting
+ ; any sensitive information, we reserve
+ ; 104 bytes for a *garbage* TSS.
+ mov eax, 5
+ add eax, NRTASKS
+ shl eax, 3
+ mov ebx, eax
+ add ebx, gdt
+ mov edx, garbage_tss
+ mov dword [ebx], 103 ; limit - 1
+ mov [ebx+2], dx
+ shr edx, 16
+ mov [ebx+4], dl
+ mov [ebx+7], dh
+ mov byte [ebx+5], 0x89
+ mov byte [ebx+6], 0x00
+ ltr ax
+ pop ebx
pop ebp
ret
diff --git a/kernel/memory.c b/kernel/memory.c
index 465b44f..0c73970 100644
--- a/kernel/memory.c
+++ b/kernel/memory.c
@@ -85,6 +85,7 @@ void *map_page(void *page) {
/*
* invalidate any cached entries for
* the mapped page window.
+ * TODO: skip this on i386
*/
__asm__ ("invlpg %0" :: "m" (mapped_page));
diff --git a/kernel/sched.c b/kernel/sched.c
index efbb80a..529db61 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -1,6 +1,9 @@
#include <asm/system.h>
+#include <kernel/con.h>
+#include <kernel/kernel.h>
#include <kernel/memory.h>
#include <kernel/sched.h>
+#include <signal.h>
#include <string.h>
#include <sys/types.h>
@@ -144,14 +147,12 @@ struct task_struct *create_proc(void *bin, size_t len) {
/* load in the user binary */
memcpy(USRSTART, bin, len);
- /*
- * here we initialize the task struct.
- * currently, we only setup state information
- * necessary to begin execution.
- */
+ /* initialize the task struct */
memset(task, 0, sizeof(struct task_struct));
task->pid = nextpid;
task->state = TSTATE_RUNNING;
+
+ /* initialize the task's state */
task->tss.cr3 = (uint32_t) pdir;
task->tss.cs = 0x1B;
task->tss.ds = 0x23;
@@ -185,7 +186,6 @@ void sched_init(void) {
create_proc(&_usrbin_start, (size_t) &_usrbin_size);
create_proc(&_usrbin_start, (size_t) &_usrbin_size);
- create_proc(&_usrbin_start, (size_t) &_usrbin_size);
switch_to(0, &tasks[0]);
}
@@ -198,11 +198,12 @@ void reschedule(void) {
return;
n = ctaskn;
- for(i = 0; i < NRTASKS; i++) {
+ while(1) {
n = (n + 1) % NRTASKS;
if(tasks[n].pid && tasks[n].state == TSTATE_RUNNING)
break;
}
+
switch_to(n, &tasks[n]);
}
@@ -217,3 +218,7 @@ void wake_up(struct task_struct **task) {
(*task)->state = TSTATE_RUNNING;
*task = NULL;
}
+
+void sighandler_default(uint32_t sig) {
+ printk("[kernel] Handling signal 0x%02x for PID 0x%04x\n", sig, ctask->pid);
+}
diff --git a/kernel/sys.c b/kernel/sys.c
index 8c33fdf..86fae41 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1,13 +1,20 @@
#include <errno.h>
#include <kernel/con.h>
#include <kernel/sched.h>
+#include <signal.h>
#include <stdint.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
extern uint32_t ticks;
int sys_puts(char *s) {
+ uint32_t sigs = 5;
+ printk("[kernel] Setting signals %01x for PID 0x%04x\n", sigs, ctask->pid);
+ ctask->signal |= sigs;
+
+ printk("[%04x] ", ctask->pid);
return printk(s);
}
@@ -27,6 +34,20 @@ void *sys_getpdir(void) {
return (void*) pdir;
}
+void *sys_signal(int sig, void (*func)(int)) {
+ if(sig < 0 || sig >= NRSIG)
+ return NULL;
+
+ /* SIGKILL and SIGSTOP cannot be caught */
+ if(sig == SIGKILL || sig == SIGSTOP)
+ return NULL;
+
+ printk("Task %x registered new signal handler 0x%08x for signal %01x\n", ctask->pid, (uint32_t) func, sig);
+ ctask->sig_handlers[sig] = func;
+
+ return func;
+}
+
int sys_dummy(void) {
return -ENOSYS;
}
diff --git a/kernel/timer.s b/kernel/timer.s
index 760bcd7..c3af34b 100644
--- a/kernel/timer.s
+++ b/kernel/timer.s
@@ -1,5 +1,6 @@
global ticks
global timer_init
+extern check_signals
extern register_isr
extern sched_tick
@@ -24,6 +25,8 @@ tick_handler:
; call the scheduler
call sched_tick
+ ; handle any pending signals before returning to normal execution
+ call check_signals
; restore the data segment selectors
pop ax
diff --git a/kernel/traps.s b/kernel/traps.s
index e084ac8..73e7f64 100644
--- a/kernel/traps.s
+++ b/kernel/traps.s
@@ -3,13 +3,24 @@
; as well as the system call interface.
;
+global check_signals
global syscall_init
global traps_init
extern call_table
+extern ctask
extern idt
extern panic
extern printk
extern register_isr
+extern sighandler_default
+
+; here we define offsets into task_struct.
+; if task_struct is modified, this must be
+; too.
+
+%define ts_signal 6
+%define ts_sig_handlers 10
+%define ts_tss_esp0 142
%macro SAVE 0
pusha
@@ -64,19 +75,6 @@ extern register_isr
iret
%endmacro
-%macro RESTORE_SYS 0
- ; preserve our modified EAX (return value for syscalls)
- mov [esp+30], eax
- ; restore the data segment selectors
- pop ax
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
- popa
- iret
-%endmacro
-
traps:
dd exc_div
dd exc_debug
@@ -204,6 +202,34 @@ exc_gprot:
exc_pagef:
SAVE_ERR
+ ; here we check if the page fault was
+ ; caused by a process trying to return
+ ; from a signal handler (this should cause
+ ; a page fault for accessing page
+ ; 0xFFFFE000 during the handler's
+ ; execution).
+
+ ; check if the offending address is 0xFFFFE000
+ mov eax, cr2
+ cmp eax, 0xFFFFE000
+ jne .skip
+ ; check if we're actually handling a signal
+ mov eax, [ctask]
+ mov eax, [eax+ts_tss_esp0]
+ cmp eax, 0xFFFFFFFF
+ je .skip
+ ; final checks that a user-initiated read caused the fault
+ mov eax, ebx
+ and eax, 6
+ cmp eax, 4
+ jne .skip
+ jmp sigret
+.skip:
+
+ ; if the above conditions are false, a
+ ; signal handler was not trying to return,
+ ; so we handle the page fault normally
+
push ebx
mov ebx, cr2
push ebx
@@ -243,6 +269,9 @@ exc_reserved:
syscall_handler:
SAVE
+ ; push the arguments onto the stack, then
+ ; calculate the offset for the appropriate
+ ; handler function and call it
push edx
push ecx
push ebx
@@ -251,9 +280,98 @@ syscall_handler:
add eax, call_table
call [eax]
add esp, 12
+ ; preserve the syscall's return value
+ mov [esp+30], eax
+ mov eax, esp
+ ; check pending signals and handle them (if any)
+ push eax
+ call check_signals
+ add esp, 4
- RESTORE_SYS
+ RESTORE
+; void check_signals(void *saved_state)
+check_signals:
+ push ebp
+ mov ebp, esp
+ push ebx
+ push esi
+ ; abort if a signal is already being handled
+ mov eax, [ctask]
+ mov ebx, [eax+ts_tss_esp0]
+ cmp ebx, 0xFFFFFFFF
+ jne .end
+ ; check if there are any pending signals
+ ; if so, we'll resume at the signal
+ ; handler if a custom handler has been
+ ; defined, otherwise, we run the default
+ ; handler.
+ mov edx, [eax+ts_signal]
+ cmp edx, 0
+ je .end
+ ; find which signal has been triggered (then clear it's flag)
+ bsf ecx, edx
+ mov ebx, 1
+ shl ebx, cl
+ xor ebx, 0xFFFFFFFF
+ and dword [eax+ts_signal], ebx
+ mov ebx, ecx
+ ; calculate offset to the handler function
+ shl ecx, 2
+ add ecx, eax
+ add ecx, ts_sig_handlers
+ cmp dword [ecx], 0
+ jne .custom_handler
+ ; call the default handler
+ push ebx
+ call sighandler_default
+ add esp, 4
+ jmp .end
+.custom_handler:
+ ; save our stack pointer
+ mov esi, [ebp+8]
+ mov [eax+ts_tss_esp0], esi
+ ; here, we push the return address onto
+ ; the user's stack. we give a bogus return
+ ; address of 0xFFFFE000 (the second-last
+ ; page) to deliberately trigger a page
+ ; fault that will alert us when the signal
+ ; handler tried to return.
+ mov ebx, [ebp+8]
+ mov esi, ebx
+ add esi, 46
+ mov esi, [esi]
+ sub esi, 4
+ mov dword [esi], 0xFFFFE000
+ ; push a new state onto our stack
+ push dword [ebx+50] ; SS
+ push dword esi ; ESP
+ push dword [ebx+42] ; EFLAGS
+ push dword [ebx+38] ; CS
+ push dword [ecx] ; EIP
+ push dword 0 ; EAX
+ push dword 0 ; ECX
+ push dword 0 ; EDX
+ push dword 0 ; EBX
+ push dword 0 ; ESP
+ push dword [ebx+10] ; EBP
+ push dword 0 ; ESI
+ push dword 0 ; EDI
+ push word [ebx] ; DS
+ RESTORE
+.end:
+ pop esi
+ pop ebx
+ pop ebp
+ ret
+
+sigret:
+ call check_signals
+ ; restore our original stack frame and reset the TSS's ESP0
+ mov esp, [eax+ts_tss_esp0]
+ mov dword [eax+ts_tss_esp0], 0xFFFFFFFF
+ ; finally, perform the normal syscall return
+ RESTORE
syscall_init:
push ebp