windows中实际的中断处理是通过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;/*获取CPU Affinity....*/ while (Affinity)/*在多处理平台上的ISR需要连接到Affinity对应的每个CPU上。。*/ { /* Increase count */ if (Affinity & 1) Count++;/*计算需要处理的count数。。。。*/ 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);/*在非分页内存上为KINTERRUPT分配内存。。。*/ if (!IoInterrupt) return STATUS_INSUFFICIENT_RESOURCES;
/* Select which Spinlock to use */ SpinLockUsed = SpinLock ? SpinLock : &IoInterrupt->SpinLock; /*如果指定了SpinLock。则使用参数里的SpinLock,否则使用刚刚分配好的KINTERRUPT的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)/*循环处理该中断需要连接的处理器,然后连接中断。。。。*/ { /* 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); /*初始化KINTERRUPT...*/
/* Connect it */ if (!KeConnectInterrupt(InterruptUsed))/*初始化完后,这里建立实际的连接。。。*/ { /* 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; }
由此看出比较重要的实际上是KeInitializeInterrupt和KeConnectInterrupt这两个函数。。。。。
VOID NTAPI KeInitializeInterrupt(IN PKINTERRUPT Interrupt, IN CHAR ProcessorNumber, IN BOOLEAN FloatingSave) { ULONG i; PULONG DispatchCode = &Interrupt->DispatchCode[0],Patch = DispatchCode;
/*每个KIINTERRUPT的实际入口就是这个DispatchCode数组。.这个数组是中断处理的入口代码汇编后形成的机器码。。。。*/
/* Set the Interrupt Header */ Interrupt->Type = InterruptObject; Interrupt->Size = sizeof(KINTERRUPT);
/* Check if we got a spinlock */ if (SpinLock)/*设置此中断对象的自旋锁。。。*/ { 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;/*这里初始化各个域*/ 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++)/*这里将汇编代码KiInterruptTemplate的机器指令复制到DispatchCode....注意。。这里很重要。。。*/ { /* 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); /*注意..KiInterruptTemplate只是一个模板而已。。。就是一个框架。所以这里需要移动到最后的4个字节。。。来将实际的中断服务函 数的地址写在这里。。。。。这样。。。就实现了对具体中断服务的跳转。。。*/
/* Apply the patch */ *Patch = PtrToUlong(Interrupt); /*这里就是将KIINTERRUPT的地址写入中断处理模板的最后4字节。。。。*/
/* Disconnect it at first */ Interrupt->Connected = FALSE; }
现在来看看KiInterruptTemplate部分的代码。。就清楚了。。。KiInterruptTemplate的代码在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:/*这条跳转指令的地址在KeConnectInterrupt函数里会被替换成实际的转入中断处理的函数的地址。。。*/ /* 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,DoFixupV86,DoFixupAbios .endfunc
/*因为
Patch = (PULONG)((ULONG_PTR)Patch + ((ULONG_PTR)&KiInterruptTemplateObject - (ULONG_PTR)KiInterruptTemplate) - 4);
所以这里之后Patch对应的就是mov edi,0这条指令的"0"这个立即数了。。。。。所以执行替换后。。这条指令就变成了mov edi,PKiInterrupt了。。*/
接下来分析另外一个重要的KeConnectInterrupt函数。。
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); /*这里获取Vector这个中断号对应的中断分发信息。。。。比如此中断上是否已经连接了中断服务。。。还有此中断是共享的还是单独的。。。。*/
/* Check if the vector is already connected */ if (Dispatch.Type == NoConnect)/*如果还没有任何ISR连接到这个中断号上。。。。*/ { /* Do the connection */ Interrupt->Connected = Connected = TRUE;
/* Initialize the list */ InitializeListHead(&Interrupt->InterruptListEntry);
/* Connect and enable the interrupt */ KiConnectVectorToInterrupt(Interrupt,NormalConnect);/*这里连接中断服务。。。*/ Status = HalEnableSystemInterrupt(Vector,Irql,Interrupt->Mode);/*调用HAL打开这个中断号对应的中断,因为未使用的中断都是被屏蔽的。。。*/ 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 = Connected = TRUE; ASSERT(Irql <= SYNCH_LEVEL);
/* Check if this is the first chain */ if (Dispatch.Type != ChainConnect)/*如果此中断是共享的。。并且还没有建立共享中断需要的入口点。。。。则建立。。。*/ { /* Setup the chainned handler */ KiConnectVectorToInterrupt(Dispatch.Interrupt,ChainConnect); }
/* Insert into the interrupt list */ InsertTailList(&Dispatch.Interrupt->InterruptListEntry, &Interrupt->InterruptListEntry);/*将刚才建立的中断连接入此中断号对应的队列。。。*/ } }
/* Unlock the dispatcher and revert affinity */ KiReleaseDispatcherLock(OldIrql); KeRevertToUserAffinityThread();
/* Check if we failed while trying to connect */ if ((Connected) && (Error))/*如果出错了。。。*/ { DPRINT1("HalEnableSystemInterrupt failed/n"); KeDisconnectInterrupt(Interrupt); Connected = FALSE; }
/* Return to caller */ return Connected; }
实际的连接函数是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); /*获取中断号对应的分发信息。。。*/
/* Check if we're only disconnecting */ if (Type == NoConnect)/*如果是要取消此中断服务。。。。则设置Handler为Dispatch.NoDispath...也就是最开始的默认中断处理。。。。。就是简单的打印一些调试信息而已。。。*/ { /* Set the handler to NoDispatch */ Handler = Dispatch.NoDispatch; } else { /* Get the right handler */ Handler = (Type == NormalConnect) ? Dispatch.InterruptDispatch: Dispatch.ChainedDispatch;/*有两种类型的中断。。。。共享的和独立的。。共享的中断处理需要循环注册了的中断服务。。。如果 是独立的中断。。。则直接调用注册了的中断服务就行了。。。。所以这里的Handler是不一样的。。*/ ASSERT(Interrupt->FloatingSave == FALSE);
/* Set the handler */ Interrupt->DispatchAddress = Handler; /*将Handler的地址写入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)); /*这里将KiInterruptTemplateDispatch那里的jmp指令的跳转地址改写为Handler....至于这里为什么要用 Handler的地址减去Patch再加4...那是因为这里是相对地址的跳转。所以是从当前指令来偏移的。。。。*/
/* 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) & 0xFFFF); ((PKIPCR)KeGetPcr())->IDT[Interrupt->Vector].Offset = (USHORT)PtrToUlong(Handler);/*好了。。这里改写相应的IDT......这里一改写。。。那真正的中断处理也就连接好了。。。。。。。。。大功告成了。。*/ }
现在再来看看共享中断和非共享中断的处理吧。。。。。。先看非共享中断的处理。。。。
func KiInterruptDispatch@0 _KiInterruptDispatch@0:
/* Increase interrupt count */ inc dword ptr PCR[KPCR_PRCB_INTERRUPT_COUNT] /*递增PCR的中断计数器。。。。*/
/* Save trap frame */ mov ebp,esp /*保存TRAP FRAME的指针。。。。。*/
/* Save vector and IRQL */ mov eax,[edi+KINTERRUPT_VECTOR]/*在跳转到这里之前。。。edi已经是指向了KIINTERRUPT了。。。这里将中断向量号存入eax........*/ mov ecx,[edi+KINTERRUPT_SYNCHRONIZE_IRQL]/*这里将IRQL写入ecx.....*/
/* Save old irql */ push eax sub esp,4
/* Begin interrupt */ push esp push eax push ecx call _HalBeginSystemInterrupt@12 /*这个函数前面已经分析过了。。。*/
/* Check if it was handled */ or al,al jz SpuriousInt
/* Acquire the lock */ GetIntLock: mov esi,[edi+KINTERRUPT_ACTUAL_LOCK] ACQUIRE_SPINLOCK(esi,IntSpin) /*获取自旋锁。。。。。*/
/* 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] /*这里调用具体的中断服务。。。。*/
/* Check if the ISR timed out */ add ebx,_KiISRTimeout/*检测ISR的处理是否超时。。。ReactOS设置了最大的ISR处理时间为55个tick......如果超过了55个tick..则说明ISR有问题了。。。。*/ 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 _DbgPrint add esp,8
/* Break into debugger,then continue */ int 3 jmp ReleaseLock
/* Cleanup verification */ VERIFY_INT_END kid,0 .endfunc
好了。。到这里具体的中断处理的注册过程就分析完了。。。现在来总结下。。。。
Windows为驱动开发提供的接口是IoConnectInterrupt 函数。。。。。。ReactOS的实现是先调用KeInitializeInterrupt初始化中断对象KIINTERUPT....。。此过程将 KiInterruptTemplate....的那条指令mov edi,0这条指令的寻址地址改写为中断对象的地址。。。然后调用KeConnectInterrupt来建立实际的连接,这里根据中断的类型是为共享的 还是非共享的。。。。改写KiInterruptTemplate后的跳转指令的跳转地址。。。。。。也就是跳转到实际的处理函数。。然后如果是第一次初 始化。。。则还要改写IDT..... (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|