Obsah / Ovladače / INT / Funkce správce přerušení
Zdrojový kód:
DRIVERS\INT.ASM
Funkce
správce přerušení
; -----------------------------------------------------------------------------
; Lock/unlock interrupt service lock
; -----------------------------------------------------------------------------
; NOTES: Use macro INTLOCK to lock, INTUNLOCK to unlock.
; -----------------------------------------------------------------------------
; ------------- Macro - lock interrupt service
%macro INTLOCK 0
LOCK_Lock IntLock ; lock interrupt service
%endmacro
; ------------- Macro - unlock interrupt service
%macro INTUNLOCK 0
LOCK_Unlock IntLock ; unlock interrupt service
%endmacro
; ------------- Interrupt service lock
IntLock: SPINLOCK ; interrupt service lock
; ------------- Interrupt service default jump table
IntJumpTab:
%assign IRQN 0
%rep INT_NUM
dd IRQ %+ IRQN
%assign IRQN IRQN+1
%endrep
; ------------- Interrupt descriptor table
IntTab: resb INT_NUM*INTDESC_size
|
Makro INTLOCK
uzamkne zámek správce přerušení IntLock a
umožní tak modifikaci jeho datových struktur. Před
uzamknutím zámku je nutné zakázat přerušení. Makro INTUNLOCK
zámek opět odemkne.
IntJumpTab
je tabulka skoků na obsluhy jednotlivých přerušení.
IntTab
je tabulka popisovačů přerušení.
; -----------------------------------------------------------------------------
; Verify interrupt handler if it can be installed into specific descriptor
; -----------------------------------------------------------------------------
; INPUT: EBX = installed interrupt handler INTHAND
; EDX = interrupt descriptor INTDESC
; OUTPUT: CY = handler cannot be installed
; NOTES: This is local function for IntInstall.
; -----------------------------------------------------------------------------
; ------------- Push registers
IntInstTest: push eax ; push EAX
; ------------- Check if interrupt descriptor is valid
test byte [edx+INTDESC_Flags],INTDESC_VALID ; valid?
jz IntInstTest8 ; interrupt descriptor is not valid
; ------------- Check handler mask of usable IRQs
movzx eax,byte [edx+INTDESC_IRQ] ; EAX <- IRQ number
bt [ebx+INTHAND_IRQMask],eax ; check IRQ mask
jnc IntInstTest8 ; this IRQ is not enabled
; ------------- "No handlers" is suitable for all the time
cmp byte [edx+INTDESC_HandNum],0 ; any handlers?
je IntInstTest9 ; no handlers, it satisfies
; ------------- Check if installed handler can be shared
test byte [ebx+INTHAND_Flags],INT_SHARE ; shared handler?
jz IntInstTest8 ; cannot share
; ------------- Check if last handler can be shared
mov eax,[edx+LIST_Prev] ; EAX <- last handler
test byte [eax+INTHAND_Flags],INT_SHARE ; shared handler?
jnz IntInstTest9 ; can be shared
; ------------- Error, handler cannot be installed
IntInstTest8: stc ; set error flag
; ------------- Pop registers
IntInstTest9: pop eax ; pop EAX
ret
|
Funkce IntInstTest
je pomocná funkce pro funkci instalace obsluhy přerušení
IntInstall. Slouží k provedení testu, zda ovladač
přerušení INTHAND, jehož adresa je funkci předána v
registru EBX, lze nainstalovat do popisovače přerušení
INTDESC, jehož adresa je funkci předána v registru EDX. Pokud
ovladač nelze nainstalovat, funkce navrátí příznak chyby CY.
Funkce ověří
platnost popisovače přerušení (musí mít nastaven příznak
platnosti) a ověří, zda je dané přerušení povolené maskou
přerušení ovladače. Není-li pro toto přerušení žádný
ovladač dosud nainstalován, je instalace povolena. V případě
existujícího jiného ovladače je instalace povolena pouze
tehdy, má-li jak instalovaný tak i stávající ovladač
povolen příznak sdílení.
; -----------------------------------------------------------------------------
; Verify and install IRQ handler
; -----------------------------------------------------------------------------
; INPUT: EBX = installed interrupt handler INTHAND
; EDX = interrupt descriptor INTDESC
; OUTPUT: CY = handler cannot be installed
; NOTES: This is local function for IntInstall.
; -----------------------------------------------------------------------------
; ------------- Check if interrupt handler can be installed
IntInstInst: call IntInstTest ; check if handler can be installed
jc short IntInstInst9 ; handler cannot be installed
; ------------- Push registers
push eax ; push EAX
; ------------- Install handler at the end of interrupt handler chain
mov eax,edx ; EAX <- interrupt descriptor
xchg eax,ebx ; EAX <- handler, EBX <- descriptor
call ListLast ; install at end of handler chain
xchg eax,ebx ; EBX <- interrupt handler
; ------------- Count IRQ handlers
inc byte [edx+INTDESC_HandNum] ; inc. number of handlers
; ------------- Initialize handler
mov [ebx+INTHAND_IntDesc],edx ; set pointer to int. descr.
movzx eax,byte [edx+INTDESC_IRQ] ; EAX <- IRQ number
mov [ebx+INTHAND_IRQ],al ; current IRQ number
; ------------- Install private interrupt vector (here is EAX=IRQ number)
test byte [ebx+INTHAND_Flags],INT_PRIVATE ; private handler?
jz IntInstInst6 ; not private handler
push ebx ; push EBX
push edx ; push EDX
add eax,INT_FIRST ; EAX <- interrupt number
mov edx,[ebx+INTHAND_Int] ; EDX <- interrupt handler
xchg eax,ebx ; EBX <- interrupt number
call SetIntGate ; set interrupt gate
pop edx ; pop EDX
pop ebx ; pop EBX
; ------------- Pop registers
IntInstInst6: pop eax ; pop EAX
; ------------- Activate IRQ
btr dword [ebx+INTHAND_Flags],INT_ACTIVE_BIT ; active?
jnc IntInstInst9 ; should not be active
call IntIntAct ; activate IRQ
clc ; flag, installation OK
IntInstInst9: ret
|
Funkce IntInstInst
je pomocná funkce pro funkci instalace obsluhy přerušení
IntInstall. Funkce otestuje, zda je instalace ovladače
přerušení povolena a pokud ano, provede jeho instalaci.
Instalovaný ovladač přerušení INTHAND je funkci předán v
registru EBX. Popisovač přerušení INTDESC je funkci předán
v registru EDX. Pokud ovladač nelze nainstalovat, funkce
navrátí příznak chyby CY.
Na začátku funkce
je pomocí funkce IntInstTest ověřeno, zda je ovladač možné
nainstalovat. Pokud ano, připojí se ovladač do seznamu
nainstalovaných ovladačů pro dané přerušení. Inkrementuje
se čítač počtu ovladačů přerušení a inicializují se
položky ovladače. Jedná-li se o privátní ovladač
přerušení, nainstaluje se jeho funkce jako vektor přerušení
(namísto standardního vektoru přerušení). Má-li ovladač
přerušení nastaven příznak aktivního přerušení, je
přerušení aktivováno.
; -----------------------------------------------------------------------------
; Install interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = interrupt handler INTHAND with filled up entries
; OUTPUT: EAX = IRQ number (or -1 on error)
; CY = cannot install interrupt handler (no free IRQ line)
; NOTES: It activates interrupt handler if it has active flag set.
; -----------------------------------------------------------------------------
; ------------- Push registers
IntInstall: push edx ; push EDX
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock interrupt service
INTLOCK ; lock interrupt service
; ------------- Try to install into recommended IRQ
movzx edx,byte [ebx+INTHAND_Best] ; EDX <- best IRQ number
cmp dl,INT_NUM ; check valid IRQ number
jae IntInstall2 ; IRQ number is not valid
imul edx,edx,INTDESC_size ; EDX <- descriptor offset
add edx,IntTab ; EDX <- descriptor
call IntInstInst ; try to install handler
jnc IntInstall9 ; handler installed OK
; ------------- Try to install into descriptor with minimal number of handles
IntInstall2: xor eax,eax ; EAX <- 0, number of handlers to find
IntInstall3: mov edx,IntTab ; EDX <- IRQ descriptor table
IntInstall4: cmp al,[edx+INTDESC_HandNum] ; check number of handlers
jne IntInstall5 ; not suitable number of handlers
call IntInstInst ; try to install handler
jnc IntInstall9 ; handler installed OK
IntInstall5: add edx,byte INTDESC_size ; EDX <- next IRQ descriptor
cmp edx,IntTab+INT_NUM*INTDESC_size ; end of table?
jb IntInstall4 ; get next entry
test byte [ebx+INTHAND_Flags],INT_SHARE ; shared handler?
jz IntInstall8 ; cannot share
inc al ; increase number of handlers
jnz IntInstall3 ; try next number of handlers
; ------------- Installation ERROR: unlock interrupt service
IntInstall8: INTUNLOCK ; unlock interrupt service
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
xor eax,eax ; EAX <- 0
dec eax ; EAX <- -1
stc ; flag, installation ERROR
ret
; ------------- Installation OK: unlock interrupt service
IntInstall9: INTUNLOCK ; unlock interrupt service
; ------------- Pop registers
popf ; pop flags
pop edx ; pop EDX
movzx eax,byte [ebx+INTHAND_IRQ] ; EAX <- IRQ number
clc ; flag, installation OK
ret
|
Funkce IntInstall
nainstaluje ovladač přerušení INTHAND, jehož adresa je
funkci předána v registru EBX, do obsluh přerušení. V
případě úspěchu funkce navrací příznak NC a v registru
EAX číslo přerušení, které bylo ovladači přiděleno.
Pokud nebylo nalezeno vyhovující přerušení, funkce navrátí
příznak chyby CY a v registru EAX navrátí -1.
Funkce se nejdříve
pokusí nainstalovat ovladač přerušení pro číslo
přerušení, které je udáno v popisovači přerušení jako
doporučené přerušení. U sdíleného přerušení může být
toto přerušení použito i v případě, že jsou pro ně již
nainstalovány jiné ovladače.
Nelze-li ovladač
nainstalovat pro doporučené přerušení, provede se instalace
pro některé jiné přerušení, které je povoleno položkou
masky povolených přerušení. Je přitom přednostně použito
přerušení s nejmenším počtem nainstalovaných ovladačů.
Je-li před
instalací nastaven příznak aktivity přerušení, je
přerušení pro instalovaný ovladač aktivováno. Jedná-li se
o privátní ovladač přerušení, je adresa jeho obslužné
funkce nainstalována do tabulky vektorů přerušení. Funkce
uzamyká zámek správce přerušení a proto nesmí být volána
z obsluhy přerušení.
; -----------------------------------------------------------------------------
; Uninstall interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = interrupt handler
; NOTES: It deactivates interrupt handler if it was active and then
; saves active bit into handler again (for later activation).
; It locks interrupt service (cannot be called from handler).
; -----------------------------------------------------------------------------
; ------------- Push registers
IntUninstall: push eax ; push EAX
; ------------- Disable interrupts
pushf ; push flags
cli ; disable interrupts
; ------------- Lock interrupt service
INTLOCK ; lock interrupt service
; ------------- Deactivate interrupt handler
mov al,[ebx+INTHAND_Flags] ; AL <- flags
and al,INT_ACTIVE ; AL <- active bit
call IntIntDeact ; deactivate interrupt handler
or [ebx+INTHAND_Flags],al ; return ACTIVE flag
; ------------- Uninstall private interrupt vector
test byte [ebx+INTHAND_Flags],INT_PRIVATE ; private handler?
jz IntUninstall2 ; not private handler
push ebx ; push EBX
push edx ; push EDX
movzx ebx,byte [ebx+INTHAND_IRQ] ; EBX <- current IRQ number
mov edx,[IntJumpTab+ebx*4] ; EDX <- address
add ebx,INT_FIRST ; EBX <- interrupt number
call SetIntGate ; set interrupt gate
pop edx ; pop EDX
pop ebx ; pop EBX
; ------------- Decrease number of handlers
IntUninstall2: mov eax,[ebx+INTHAND_IntDesc] ; EAX <- interrupt descriptor
dec byte [eax+INTDESC_HandNum]; decrease number of handlers
; ------------- Detach interrupt handler from the list
call ListDelEBX ; free IRQ handler
; ------------- Unlock interrupt service
INTUNLOCK ; unlock interrupt service
; ------------- Enable interrupts
popf ; pop flags
; ------------- Pop registers
pop eax ; pop EAX
ret
|
Funkce IntUninstall
odinstaluje ovladač přerušení INTHAND, jehož adresa je
funkci předána v registru EBX. Pokud je ovladač před
odinstalováním aktivní, je deaktivován a přitom je uchován
jeho příznak aktivity - to umožňuje pozdějí automatickou
reaktivaci při nové instalaci. Jedná-li se o privátní
ovladač, je obnoven vektor přerušení na standardní adresu,
která používá standardní obsluhu správce přerušení. Po
snížení čítače počtu nainstalovaných ovladačů je
ovladač odpojen ze seznamu nainstalovaných ovladačů tohoto
přerušení.
; -----------------------------------------------------------------------------
; Activate interrupt handler, can be called only from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = interrupt handler
; NOTES: It does NOT lock interrupt service.
; -----------------------------------------------------------------------------
; ------------- Activate interrupt handler
IntIntAct: bts dword [ebx+INTHAND_Flags],INT_ACTIVE_BIT ; active?
jc IntIntAct9 ; handler is already active
; ------------- Push registers
push eax ; push EAX
; ------------- Count number of active handlers
mov eax,[ebx+INTHAND_IntDesc] ; EAX <- interrupt descriptor
inc byte [eax+INTDESC_Active] ; increase active handlers
cmp byte [eax+INTDESC_Active],1 ; is it first handler?
jne IntIntAct8 ; it is not first handler
; ------------- Enable IRQ
movzx eax,byte [eax+INTDESC_IRQ] ; EAX <- IRQ number
call IRQEnable ; enable IRQ
; ------------- Pop registers
IntIntAct8: pop eax ; pop EAX
IntIntAct9: ret
|
Funkce IntIntAct
aktivuje ovladač přerušení INTHAND, jehož adresa je funkci
předána v registru EBX. Je-li ovladač již aktivní, nic se
neprovede. Funkce inkrementuje čítač aktivních ovladačů
daného přerušení a jedná-li se o první aktivovaný
ovladač, aktivuje se obsluha přerušení pomocí funkce IRQEnable. Tato varianta funkce neuzamyká zámek
správce přerušení a proto může být volána pouze z obsluhy
přerušení.
; -----------------------------------------------------------------------------
; Activate interrupt handler, must NOT be called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = interrupt handler
; NOTES: It DOES lock interrupt service.
; -----------------------------------------------------------------------------
; ------------- Disable interrupts
IntAct: pushf ; push flags
cli ; disable interrupts
; ------------- Lock interrupt service
INTLOCK ; lock interrupt service
; ------------- Activate interrupt handler
call IntIntAct ; activate interrupt handler
; ------------- Unlock interrupt service
INTUNLOCK ; unlock interrupt service
; ------------- Enable interrupts
popf ; pop flags
ret
|
Funkce IntAct
aktivuje ovladač přerušení INTHAND, jehož adresa je funkci
předána v registru EBX. Je-li ovladač již aktivní, nic se
neprovede. Voláním funkce IntIntAct se inkrementuje čítač
aktivních ovladačů daného přerušení a jedná-li se o
první aktivovaný ovladač, aktivuje se obsluha přerušení
pomocí funkce IRQEnable. Tato varianta funkce uzamyká zámek správce
přerušení a proto nesmí být volána z obsluhy přerušení.
; -----------------------------------------------------------------------------
; Deactivate interrupt handler, can be called only from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = interrupt handler
; NOTES: It does NOT lock interrupt service.
; -----------------------------------------------------------------------------
; ------------- Deactivate interrupt handler
IntIntDeact: btr dword [ebx+INTHAND_Flags],INT_ACTIVE_BIT ; active?
jnc IntIntDeact9 ; handler is already inactive
; ------------- Push registers
push eax ; push EAX
; ------------- Count number of active handlers
mov eax,[ebx+INTHAND_IntDesc] ; EAX <- interrupt descriptor
dec byte [eax+INTDESC_Active] ; decrease active handlers
jnz IntIntDeact8 ; another handler remains active
; ------------- Disable IRQ
movzx eax,byte [eax+INTDESC_IRQ] ; EAX <- IRQ number
call IRQDisable ; disable IRQ
call IRQAck ; acknowledge interrupt
; ------------- Pop registers
IntIntDeact8: pop eax ; pop EAX
IntIntDeact9: ret
|
Funkce IntIntDeact
deaktivuje ovladač přerušení INTHAND, jehož adresa je funkci
předána v registru EBX. Je-li ovladač již neaktivní, nic se
neprovede. Funkce dekrementuje čítač aktivních ovladačů
daného přerušení a pokud se jednalo o poslední aktivní
ovladač, deaktivuje se obsluha přerušení pomocí funkce IRQDisable. Po deaktivaci přerušení jsou potvrzena
případná čekající přerušení funkcí IRQAck. Tato varianta funkce neuzamyká zámek
správce přerušení a proto může být volána pouze z obsluhy
přerušení.
; -----------------------------------------------------------------------------
; Deactivate interrupt handler, must NOT be called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT: EBX = interrupt handler
; NOTES: It DOES lock interrupt service.
; -----------------------------------------------------------------------------
; ------------- Disable interrupts
IntDeact: pushf ; push flags
cli ; disable interrupts
; ------------- Lock interrupt service
INTLOCK ; lock interrupt service
; ------------- Deactivate interrupt handler
call IntIntDeact ; deactivate interrupt handler
; ------------- Unlock interrupt service
INTUNLOCK ; unlock interrupt service
; ------------- Enable interrupts
popf ; pop flags
ret
|
Funkce IntDeact
deaktivuje ovladač přerušení INTHAND, jehož adresa je funkci
předána v registru EBX. Je-li ovladač již neaktivní, nic se
neprovede. Voláním funkce IntIntDeact se dekrementuje čítač
aktivních ovladačů daného přerušení a pokud se jednalo o
poslední aktivní ovladač, deaktivuje se obsluha přerušení
pomocí funkce IRQDisable. Po deaktivaci přerušení jsou potvrzena
případná čekající přerušení funkcí IRQAck. Tato varianta funkce uzamyká zámek
správce přerušení a proto nesmí být volána z obsluhy
přerušení.
; -----------------------------------------------------------------------------
; Interrupt service (IRQ 0 to 31, i.e. INT 32 to 63)
; -----------------------------------------------------------------------------
; ------------- IRQ head (push registers and prepare IRQ number)
%assign IRQN 0
%rep INT_NUM-1-6
IRQ %+ IRQN: push eax ; push EAX
mov al,IRQN ; AL <- IRQ number
jmp short IRQInt ; IRQ common service
%assign IRQN IRQN+1
%endrep
IRQ %+ IRQN: push eax ; push EAX
mov al,IRQN ; AL <- IRQ number
; ------------- Push other registers (here AL = IRQ number)
IRQInt: push ebx ; push EBX
push ecx ; push ECX
push edx ; push EDX
push esi ; push ESI
push ds ; push DS
push es ; push ES
cld ; direction up
; ------------- Initialize kernel segments
mov ebx,SYSTEM_DS ; EBX <- system data segment
mov ds,ebx ; DS <- system data segment
mov es,ebx ; ES <- system data segment
; ------------- Lock interrupt service
INTLOCK ; lock interrupt service
; ------------- Interrupt descriptor (-> ECX)
movzx eax,al ; EAX <- IRQ number
imul ecx,eax,INTDESC_size ; ECX <- descriptor offset
add ecx,IntTab ; ECX <- descriptor
; ------------- Walk through the list of handlers
mov edx,ecx ; EDX <- list header
IRQInt2: mov ecx,[ecx+LIST_Next] ; ECX <- next handler
cmp ecx,edx ; end of list?
je IRQInt4 ; end of list
; ------------- Check if interrupt handler is active
test byte [ecx+INTHAND_Flags],INT_ACTIVE ; active?
jz IRQInt2 ; handler is not active
; ------------- Call interrupt handler
mov esi,[ecx+INTHAND_Int] ; ESI <- interrupt handler
mov ebx,[ecx+INTHAND_Data] ; EBX <- user data
call esi ; call interrupt handler
jmp short IRQInt2 ; service next handler
; ------------- Acknowledge interrupt (EAX = interrupt number)
IRQInt4: call IRQAck ; acknowledge interrupt
; ------------- Unlock interrupt service
INTUNLOCK ; unlock interrupt service
; ------------- Pop registers
pop es ; pop ES
pop ds ; pop DS
pop esi ; pop ESI
pop edx ; pop EDX
pop ecx ; pop ECX
pop ebx ; pop EBX
pop eax ; pop EAX
iret
%rep 6
%assign IRQN IRQN+1
IRQ %+ IRQN: push eax ; push EAX
mov al,IRQN ; AL <- IRQ number
jmp short IRQInt ; IRQ common service
%endrep
|
Standardní obsluha
přerušení správce přerušení začíná návěštími IRQ0
až IRQ31. Na začátku každého přerušení
je do registru AL připraveno číslo přerušení. Po
inicializaci segmentových registrů a uzamknutí správce
přerušení se připraví příslušný popisovač přerušení.
Postupně se prochází všechny ovladače přerušení a u těch
ovladačů, které jsou nastavené jako aktivní, se zavolá
obslužná funkce ovladače přerušení. Po obsluze všech
ovladačů přerušení se přerušení potvrdí funkcí IRQAck a funkce odemkne zámek správce
přerušení.
; -----------------------------------------------------------------------------
; Initialize interrupt service
; -----------------------------------------------------------------------------
; NOTES: Interrupt must be disabled at this point.
; -----------------------------------------------------------------------------
; ------------- Initialize IRQ descriptor table
IntInit: mov ebx,IntTab ; EBX <- IRQ
xor eax,eax ; EAX <- 0, IRQ number
IntInit2: call ListInit ; initialize list of IRQ handlers
mov [ebx+INTDESC_IRQ],al ; set IRQ number
call IRQInfo ; check if IRQ channel is valid
jc IntInit3 ; IRQ channel is not valid
mov byte [ebx+INTDESC_Flags],INTDESC_VALID ; valid IRQ line
IntInit3: inc eax ; increase IRQ number
add ebx,byte INTDESC_size ; next entry
cmp al,INT_NUM ; next entry?
jb IntInit2 ; process next entry
; ------------- Initialize IRQ interrupt vectors
xor ebx,ebx ; EBX <- 0
mov bl,INT_FIRST ; BL <- index of first IRQ interrupt
IntInit4: mov edx,[IntJumpTab+ebx*4-INT_FIRST*4] ; EDX<-address
call SetIntGate ; set interrupt gate (it increases EBX)
cmp bl,INT_FIRST+INT_NUM ; all interrupts?
jb IntInit4 ; next interupt
ret
|
Funkce IntInit
provede inicializaci správce přerušení. Funkce musí být
volána po instalaci ovladače řadiče přerušení IRQ, ale před instalací ostatních
ovladačů. Přerušení musí být během provádění funkce
zakázáno.
Funkce prochází
seznam popisovačů přerušení, inicializuje jejich záhlaví
seznamu ovladačů a po ověření platnosti přerušení pomocí
funkce IRQInfo
nastavuje jejich příznak platnosti.
Po inicializaci
popisovačů přerušení funkce nainstaluje standardní vektory
obsluhy přerušení pomocí tabulky IntJumpTab a funkce
SetIntGate.
Obsah / Ovladače / INT / Funkce správce přerušení