In windows, the actual interrupt processing is registered through IoConnectInterrupt ....
NTSTATUS
NTAPI
IoConnectInterrupt (out pkinterrupt * InterruptObject,
IN PKSERVICE_ROUTINE ServiceRoutine,
In pvoid ServiceContext,
IN PKSPIN_LOCK SpinLock,
In ulong Vector,
In kirql Irql,
In kirql SynchronizeIrql,
IN KINTERRUPT_MODE InterruptMode,
In boolean ShareVector,
In kaffinity ProcessorEnableMask,
In boolean FloatingSave)
{
PKINTERRUPT Interrupt;
PKINTERRUPT InterruptUsed;
PIO_INTERRUPT IoInterrupt;
PKSPIN_LOCK SpinLockUsed;
BOOLEAN FirstRun;
CCHAR Count = 0;
KAFFINITY Affinity;
PAGED_CODE ();
/* Assume failure */
* InterruptObject = NULL;
/* Get the affinity */
Affinity = ProcessorEnableMask & KeActiveProcessors;/* Get CPU Affinity ....*/
While (Affinity)/* The ISR on the Multi-processing platform needs to be connected to each CPU corresponding to Affinity .. */
{
/* Increase count */
If (Affinity & 1) Count ++;/* calculate the count to be processed .... */
Affinity> = 1;
}
/* Make sure we have a valid CPU count */
If (! Count) return STATUS_INVALID_PARAMETER;
/* Allocate the array of I/O Interrupts */
IoInterrupt = ExAllocatePoolWithTag (NonPagedPool,
(Count-1) * sizeof (KINTERRUPT) +
Sizeof (IO_INTERRUPT ),
TAG_KINTERRUPT);/* allocate memory for KINTERRUPT in non-Paging memory... */
If (! IoInterrupt) return STATUS_INSUFFICIENT_RESOURCES;
/* Select which Spinlock to use */
SpinLockUsed = SpinLock? SpinLock: & IoInterrupt-> SpinLock;
/* If SpinLock is specified. Use the SpinLock In the parameter; otherwise, use the allocated KINTERRUPT's SpinLock .....*/
/* We first start with a built-in Interrupt inside the I/O Structure */
* InterruptObject = & IoInterrupt-> FirstInterrupt;
Interrupt = (PKINTERRUPT) (IoInterrupt + 1 );
FirstRun = TRUE;
/* Start with a fresh structure */
RtlZeroMemory (IoInterrupt, sizeof (IO_INTERRUPT ));
/* Now create all the interrupts */
Affinity = ProcessorEnableMask & KeActiveProcessors;
For (Count = 0; Affinity; Count ++, Affinity> = 1)/* processes the processor that needs to be connected to the interrupt cyclically, and then the connection is interrupted .... */
{
/* Check if it's enabled for this CPU */
If (Affinity & 1)
{
/* Check which one we will use */
InterruptUsed = FirstRun? & IoInterrupt-> FirstInterrupt: Interrupt;
/* Initialize it */
KeInitializeInterrupt (InterruptUsed,
ServiceRoutine,
ServiceContext,
SpinLockUsed,
Vector,
Irql,
SynchronizeIrql,
InterruptMode,
ShareVector,
Count,
FloatingSave);/* initialize KINTERRUPT ...*/
/* Connect it */
If (! KeConnectInterrupt (InterruptUsed)/* after initialization, the actual connection is established here... */
{
/* Check how far we got */
If (FirstRun)
{
/* We failed early so just free this */
ExFreePool (IoInterrupt );
}
Else
{
/* Far enough, so disconnect everything */
IoDisconnectInterrupt (& IoInterrupt-> FirstInterrupt );
}
/* And fail */
Return STATUS_INVALID_PARAMETER;
}
/* Now we 've used up our First Run */
If (FirstRun)
{
FirstRun = FALSE;
}
Else
{
/* Move on to the next one */
IoInterrupt-> Interrupt [(UCHAR) Count] = Interrupt ++;
}
}
}
/* Return Success */
Return STATUS_SUCCESS;
}
From this we can see that the more important functions are KeInitializeInterrupt and KeConnectInterrupt .....
VOID
NTAPI
KeInitializeInterrupt (in pkinterrupt Interrupt,
IN PKSERVICE_ROUTINE ServiceRoutine,
In pvoid ServiceContext,
IN PKSPIN_LOCK SpinLock,
In ulong Vector,
In kirql Irql,
In kirql SynchronizeIrql,
IN KINTERRUPT_MODE InterruptMode,
In boolean ShareVector,
In char ProcessorNumber,
In boolean FloatingSave)
{
ULONG I;
PULONG DispatchCode = & Interrupt-> DispatchCode [0], Patch = DispatchCode;
/* The actual entry of each KIINTERRUPT is the DispatchCode array .. This array is the machine code generated after the assembly of the interrupt processing entry code .... */
/* Set the Interrupt Header */
Interrupt-> Type = InterruptObject;
Interrupt-> Size = sizeof (KINTERRUPT );
/* Check if we got a spinlock */
If (SpinLock)/* sets the spin lock of this interrupt object... */
{
Interrupt-> ActualLock = SpinLock;
}
Else
{
/* This means we'll be usin the built-in one */
KeInitializeSpinLock (& Interrupt-> SpinLock );
Interrupt-> ActualLock = & Interrupt-> SpinLock;
}
/* Set the other settings */
Interrupt-> ServiceRoutine = ServiceRoutine;/* initialize each domain */
Interrupt-> ServiceContext = ServiceContext;
Interrupt-> Vector = Vector;
Interrupt-> Irql = Irql;
Interrupt-> SynchronizeIrql = SynchronizeIrql;
Interrupt-> Mode = InterruptMode;
Interrupt-> ShareVector = ShareVector;
Interrupt-> Number = ProcessorNumber;
Interrupt-> FloatingSave = FloatingSave;
Interrupt-> TickCount = (ULONG)-1;
Interrupt-> DispatchCount = (ULONG)-1;
/* Loop the template in memory */
For (I = 0; I <KINTERRUPT_DISPATCH_CODES; I ++)/* copy the machine commands of the assembly code KiInterruptTemplate to DispatchCode... note .. This is important... */
{
/* Copy the dispatch code */
* DispatchCode ++ = KiInterruptTemplate [I];
}
/* Sanity check */
ASSERT (ULONG_PTR) & KiChainedDispatch2ndLvl-
(ULONG_PTR) KiInterruptTemplate <= (KINTERRUPT_DISPATCH_CODES * 4 ));
/* Jump to the last 4 bytes */
Patch = (PULONG) (ULONG_PTR) Patch +
(ULONG_PTR) & KiInterruptTemplateObject-
(ULONG_PTR) KiInterruptTemplate)-4 );
/* Note that. KiInterruptTemplate is just a template... Is a framework. So here we need to move to the last 4 bytes... To interrupt the service
The address of the number is written here ..... This way... The specific service interruption is redirected... */
/* Apply the patch */
* Patch = PtrToUlong (Interrupt);/* write the KIINTERRUPT address to the last 4 bytes of the Interrupt processing template .... */
/* Disconnect it at first */
Interrupt-> Connected = FALSE;
}
Now let's take a look at the code in KiInterruptTemplate .. That's it... The KiInterruptTemplate code is in ntoskrnl/ke/i386/Traps. s ....
. Func KiInterruptTemplate
_ KiInterruptTemplate:
/* Enter interrupt trap */
INT_PROLOG kit_a, kit_t, DoPushFakeErrorCode
_ KiInterruptTemplate2ndDispatch:
/* Dummy code, will be replaced by the address of the KINTERRUPT */
Mov edi, 0
_ KiInterruptTemplateObject:/* The jump instruction address will be replaced with the actual address of the function to be transferred to interrupt processing in the KeConnectInterrupt function... */
/* Dummy jump, will be replaced by the actual jump */
Jmp _ KeSynchronizeExecution @ 12
_ KiInterruptTemplateDispatch:
/* Marks the end of the template so that the jump above can be edited */
TRAP_FIXUPS kit_a, kit_t, DoFixupV86, DoFixupAbios
. Endfunc
/* Because
Patch = (PULONG) (ULONG_PTR) Patch +
(ULONG_PTR) & KiInterruptTemplateObject-
(ULONG_PTR) KiInterruptTemplate)-4 );
So here, the Patch corresponds to mov edi, and the "0" value of the 0 command is counted immediately ..... After replacement .. This command becomes mov edi and PKiInterrupt .. */
Next, we will analyze another important KeConnectInterrupt function ..
BOOLEAN
NTAPI
KeConnectInterrupt (in pkinterrupt Interrupt)
{
BOOLEAN Connected, Error, Status;
KIRQL Irql, OldIrql;
UCHAR Number;
ULONG Vector;
DISPATCH_INFO Dispatch;
/* Get data from interrupt */
Number = Interrupt-> Number;
Vector = Interrupt-> Vector;
Irql = Interrupt-> Irql;
/* Validate the settings */
If (Irql> HIGH_LEVEL) |
(Number> = KeNumberProcessors) |
(Interrupt-> SynchronizeIrql <Irql) |
(Interrupt-> FloatingSave ))
{
Return FALSE;
}
/* Set defaults */
Connected = FALSE;
Error = FALSE;
/* Set the system affinity and acquire the dispatcher lock */
KeSetSystemAffinityThread (1 <Number );
OldIrql = KiAcquireDispatcherLock ();
/* Check if it's already been connected */
If (! Interrupt-> Connected)
{
/* Get vector dispatching information */
KiGetVectorDispatch (Vector, & Dispatch);/* obtain the interrupt distribution information corresponding to the interrupt Number of the Vector .... Such as whether the interrupted service has been connected... In addition, whether the interrupt is shared or independent .... */
/* Check if the vector is already connected */
If (Dispatch. Type = NoConnect)/* if no ISR is connected to this interrupt number .... */
{
/* Do the connection */
Interrupt-> Connected = TRUE;
/* Initialize the list */
InitializeListHead (& Interrupt-> InterruptListEntry );
/* Connect and enable the interrupt */
KiConnectVectorToInterrupt (Interrupt, NormalConnect);/* the connection is interrupted... */
Status = HalEnableSystemInterrupt (Vector, Irql, Interrupt-> Mode);/* Call HAL to open the Interrupt corresponding to the Interrupt number, because unused interruptions are blocked... */
If (! Status) Error = TRUE;
}
Else if (Dispatch. Type! = UnknownConnect )&&
(Interrupt-> ShareVector )&&
(Dispatch. Interrupt-> ShareVector )&&
(Dispatch. Interrupt-> Mode = Interrupt-> Mode ))
{
/* The vector is shared and the interrupts are compatible */
ASSERT (FALSE); // FIXME: not yet supported/TESTED
Interrupt-> Connected = TRUE;
ASSERT (Irql <= SYNCH_LEVEL );
/* Check if this is the first chain */
If (Dispatch. Type! = ChainConnect)/* If the interrupt is shared .. And there is no entry point required for shared interrupt .... ... */
{
/* Setup the chainned handler */
KiConnectVectorToInterrupt (Dispatch. Interrupt, ChainConnect );
}
/* Insert into the interrupt list */
InsertTailList (& Dispatch. Interrupt-> InterruptListEntry,
& Interrupt-> InterruptListEntry);/* connect the established Interrupt to the queue corresponding to this Interrupt number... */
}
}
/* Unlock the dispatcher and revert affinity */
KiReleaseDispatcherLock (OldIrql );
KeRevertToUserAffinityThread ();
/* Check if we failed while trying to connect */
If (Connected) & (Error)/* if an Error occurs... */
{
DPRINT1 ("HalEnableSystemInterrupt failed/n ");
KeDisconnectInterrupt (Interrupt );
Connected = FALSE;
}
/* Return to caller */
Return Connected;
}
The actual connection function is KiConnectVectorToInterrupt ..
VOID
NTAPI
KiConnectVectorToInterrupt (in pkinterrupt Interrupt,
IN CONNECT_TYPE Type)
{
DISPATCH_INFO Dispatch;
PKINTERRUPT_ROUTINE Handler;
PULONG Patch = & Interrupt-> DispatchCode [0];
/* Get vector data */
KiGetVectorDispatch (Interrupt-> Vector, & Dispatch);/* Get the distribution information corresponding to the Interrupt number... */
/* Check if we're re only disconnecting */
If (Type = NoConnect)/* if you want to cancel this service interruption .... Set Handler to Dispatch. NoDispath..., that is, the default interrupt processing at the beginning ..... Simply print some debugging information... */
{
/* Set the handler to NoDispatch */
Handler = Dispatch. NoDispatch;
}
Else
{
/* Get the right handler */
Handler = (Type = NormalConnect )?
Dispatch. InterruptDispatch:
Dispatch. ChainedDispatch;/* two types of interrupt .... Shared and independent .. The shared interrupt processing needs to register the interrupt service cyclically... If
Is an independent interrupt... Then you can directly call the registered service interruption .... So the Handler here is different .. */
ASSERT (Interrupt-> FloatingSave = FALSE );
/* Set the handler */
Interrupt-> DispatchAddress = Handler;/* write the Handler address to DispatchAddress ....*/
/* Jump to the last 4 bytes */
Patch = (PULONG) (ULONG_PTR) Patch +
(ULONG_PTR) & KiInterruptTemplateDispatch-
(ULONG_PTR) KiInterruptTemplate)-4 );
/* Apply the patch */
* Patch = (ULONG) (ULONG_PTR) Handler-(ULONG_PTR) Patch + 4 ));
/* Here, rewrite the jump address of the jmp command in KiInterruptTemplateDispatch to Handler...
Handler's address minus Patch plus 4... That's because this is a jump to the relative address. So it is offset from the current command .... */
/* Now set the final handler address */
ASSERT (Dispatch. FlatDispatch = NULL );
Handler = (PVOID) & Interrupt-> DispatchCode;
}
/* Set the pointer in the IDT */
(PKIPCR) KeGetPcr ()-> IDT [Interrupt-> Vector]. ExtendedOffset =
(USHORT) (ULONG_PTR) Handler> 16) & 0 xFFFF );
(PKIPCR) KeGetPcr ()-> IDT [Interrupt-> Vector]. Offset =
(USHORT) PtrToUlong (Handler);/* OK .. Here, rewrite the corresponding IDT... here... Then the real interrupt processing will be connected ......... It's done .. */
}
Now let's take a look at the handling of shared interruptions and non-shared interruptions ...... First look at the handling of non-shared interruptions ....
Func KiInterruptDispatch @ 0
_ KiInterruptDispatch @ 0:
/* Increase interrupt count */
Inc dword ptr PCR [KPCR_PRCB_INTERRUPT_COUNT]/* interrupt counter of incremental PCR .... */
/* Save trap frame */
Mov ebp, esp/* save trap frame pointer ..... */
/* Save vector and IRQL */
Mov eax, [edi + KINTERRUPT_VECTOR]/* Before you jump to this page... Edi already points to KIINTERRUPT... Here we store the interrupt vector number into eax ........*/
Mov ecx, [edi + KINTERRUPT_SYNCHRONIZE_IRQL]/* write IRQL to ecx .....*/
/* Save old irql */
Push eax
Sub esp, 4
/* Begin interrupt */
Push esp
Push eax
Push ecx
Call _ HalBeginSystemInterrupt @ 12/* this function has been analyzed before... */
/* Check if it was handled */
Or al, al
Jz SpuriousInt
/* Acquire the lock */
GetIntLock:
Mov esi, [edi + KINTERRUPT_ACTUAL_LOCK]
ACQUIRE_SPINLOCK (esi, IntSpin)/* Get the spin lock ..... */
/* Make sure that this interrupt isn't storming */
VERIFY_INT kid
/* Save the tick count */
Mov ebx, _ KeTickCount
/* Call the ISR */
Mov eax, [edi + KINTERRUPT_SERVICE_CONTEXT]
Push eax
Push edi
Call [edi + KINTERRUPT_SERVICE_ROUTINE]/* call the specific interrupted service here .... */
/* Check if the ISR timed out */
Add ebx, _ KiISRTimeout/* to check whether ISR processing times out... ReactOS sets the maximum ISR processing time to 55 tick ...... if more than 55 tick .... It indicates that ISR is faulty .... */
Cmp _ KeTickCount, ebx
Jnc IsrTimeout
ReleaseLock:
/* Release the lock */
RELEASE_SPINLOCK (esi)
/* Exit the interrupt */
INT_EPILOG 0
SpuriousInt:
/* Exit the interrupt */
Add esp, 8
INT_EPILOG 1
# Ifdef CONFIG_SMP
IntSpin:
SPIN_ON_LOCK (esi, GetIntLock)
# Endif
IsrTimeout:
/* Print warning message */
Push [edi + KINTERRUPT_SERVICE_ROUTINE]
Push offset _ IsrTimeoutMsg
Call _ dbuplint
Add esp, 8
/* Break into debugger, then continue */
Int 3
Jmp ReleaseLock
/* Cleanup verification */
VERIFY_INT_END kid, 0
. Endfunc
Okay .. The specific registration process of Interrupt Processing is analyzed... Now let's summarize ....
Windows provides IoConnectInterrupt for driver development.
Function ...... The implementation of ReactOS is to first call KeInitializeInterrupt to initialize the interrupt object KIINTERUPT ...... This process will
The command of KiInterruptTemplate... mov
Edi, 0. The addressing address of this instruction is changed to the address of the interrupt object... Then call KeConnectInterrupt to establish the actual connection. Here, the interruption type is shared.
Or non-shared .... Rewrite the jump address of the jump command after KiInterruptTemplate ...... That is, jump to the actual processing function .. Then, if it is the first time
Start-up... Rewrite IDT .....