Here is the scheduler code that makes it possible to execute handlers with interrupts enabled:
Code: Select all
; This locks the scheduler so it cannot switch thread. This macro is exported by the scheduler.
EnterInt MACRO
mov ax,core_data_sel ; load the unique per-core GDT selector
mov ds,ax
add ds:ps_nesting,1 ; disable scheduling
push ds:ps_sel ; save the core selector
ENDM
; This unlocks the scheduler and checks if some actions needs to be performed after the IRQ. Should be run with interrupts disabled
LeaveInt MACRO
local tucDone
local tucSwap
pop ds ; restore core selector
sub ds:ps_nesting,1 ; enable scheduler if ps_nesting becomes -1
jnc tucDone ; if we are a nested interrupt, just exit
;
mov ax,ds:ps_curr_thread ; check if there is a current thread. If not, the scheduler was running
or ax,ax
jz tucDone
;
test ds:ps_flags,PS_FLAG_TIMER OR PS_FLAG_PREEMPT ; check for pending actions
jnz tucSwap
;
mov ax,ds:ps_wakeup_list ; check for pending thread wakeups
or ax,ax
jz tucDone
tucSwap:
add ds:ps_nesting,1 ; something needs to be handled, so lock again
sti
mov ax,ds
mov fs,ax
OsGate irq_schedule_nr ; schedule
tucDone:
Code: Select all
This is the handler header, which has local variables that stores the entry DS and 48-bit handler procedure
msi_handler_struc STRUC
msi_linear DD ? ; linear address of code segment so it can easily be modified by the setup-code, but not at runtime
msi_handler_ads DD ?,? ; handler procedure
msi_handler_data DW ? ; handler data
msi_handler_struc ENDS
MsiStart:
msi_handler msi_handler_struc <> ; put the header first in the code segment
MsiEntry:
push ds
push es
push fs
pushad
;
EnterInt
sti
;
mov ds,cs:msi_handler_data
call fword ptr cs:msi_handler_ads
;
cli
mov ax,apic_mem_sel
mov ds,ax
xor eax,eax
mov ds:APIC_EOI,eax ; EOI APIC
LeaveInt
;
popad
pop fs
pop es
pop ds
iretd
MsiEnd: