forked from BSB-WS23/mpstubs
121 lines
2.8 KiB
NASM
121 lines
2.8 KiB
NASM
; Low-level stuff required for interrupt handling
|
|
; The "actual" code to be executed is implemented in the C function "interrupt_handler"
|
|
|
|
[EXTERN interrupt_handler]
|
|
|
|
[SECTION .text]
|
|
|
|
; Entry function for interrupt handling (of any vector)
|
|
|
|
; The interrupt handling of each vector has to be started in assembler to store
|
|
; the scratch registers (see SystemV calling conventions) before the actual
|
|
; high-level (C++) interrupt_handler function can be executed.
|
|
;
|
|
; For this purpose we use a macro to generate a customized entry function for
|
|
; each interrupt vector (0-255), wheres the vector itself is the first parameter.
|
|
; The second parameter is a boolean flag indicating whether an error code is
|
|
; placed on the stack for the corresponding trap (by the CPU).
|
|
;
|
|
; Usage: IRQ <vector> <error-code?>
|
|
%macro IRQ 2
|
|
ALIGN 16
|
|
interrupt_entry_%1:
|
|
%if %2 == 0
|
|
; If the vector is not a trap with an error code automatically pushed
|
|
; on the stack, the entry function pushes a zero instead
|
|
; to retain an identical stack layout in each case.
|
|
push 0
|
|
%endif
|
|
|
|
; The interrupt may be triggered asynchronously, therefore the whole context
|
|
; has to be saved and restored, or the interrupted code might not be able to
|
|
; continue. The C++ compiler will only generates code to preserve
|
|
; non-scratch registers in the high-level interrupt handler -- the scratch
|
|
; registers have to be saved (and restored later) manually!
|
|
push rax
|
|
push rcx
|
|
push rdx
|
|
push rsi
|
|
push rdi
|
|
push r8
|
|
push r9
|
|
push r10
|
|
push r11
|
|
|
|
; Clear direction flag for string operations
|
|
cld
|
|
|
|
; Assign vector as first parameter for the high-level interrupt handler
|
|
mov rdi, %1
|
|
|
|
; Assign pointer to the context (= interrupt stack) as second parameter
|
|
mov rsi, rsp
|
|
|
|
; Call the high-level interrupt handler routine
|
|
call interrupt_handler
|
|
|
|
; Restore scratch registers
|
|
pop r11
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop rdi
|
|
pop rsi
|
|
pop rdx
|
|
pop rcx
|
|
pop rax
|
|
|
|
; Drop error code (or the fake zero value)
|
|
add rsp, 8
|
|
|
|
; Return from interrupt
|
|
iretq
|
|
%endmacro
|
|
|
|
; For traps the CPU sometimes pushes an error code onto the stack.
|
|
; These vectors are documented in the manual (or instead use the
|
|
; [osdev wiki](https://wiki.osdev.org/Exceptions) for a handy list).
|
|
; Therefore we manually call the macro for the corresponding traps.
|
|
IRQ 0, 0
|
|
IRQ 1, 0
|
|
IRQ 2, 0
|
|
IRQ 3, 0
|
|
IRQ 4, 0
|
|
IRQ 5, 0
|
|
IRQ 6, 0
|
|
IRQ 7, 0
|
|
IRQ 8, 1
|
|
IRQ 9, 0
|
|
IRQ 10, 1
|
|
IRQ 11, 1
|
|
IRQ 12, 1
|
|
IRQ 13, 1
|
|
IRQ 14, 1
|
|
IRQ 15, 0
|
|
IRQ 16, 0
|
|
IRQ 17, 1
|
|
|
|
; All subsequent interrupts (18 - 255) have no error code,
|
|
; therefore we use a loop calling the macro.
|
|
%assign i 18
|
|
%rep 238
|
|
IRQ i, 0
|
|
%assign i i+1
|
|
%endrep
|
|
|
|
[SECTION .data]
|
|
|
|
; Create a function pointer array for each interrupt entry
|
|
; (to be used in C++ for IDT::handle)
|
|
[GLOBAL interrupt_entry]
|
|
|
|
interrupt_entry:
|
|
%macro interrupt_vector 1
|
|
dq interrupt_entry_%1
|
|
%endmacro
|
|
%assign i 0
|
|
%rep 256
|
|
interrupt_vector i
|
|
%assign i i+1
|
|
%endrep
|