IDT 와 ISR
오늘은 IDT 와 ISR 에 대해서 알려 드리고자 합니다. IDT, ISR 에 대해 설명하고자 하면 책을 한권을 써야 하고 저의 내공이 그렇게 깊지 않기 때문에 Windbg 를 사용해서 IDT 에서 ISR 을 찾아가는 과정을 설명 드리고자 합니다.
먼저 !idt 명령을 사용하면 IDT 의 내용이 확인 가능 합니다.
0: kd> !idt
Dumping IDT:
37: 80a7817c hal!PicSpuriousService37
3d: 80a79560 hal!HalpApcInterrupt
41: 80a793b4 hal!HalpDispatchInterrupt
50: 80a78254 hal!HalpApicRebootService
51: 89535044 serial!SerialCIsrSw (KINTERRUPT 89535008)
52: 8956d044 i8042prt!I8042MouseInterruptService (KINTERRUPT 8956d008)
53: 895f8044 USBPORT!USBPORT_InterruptService (KINTERRUPT 895f8008)
USBPORT!USBPORT_InterruptService (KINTERRUPT 89571008)
USBPORT!USBPORT_InterruptService (KINTERRUPT 89569008)
63: 895fe044 VIDEOPRT!pVideoPortInterrupt (KINTERRUPT 895fe008)
72: 8a10f044 atapi!IdePortInterrupt (KINTERRUPT 8a10f008)
73: 89b08044 storport!RaidpAdapterInterruptRoutine (KINTERRUPT 89b08008)
83: 89b38044 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 89b38008)
93: 89be6044 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 89be6008)
a2: 89537044 serial!SerialCIsrSw (KINTERRUPT 89537008)
a3: 8a34d044 storport!RaidpAdapterInterruptRoutine (KINTERRUPT 8a34d008)
b1: 8a35d044 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 8a35d008)
b3: 8953a044 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 8953a008)
b4: 895f1044 NDIS!ndisMIsr (KINTERRUPT 895f1008)
c1: 80a783e0 hal!HalpBroadcastCallService
d1: 80a77754 hal!HalpClockInterrupt
e1: 80a787f4 hal!HalpIpiHandler
e3: 80a78618 hal!HalpLocalApicErrorService
fd: 80a78d64 hal!HalpProfileInterrupt
fe: 80a78f0c hal!HalpPerfInterrupt
자 여기서 키보드 인터럽트를 직접 IDT 에서 ISR 까지 따라가는 작업을 해 보겠습니다.
먼저 r idt 명령으로 idtr 레지스터의 값을 확인하여 idt 의 address 를 찾습니다.
0: kd> r idtr
idtr=8003f400
keyboard interrupt 에 해당하는 index b3 을 확인 합니다.
0: kd> dt _KIDTENTRY 8003f400 + b4 * @@(sizeof(_KIDTENTRY))
nt!_KIDTENTRY
+0x000 Offset : 0xa044
+0x002 Selector : 8
+0x004 Access : 0x8e00
+0x006 ExtendedOffset : 0x8953
실제로 virtual address 에 있는 내용을 확인해 봅니다.
0: kd> dd 8003f400 + b3 * @@(sizeof(_KIDTENTRY)) L2
8003f9a0 0008a044 89538e00
여기서 ExtendedOffset 과 Offset 을 합친 0x8953a044 가 실제로 Interrupt 가 발생하였을 때 실행되는 Code 의 위치인데 Windows 에서는 이것을 Interrupt Object 라는 것으로 관리 합니다. 이 구조체는 아래와 같습니다.
0: kd> dt _KINTERRUPT
nt!_KINTERRUPT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 InterruptListEntry : _LIST_ENTRY
+0x00c ServiceRoutine : Ptr32 unsigned char
+0x010 ServiceContext : Ptr32 Void
+0x014 SpinLock : Uint4B
+0x018 TickCount : Uint4B
+0x01c ActualLock : Ptr32 Uint4B
+0x020 DispatchAddress : Ptr32 void
+0x024 Vector : Uint4B
+0x028 Irql : UChar
+0x029 SynchronizeIrql : UChar
+0x02a FloatingSave : UChar
+0x02b Connected : UChar
+0x02c Number : Char
+0x02d ShareVector : UChar
+0x030 Mode : _KINTERRUPT_MODE
+0x034 ServiceCount : Uint4B
+0x038 DispatchCount : Uint4B
+0x03c DispatchCode : [106] Uint4B
결국 0x895f1044 는 KINTERRUPT 구조체의 0x3c 위치에 있는 DispatchCode 의 Address 입니다.
0: kd> ? 8953a044 - 3c
Evaluate expression: -1991008248 = 8953a008
이 값은 !idt 의 결과에서 확인한 8953a044 의 값과 동일 합니다.
b3: 8953a044 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 8953a008)
자 그럼 확인해 볼까요?
0: kd> dt _KINTERRUPT 8953a008
nt!_KINTERRUPT
+0x000 Type : 22
+0x002 Size : 484
+0x004 InterruptListEntry : _LIST_ENTRY [ 0x8953a00c - 0x8953a00c ]
+0x00c ServiceRoutine : 0xbaabe0a5 unsigned char i8042prt!I8042KeyboardInterruptService+0
+0x010 ServiceContext : 0x899eac88
+0x014 SpinLock : 0
+0x018 TickCount : 0xffffffff
+0x01c ActualLock : 0x899ead48 -> 1
+0x020 DispatchAddress : 0x8083a63c void nt!KiInterruptDispatch+0
+0x024 Vector : 0x1b3
+0x028 Irql : 0xa ''
+0x029 SynchronizeIrql : 0xa ''
+0x02a FloatingSave : 0 ''
+0x02b Connected : 0x1 ''
+0x02c Number : 0 ''
+0x02d ShareVector : 0 ''
+0x030 Mode : 1 ( Latched )
+0x034 ServiceCount : 0
+0x038 DispatchCount : 0xffffffff
+0x03c DispatchCode : [106] 0x56535554
DispatchCode 에는 어떤 내용이 있는지 확인해 보도록 하겠습니다.
lkd> u 89c03008 + 3c L30
89c03044 54 push esp
89c03045 55 push ebp
89c03046 53 push ebx
89c03047 56 push esi
89c03048 57 push edi
89c03049 83ec54 sub esp,54h
89c0304c 8bec mov ebp,esp
89c0304e 89442444 mov dword ptr [esp+44h],eax
89c03052 894c2440 mov dword ptr [esp+40h],ecx
89c03056 8954243c mov dword ptr [esp+3Ch],edx
89c0305a f744247000000200 test dword ptr [esp+70h],20000h
89c03062 0f8531010000 jne 89c03199
89c03068 66837c246c08 cmp word ptr [esp+6Ch],8
89c0306e 7423 je 89c03093
89c03070 8c642450 mov word ptr [esp+50h],fs
89c03074 8c5c2438 mov word ptr [esp+38h],ds
89c03078 8c442434 mov word ptr [esp+34h],es
89c0307c 8c6c2430 mov word ptr [esp+30h],gs
89c03080 bb30000000 mov ebx,30h
89c03085 b823000000 mov eax,23h
89c0308a 668ee3 mov fs,bx
89c0308d 668ed8 mov ds,ax
89c03090 668ec0 mov es,ax
89c03093 648b1d00000000 mov ebx,dword ptr fs:[0]
89c0309a 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh
89c030a5 895c244c mov dword ptr [esp+4Ch],ebx
89c030a9 81fc00000100 cmp esp,10000h
89c030af 0f82b8000000 jb 89c0316d
89c030b5 c744246400000000 mov dword ptr [esp+64h],0
89c030bd 648b0d24010000 mov ecx,dword ptr fs:[124h]
89c030c4 fc cld
89c030c5 83652c00 and dword ptr [ebp+2Ch],0
89c030c9 f64103ff test byte ptr [ecx+3],0FFh
89c030cd 7522 jne 89c030f1
89c030cf 8b5d60 mov ebx,dword ptr [ebp+60h]
89c030d2 8b7d68 mov edi,dword ptr [ebp+68h]
89c030d5 89550c mov dword ptr [ebp+0Ch],edx
89c030d8 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h
89c030df 895d00 mov dword ptr [ebp],ebx
89c030e2 897d04 mov dword ptr [ebp+4],edi
89c030e5 bf0830c089 mov edi,89C03008h
89c030ea e96d64c3f6 jmp nt!KiInterruptDispatch (8083955c)
Dispatch code 는 대부분의 내용이 모든 Interrupt 에서 공통적으로 사용되는 것인데 파란색으로 되어 있는 부분이 각각의 Interrupt 마다 다르다. edi 에 89c03008h 즉 이 Interrupt 의 Interrupt Object 값을 넣고 nt!KiInterruptDispatch 로 Jump 합니다.
KiInterruptDispatch 코드를 살펴 보면
lkd> u nt!KiInterruptDispatch L100
nt!KiInterruptDispatch:
8083955c 8bec mov ebp,esp
8083955e 8b4724 mov eax,dword ptr [edi+24h]
80839561 8b4f29 mov ecx,dword ptr [edi+29h]
80839564 50 push eax
80839565 83ec04 sub esp,4
80839568 54 push esp
80839569 50 push eax
8083956a 51 push ecx
8083956b ff1554108080 call dword ptr [nt!_imp__HalBeginSystemInterrupt (80801054)]
80839571 0ac0 or al,al
80839573 7449 je nt!KiInterruptDispatch+0x62 (808395be)
80839575 64ff0544060000 inc dword ptr fs:[644h]
8083957c 83ec0c sub esp,0Ch
8083957f 648b0d1c000000 mov ecx,dword ptr fs:[1Ch]
80839586 83790800 cmp dword ptr [ecx+8],0
8083958a c745f400000000 mov dword ptr [ebp-0Ch],0
80839591 753f jne nt!KiInterruptDispatch+0x76 (808395d2)
80839593 8b771c mov esi,dword ptr [edi+1Ch]
80839596 f00fba2e00 lock bts dword ptr [esi],0
8083959b 7229 jb nt!KiInterruptDispatch+0x6a (808395c6)
8083959d 8b4710 mov eax,dword ptr [edi+10h]
808395a0 50 push eax
808395a1 57 push edi
808395a2 ff570c call dword ptr [edi+0Ch]
edi+0Ch 즉 edi 에는 InterruptObject 구조체의 포인터가 들어 있고 0Ch 위치에는 ServiceRoutine 이 있으므로 해당 Interrupt 의 Service routine 을 호출하는 것입니다.
마지막으로 그럼 ISR 을 개발하는 개발자는 이런 것들을 직접 넣어줘야 할까요?
아래 링크를 확인해 보면 ISR 을 만드는 것에 대한 자세한 설명이 있습니다.
https://msdn.microsoft.com/en-us/library/ms795203.aspx
간단히 설명하자면 개발자는 Interrupt 에 대한 ServiceRoutine, SpinLock 등을 정의 한 후 IoConnectInterrupt 함수를 호출하면 결과로 _IO_CONNECT_INTERRUPT 구조체가 나오는 것 입니다.
아래는 WDK Sample 중 PnPi8042 에서 사용된 IoConnectInterrupt 에 대한 내용으로 자세한 내용은 WDK Sample 중 \WinDDK\6001.18000\src\input\pnpi8042 를 참조하면 됩니다.
status = IoConnectInterrupt(
&(KeyboardExtension->InterruptObject),
(PKSERVICE_ROUTINE) I8042KeyboardInterruptService,
self,
&KeyboardExtension->InterruptSpinLock,
KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector,
(KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level,
configuration->InterruptSynchIrql,
KeyboardExtension->InterruptDescriptor.Flags
== CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive,
(BOOLEAN) (KeyboardExtension->InterruptDescriptor.ShareDisposition
== CmResourceShareShared),
KeyboardExtension->InterruptDescriptor.u.Interrupt.Affinity,
configuration->FloatingSave
);
Comments
Anonymous
March 16, 2009
The comment has been removedAnonymous
May 19, 2009
해당 인터럽트가 어느정도 로드를 발생 시키는지 볼려면 !idt 주소 한 다음 해당 idt의 thread를 찾아가서 cpu time을 봤는데... 이거 맞게 한건가요?? 아는 사람두 없구 물어볼 사람두 없어서...;;;Anonymous
June 18, 2009
xperf 를 사용하시는 것이 가장 좋습니다. xperf 로 interrupt 관련 정보를 수집하면 어떤 Interrupt 가 어떤 Device 와 연계되어 얼마나 호출되었는지 잘 알 수 있습니다.