diff options
| author | Jake Mannens <jake72360@gmail.com> | 2018-07-25 01:17:39 +1000 |
|---|---|---|
| committer | Jake Mannens <jake72360@gmail.com> | 2018-07-25 01:17:39 +1000 |
| commit | 45819c035d0b92275de68e559f066cbe50996926 (patch) | |
| tree | 0ae32cffc4567dee891ecca9e5162731b341d1a0 /kernel/traps.s | |
| parent | a0aa45ea6f4027dd4080cf1456a2fc5d9e94f87b (diff) | |
Fixed a bug in switch_to() in which the value of EBX was not popped
prior to return. This meant that switching to the same task did not
abort properly as the incorrect return address was popped off the stack.
Fixed a bug where the task register was not initialized before the
scheduler. This meant that on the first task switch, the CPU would dump
it's current state to a random address (0 most likely), potentially
corrupting important data. This has been corrected by introducing a
'garbage TSS' (and associated descriptor in the GDT) which is selected
before the scheduler is initialized as a safe place for the data to be
written.
Modified the scheduler so that it now waits indefinitely until a task
becomes ready to run. This fixes the possible bug where the scheduler
won't re-schedule the currently running task if it is the only task on
the system.
Add signal handling capabilities to the kernel. The bulk of this is
present in the subroutine check_signals() defined in traps.s. This
function is called on every timer tick and system call prior to
userspace return. The subroutine operates by pushing fake state
information onto the kernel's stack, then using it to return to
userspace. Prior to this, the subroutine pushes the return address
0xFFFFE000 onto the user's stack. This address corresponds to the
unmapped page located between the top of the user's stack (lower) and
the kernel's stack page (upper). When the user's signal handler tries to
return, it will cause a page fault that will be used as a notification
mechanism to inform the kernel that the signal handler is done. The
kernel will then switch to the originally pushed state information and
use it to return the task to the original execution state. Due to it's
nature, check_signals() must only be called prior to exiting the kernel
since it may not return.
Added the header file 'signal.h' which defines (most) of the POSIX
signals as well as the prototype for the signal() function.
Added the 'signal' element to the task structure. This field is a bitmap
of all currently pending signals.
Added the 'sig_handlers' element to the task structure. This is an array
of all user-defined signal handlers. Currently, a value of 0 indicates
the default handler should be used whilst any other value is considered
to be the address of a userspace signal handler. The ability to ignore a
signal is not yet present but will be added sometime soon.
Added the sys_signal system call to register a signal.
Added the stub function sighandler_default() to sched.c which handles
all signals not caught by the user.
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 |
