diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/asm.s | 1 | ||||
| -rw-r--r-- | kernel/boot.s | 32 | ||||
| -rw-r--r-- | kernel/memory.c | 1 | ||||
| -rw-r--r-- | kernel/sched.c | 19 | ||||
| -rw-r--r-- | kernel/sys.c | 21 | ||||
| -rw-r--r-- | kernel/timer.s | 3 | ||||
| -rw-r--r-- | kernel/traps.s | 146 |
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 |
