Log in

View Full Version : KeGetCurrentIrql can't return HIGH_LEVEL


deroko
February 3rd, 2008, 12:50
I was playing with IRQL and spotted one interesting thing. If IRQL is raised to HIGH_LEVEL, then KeGetCurrentIrql will return wrong info about IRQL:

Code:

.text:800123B8 ; __fastcall KfRaiseIrql(x)
.text:800123B8 public @KfRaiseIrql@4
.text:800123B8 @KfRaiseIrql@4 proc near
.text:800123B8
.text:800123B8 movzx edx, cl
.text:800123BB movzx ecx, ds:_HalpIRQLtoTPR[edx]
.text:800123C2 mov eax, ds:0FFFE0080h
.text:800123C7 mov ds:0FFFE0080h, ecx
.text:800123CD shr eax, 4
.text:800123D0 movzx eax, ds:_HalpVectorToIRQL[eax]
.text:800123D7 retn
.text:800123D7 @KfRaiseIrql@4 endp

Decode IRQL to Task Priority Register [APIC_base+0x80]

Code:
.text:80012398 _HalpIRQLtoTPR db 0h <--- PASSIVE_LEVEL (0)
.text:80012399 db 3Dh <--- APC_LEVEL (1)
.text:8001239A db 41h <--- DISPATCH_LEVEL (2)
.text:8001239B db 41h
.text:8001239C db 51h
.text:8001239D db 61h <--- CMCI_LEVEL (5)
.text:8001239E db 71h
.text:8001239F db 81h
.text:800123A0 db 91h
.text:800123A1 db 0A1h
.text:800123A2 db 0B1h
.text:800123A3 db 0B1h
.text:800123A4 db 0B1h
.text:800123A5 db 0B1h
.text:800123A6 db 0B1h
.text:800123A7 db 0B1h
.text:800123A8 db 0B1h
.text:800123A9 db 0B1h
.text:800123AA db 0B1h
.text:800123AB db 0B1h
.text:800123AC db 0B1h
.text:800123AD db 0B1h
.text:800123AE db 0B1h
.text:800123AF db 0B1h
.text:800123B0 db 0B1h
.text:800123B1 db 0B1h
.text:800123B2 db 0B1h
.text:800123B3 db 0C1h <--- PROFILE_LEVEL (27)
.text:800123B4 db 0D1h <--- CLOCK1/2_LEVEL (28)
.text:800123B5 db 0E1h <--- IPI_LEVEL (29)
.text:800123B6 db 0EFh <--- POWER_LEVEL(30)
.text:800123B7 db 0FFh <--- HIGH_LEVEL (31)

Also there is a array used to decode Task Priority Register to IRQL:

Code:
.data:8001D218 _HalpVectorToIRQL db 0h <--- PASSIVE_IRQL
.data:8001D219 db 0FFh
.data:8001D21A db 0FFh
.data:8001D21B db 1 <--- APC_LEVEL
.data:8001D21C db 2 <--- DISPATCH_LEVEL
.data:8001D21D db 0FFh
.data:8001D21E db 0FFh
.data:8001D21F db 0FFh
.data:8001D220 db 0FFh
.data:8001D221 db 0FFh
.data:8001D222 db 0FFh
.data:8001D223 db 0FFh
.data:8001D224 db 1Bh PROFILE_LEVEL
.data:8001D225 db 1Ch CLOCK1/2_LEVEL
.data:8001D226 db 1Dh IPI_LEVEL and POWER LEVEL
are different in Task priority sub-class
(lower 8bits of Task Priority Register)
.data:8001D227 db 1Eh POWER_LEVEL

Basically if we are running at HIGH_LEVEL, KeGetCurrentIrql will always return POWER_LEVEL. Calculaion is simple here, it uses Task Priority, upper 8 bits of TPR (Task Priority Register) as index into _HalpVectorToIRQL or better name would be _HalpTPRtoIRQL for this 2nd array.

So are you sure that you are running at HIGH_IRQL? or POWER_LEVEL?

dELTA
February 3rd, 2008, 13:23
Another gem for the archives.

JMI
February 3rd, 2008, 16:14
Good find and thanks for sharing with our readers.

Regards,

Kayaker
February 5th, 2008, 00:08
You're always getting into the good stuff deroko

That is weird. I confirmed the same thing programatically, when running at the 2 highest Irql levels, KeGetCurrentIrql returns one level lower. POWER_LEVEL returns IPI_LEVEL and HIGH_LEVEL returns POWER_LEVEL.

At least one could always get the "true" irql as it's encoded in the TPR and make their own KeGetCurrentIrql function.

KIRQL TPRVector;
TPRVector = *(KIRQL*)0x0FFFE0080;


It looks like that's just a way of dealing with the APIC architecture and HIGH_LEVEL is actually a Windows construct. If you look at Table7.1 in the Intel V.3 doc, the highest level listed is Priority Class 15, described as POWER_LEVEL.

deroko
February 5th, 2008, 20:46
yup 15 disables delivery of all interupts on a given cpu. Funny is that even when you sstep KfRaiseIrq/lLowerIrql with sice, it always shows EF (power level, maybe irql at which sice is running) or it's just my machine

Kayaker
February 5th, 2008, 22:14
I saw that behaviour in Sice as well. I think the EF level may be correct though. Back when I was making KDExtensions for Softice I naturally called KeGetCurrentIrql within a debugger extension itself to see the result. It returned IPI_LEVEL 29 (0x1D) (not being single stepped).

If we take into account the fact that KeGetCurrentIrql returns one level lower, then the actual Irql level would have been POWER_LEVEL (0x1E), which corresponds correctly to the 0xEF TPR encoding.

I might have guessed that Softice would run at HIGH_LEVEL, but from this osr thread it sounds like that is reserved for the debugger that is built into the NT kernel, and not a third party kernel debugger like Sice.

Why Is The IRQL Always 0xFF When I Do !PCR?
http://www.osronline.com/article.cfm?article=372

deroko
February 7th, 2008, 09:48
ah that explains it. when I think better it's quite logical softice should drop irql (I saw code in it where it modifies TPR directly) when executing instruction(single steping), but when it gains control again, after that instruction, it has to raise IRQL, and seems that there is no way to see current IRQL in sice by looking at FFFE0080, not sure if sice directly modifies PCR at the same time, but if it doesn't then there is stored real irql

Also what I saw on mp machine is that softice doesn't list IRQL when cpu command is used, only lists priority from TPR

Code:

CPU PCR Base IDT Base GDT Base Pri TID Process(ID) CS:EIP
0 FFDFF000 8003F400 8003F000 D1 0 Idle(0) 8:806E2F3D
1 BAB40000 BAB44590 BAB44190 41 0 Idle(0) 8:805450D0