diff options
Diffstat (limited to 'kernel/traps.s')
| -rw-r--r-- | kernel/traps.s | 146 |
1 files changed, 132 insertions, 14 deletions
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 |
