; ; traps.s provides entry points to the exception handlers ; 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_trap extern reschedule extern sighandler_default extern ticked ; here we define offsets into task_struct. ; if task_struct is modified, this must be ; too. %define ts_signal 6 %define ts_sig_handlers 14 %define ts_tss_esp0 146 %macro SAVE 0 pusha ; save the data segment selectors mov ax, ds push ax ; switch to kernel data segments mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax ; restore EAX (needed for syscalls) mov eax, [esp+30] %endmacro %macro SAVE_ERR 0 pusha ; fetch the error code that was pushed mov ebx, [esp+32] ; save the data segment selectors mov ax, ds push ax ; switch to kernel data segments mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax %endmacro %macro RESTORE 0 ; restore the data segment selectors pop ax mov ds, ax mov es, ax mov fs, ax mov gs, ax popa iret %endmacro %macro RESTORE_ERR 0 ; restore the data segment selectors pop ax mov ds, ax mov es, ax mov fs, ax mov gs, ax popa add esp, 4 iret %endmacro traps: dd exc_div dd exc_debug dd exc_nmi dd exc_brk dd exc_ovf dd exc_bounds dd exc_invop dd exc_nomath dd exc_dbf dd exc_coseg dd exc_invtss dd exc_segnotpres dd exc_stackf dd exc_gprot dd exc_pagef dd exc_reserved dd exc_mathf dd exc_alignchk dd exc_machinechk dd exc_simdfp dd exc_virt dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved dd exc_reserved traps_init: push ebp mov ebp, esp mov ecx, 0 mov edx, idt .loop: mov eax, ecx shl eax, 2 add eax, traps mov eax, [eax] mov [edx], ax shr eax, 16 mov [edx+6], ax mov word [edx+2], 0x08 mov byte [edx+4], 0x00 mov byte [edx+5], 0x8E inc ecx cmp ecx, 32 je .end add edx, 8 jmp .loop .end: pop ebp ret exc_div: SAVE RESTORE exc_debug: SAVE RESTORE exc_nmi: SAVE RESTORE exc_brk: SAVE RESTORE exc_ovf: SAVE RESTORE exc_bounds: SAVE RESTORE exc_invop: SAVE RESTORE exc_nomath: SAVE RESTORE exc_dbf: SAVE_ERR RESTORE_ERR exc_coseg: SAVE RESTORE exc_invtss: SAVE_ERR RESTORE_ERR exc_segnotpres: SAVE_ERR RESTORE_ERR exc_stackf: SAVE_ERR RESTORE_ERR exc_gprot: SAVE_ERR push ebx push .fmt call printk add esp, 8 call panic RESTORE_ERR .fmt: db "General Protection Fault (0x%08x)", 10, 0 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 push .fmt call printk add esp, 12 call panic RESTORE_ERR .fmt: db "Page Fault (illegal memory access: 0x%08x:0x%02x)", 10, 0 exc_mathf: SAVE RESTORE exc_alignchk: SAVE RESTORE exc_machinechk: SAVE RESTORE exc_simdfp: SAVE RESTORE exc_virt: SAVE RESTORE exc_reserved: SAVE RESTORE syscall_handler: SAVE mov dword [ticked], 0 ; push the arguments onto the stack, then ; calculate the offset for the appropriate ; handler function and call it push edx push ecx push ebx and eax, 0xFF shl eax, 2 add eax, call_table call [eax] add esp, 12 ; preserve the syscall's return value mov [esp+30], eax ; check if the timer fired while we were in kernel mode mov eax, [ticked] jz .skip call reschedule .skip: ; check pending signals and handle them (if any) mov eax, esp push eax call check_signals add esp, 4 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 inc ecx 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: mov ebx, esp push ebx call check_signals add esp, 4 ; 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 mov ebp, esp push syscall_handler push dword 3 push dword 0x80 call register_trap add esp, 12 pop ebp ret