|
|
楼主 |
发表于 2008-2-27 14:35:05
|
显示全部楼层
从IRQ到IRQL(APIC版)
来自:http://www.nsfocus.net/index.php ... o=view&mid=2534
+ S! \) z3 Q1 K+ `0 c0 ~/ S( J' Z$ ^; m3 a* Z/ D
从IRQ到IRQL(APIC版)- l/ V8 r. M3 H! b" r+ H
[/ }. E, [; n% c& \6 m作者:SoBeIt; r* {5 R7 o) I8 X( `4 ?3 k/ D
出处:https://www.xfocus.net/bbs/index.php?act=ST&f=2&t=45502) ]4 {# }& P% A; V
日期:2005-02-04
; E+ O; M, [* F5 `: B2 ~& j$ P9 W4 @! d! U+ H5 A6 F' w! T0 O* C
事实上,老久的PIC在很早以前就被淘汰了,取而代之的是APIC。由于APIC可以兼容PIC,所以在很多单处理器系统上我们看到的PIC实际是APIC的兼容PIC模式。APIC主要应用于多处理器操作系统,是为了解决IRQ太少和处理器间中断而产生的,当然,单处理器操作系统也可以使用APIC(不是模拟PIC)。APIC的HAL和PIC的HAL有很大的不同,很突出的一个特点就是APIC的HAL不用再象PIC的HAL那样虚拟一个中断控制器,IRQL的概念已经可以通过中断向量的形式被APIC支持。事实上,因为被APIC所支持,所以在APIC HAL里IRQL的实现比PIC HAL那样虚拟一个中断控制器要简单得多了。
6 f( i+ v/ |+ y& S% Y& P6 L8 s+ h/ x0 k3 R9 \( d
现在来简单介绍一下APIC的结构(关于APIC详细的描述请参考《IA-32 Inel Architecture Software Developer's Manual Volume 3 Chapter 8》)。整个APIC系统由本地APIC、IO APIC和APIC串行总线组成(在Pentium 4和Xeon以后,APIC总线放到了系统总线中)组成。每个处理器中集成了一个本地APIC,而IO APIC是系统芯片组中一部分,APIC总线负责连接IO APIC和各个本地APIC。本地APIC接收该处理器产生的本地中断比如时钟中断,以及由该处理器产生的处理器间中断,并从APIC串行总线接收来自IO APIC的消息;IO APIC负责接收所有外部的硬件中断,并翻译成消息选择发给接收中断的处理器,以及从本地APIC接收处理器间中断消息。( f& ]1 J5 A5 k4 N5 t0 R s
3 ?) @" Q p# y: e# b4 W4 b" b
和PIC一样,控制本地APIC和IO APIC的方法是通过读写该单元中的相关寄存器。不过和PIC不一样的是,Intel把本地APIC和IO APIC的寄存器都映射到了物理地址空间,本地APIC默认映射到物理地址0xffe00000,IO APIC默认映射到物理地址0xfec00000。windows HAL再进一步把本地APIC映射到虚拟地址0xfffe0000,把IO APIC映射到虚拟地址0xffd06000,也就是说对该地址的读写实际就是对寄存器的读写,本地APIC里几个重要的寄存有EOI寄存器,任务优先级寄存器(TPR),处理器优先级寄存器(PPR),中断命令寄存器(ICR,64位),中断请求寄存器(IRR,256位,对应每个向量一位),中断在服务寄存器(ISR,256位)等。IO APIC里几个重要的寄存器有版本寄存器,I/O寄存器选择寄存器、I/O窗口寄存器(用要访问的I/O APIC寄存器的索引设置地址I/O寄存器选择寄存器,此时访问I/O窗口寄存器就是访问被选定的寄存器)还有很重要的是一个IO重定向表,每一个表项是一个64位寄存器,包括向量和目标模式、传输模式等相关位,每一个表项连接一条IRQ线,表项的数目随处理器的版本而不一样,在Pentium 4上为24个表项。表项的数目保存在IO APIC版本寄存器的[16:23]位。APIC系统支持255个中断向量,但Intel保留了0-15向量,可用的向量是16-255。并引进一个概念叫做任务优先级=中断向量/16,因为保留了16个向量,所以可用的优先级是2-15。当用一个指定的优先级设置本地APIC中的任务优先级寄存器TPR后,所有优先级低于TPR中优先级的中断都被屏蔽,是不是很象IRQL的机制?事实上,APIC HAL里的IRQL机制也就是靠着这个任务优先级寄存器得以实现。同一个任务优先级包括了16个中断向量,可以进一步细粒度地区分中断的优先级。
4 B- a3 e9 G. n3 y' ]- r9 k
/ Z7 |, S( P. p. S! u 在HAL里虽然HalBeginSystemInterrupt仍然是IRQL机制的发动引擎,但是因为有APIC的支持,它和其它共同实现IRQL的函数要比PIC HAL里对应的函数功能简单得多。HalBeginSystemInterrupt通过用IRQL做索引在HalpIRQLtoTPR数组中获取该IRQL对应的任务优先级,用该优先级设置任务优先级寄存器TPR,并把TPR中原先的任务优先级/16做为索引在HalpVectorToIRQL数组中获取对应的原先的IRQL然后返回。若IRQL是从低于DISPATCH_LEVEL提升到高于DISPATCH_LEVEL,还需要设置KPCR+0x95(0xffdff095)为DISPATCH_LEVEL(0x2),表示是从DISPATCH_LEVEL以下的级别提升IRQL。HalEndSystemInterrupt向本地APIC的EOI寄存发送0,表示中断结束,可以接收新中断。并还要判断要降到的IRQL是否小于DISPATCH_LEVEL,若小于则进一步判断KPCR+0x96(0xffdff096)是否置位,若置位则表示有DPC中断在等待(在IRQL高于DISPATCH_LEVEL被引发,然后等待直到IRQL降到低于DISPATCH_LEVEL),则将KPCR+0x95和KPCR+0x96清0后调用KiDispatchInterrupt响应DPC软中断。否则做的工作就是和HalBeginSystemInterrupt一样的过程:把要降到的IRQL转换成任务优先级设置TRP,并把久的任务优先级转成IRQL返回。KfRaiseIrql、KfLowerIrql之类的函数也是这么一回事,把当前IRQL转成任务优先级修改TPR,并把原先TPR的值转成原先的IRQL并返回。而现在软中断的产生也有了APIC支持,APIC通过产生一个发向自己的处理器间中断,就可以产生一个软中断,因为可以指定该中断的向量,所以软中断就可以区分优先级别,如APC_LEVEL、DISPATCH_LEVEL。产生软中断的函数一样还是HalRequestSoftwareInterrupt,该函数会先判断KPCR+0x95是否和要产生的软中断IRQL一样,若是的话则置位KPCR+0x96并返回,表示现在IRQL大于DISPATCH_LEVEL所以不处理DPC中断。否则以要产生的软中断的IRQL为索引从HalpIRQLtoTPRHAL取出对应任务优先级,并或上0x4000,表示是发向自身的固定处理间中断,并用该值设置中断命令寄存器ICW的低32位,然后读取中断命令寄存器ICW的低32位是否为0x1000,确定中断消息已经发送后就返回,这时候软中断已经产生。值得注意的是APIC HAL里没有HalEndSoftwareInterrupt这个函数。HAL为软中断的IRQL提供了一个固定的中断向量:9 s- N D( i0 B. J8 v \
( P% {$ J% c$ x6 t: u; s( F
#define ZERO_VECTOR 0x00 // IRQL 00
/ ]! r* s( ?; F! L2 [#define APC_VECTOR 0x3D // IRQL 01
% X d' B! x% y#define DPC_VECTOR 0x41 // IRQL 02
' }0 f3 V( A; f/ Y, S7 N* U#define APIC_GENERIC_VECTOR 0xC1 // IRQL 278 G# A6 j. \& N/ Q* c5 ]4 z
#define APIC_CLOCK_VECTOR 0xD1 // IRQL 28- T9 d4 A2 `: |7 e# n0 U" E
#define APIC_SYNCH_VECTOR 0xD1 // IRQL 28
! |4 O. ^3 r/ L1 e" g) b( u: \#define APIC_IPI_VECTOR 0xE1 // IRQL 29
- t) V' h4 _/ W) a+ K. c% F#define POWERFAIL_VECTOR 0xEF // IRQL 30
) _' i8 T4 }( z, k8 _* B#define APIC_PROFILE_VECTOR 0xFD // IRQL 31$ K( X% ^8 r6 S+ U8 Y
$ U! M7 p4 E7 U x. p% [$ ]8 x x9 A1 b' u6 h
现在看一下一些重要的数据:) F3 k$ X' @9 u! t9 a$ e% V! G, A
/ M4 v4 c2 o- a
这是我写的代码输出的IO APIC重定向表内容:
: e- Y$ P0 b3 } l2 g. F9 u4 X5 H4 m7 P$ W
Redirect Table Index: 0x17! z9 U1 K. I5 F6 ?0 K U
Redirect Table[ 0]: ff
: Z0 f! E7 F0 c; RRedirect Table[ 1]: b3
6 Y% |; D' C7 ~( URedirect Table[ 2]: ff1 i% t0 f, {$ U
Redirect Table[ 3]: 51
2 w0 v. L. X, b! J! oRedirect Table[ 4]: ff# n9 v7 \5 @9 O7 ~. h' G+ T
Redirect Table[ 5]: ff8 J0 r2 U4 {/ B9 C) y+ k
Redirect Table[ 6]: 62
( P: Y9 J0 [! J2 i. l) z. oRedirect Table[ 7]: ff3 x! q$ B) d2 D' c9 j% b
Redirect Table[ 8]: d1! \1 c5 r0 O7 o" [' r
Redirect Table[ 9]: b1
' T/ D% U/ `) v3 u6 J" dRedirect Table[ a]: ff. k& e, a% i! E9 f
Redirect Table[ b]: ff
& c8 \% |: y' S8 ^+ {Redirect Table[ c]: 52
: b9 e7 f3 W4 h) y* iRedirect Table[ d]: ff
+ R* Z# s9 p, BRedirect Table[ e]: ff5 _$ X7 O8 J v! K3 X& @* R& t
Redirect Table[ f]: 92, v e( b" N0 O' g0 T" W+ F# ?9 e
Redirect Table[10]: ff' @. h7 R1 y K- ^ I" z- ?6 W
Redirect Table[11]: a3
* q6 M' J5 w5 A8 t5 o! C# F! SRedirect Table[12]: 83
) C$ `2 T' x- k9 Y$ C1 \Redirect Table[13]: 93( R: O8 K2 [2 ^8 O& S5 d0 D' l
Redirect Table[14]: ff/ { }. U5 S0 _7 c
Redirect Table[15]: ff/ P6 T0 x2 O8 p1 k
Redirect Table[16]: ff$ C: ~- Q0 ~- w y$ o3 r
Redirect Table[17]: ff6 c( G5 F* J0 N( M' A
+ @( C4 c \8 _4 g这是IDT表中被注册的向量:
- F# P4 ?- s8 D+ K- H4 q+ [5 L7 M* s1 _' x7 d
1f: 80064908 (hal!HalpApicSpuriousService)3 }& A; P8 S" p! X$ \( B
37: 800640b8 (hal!PicSpuriousService37)
6 ~1 ]+ U: e5 j1 v6 T) \) t+ D3d: 80065254 (hal!HalpApcInterrupt)" h) n* `/ C" ^+ u4 q
41: 800650c8 (hal!HalpDispatchInterrupt)
/ ?( L" S3 J- L% A: i- o! S$ o50: 80064190 (hal!HalpApicRebootService)
3 `& r0 y F. S0 i' h51: 817f59e4
' M Q6 }6 {7 C% @3 D$ }2 Z(Vector:51,Irql:4,SyncIrql:4,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:serial!SerialCIsrSw(f3c607c7))% a: V' b" ]6 c
52: 817f5044
* H& @; o2 r( L3 x+ `' s) N K(Vector:52,Irql:4,SyncIrql:a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042MouseInterruptService(f3c57a2c))
9 d* t. h2 \- v7 I$ o9 u83: 817d2d44
. m' F u" ^/ M2 _& ~- i) Q! h(Vector:83,Irql:7,SyncIrql:7,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:NDIS!ndisMIsr(bff1b794))
* d& L& O. s& e: X$ @" O9 C* J92: 81821384 & x+ f) A$ h; ?6 C" Y: n
(Vector:92,Irql:8,SyncIrql:8,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:atapi!ScsiPortInterrupt(bff892be))
+ Q) T0 |& i# z' U0 {93: 8185ed64
7 u1 h. h2 h$ C$ b# r0 P" E(Vector:93,Irql:8,SyncIrql:8,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:uhcd!UHCD_InterruptService(f3f0253e))* |7 E( ~2 a4 h8 ~5 P2 Y
a3: 8186cdc4 ( s' y; w& E0 v$ b% n: |0 X6 I
(Vector:a3,Irql:9,SyncIrql:9,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:SCSIPORT!ScsiPortInterrupt(bff719f0))
; V" j. k" w+ @( I* \( y5 tb1: 818902e4
, s/ j$ Y) |9 T(Vector:b1,Irql:a,SyncIrql:a,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:ACPI!ACPIInterruptServiceRoutine(bffe14b4))
& k+ v" r" z- q2 v$ x7 k4 Z) h6 Tb3: 81881664
8 ?1 }# L) R( j- h(Vector:b3,Irql:a,SyncIrql:a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042KeyboardInterruptService(f3c51918))
) C) w( X# J! g* B9 f2 vc1: 800642fc (hal!HalpBroadcastCallService)
d( c1 d3 c6 M" h A- \d1: 80063964 (hal!HalpClockInterrupt). ~- E; U* P L6 X6 Y% |
e1: 80064858 (hal!HalpIpiHandler)
% @" O( J, R8 ge3: 800645d4 (hal!HalpLocalApicErrorService)# A$ h& G# [5 G1 m4 l
fd: 80064d64 (hal!HalpProfileInterrupt), [! K; O' d) F3 }& p* h) |$ m
fe: 80064eec (hal!HalpPerfInterrupt)
( q" {/ h8 @: |: K$ ?9 e" J- Q- J0 {
象a3、b1这类输出内容很多的是被硬件注册的中断向量,而象d1、e3这种输出内容少的是注册为了的HAL内部使用的中断向量和本地APIC中断向量
* {5 c6 z) w! K6 P/ s7 {( ]
) }0 J6 y- C$ A& o D" D+ u$ q这是几个重要的数组:1 _* E% n+ z2 X5 Y, f. ?$ `
/ {9 U7 D/ q1 F2 L- d0 BHalVectorToIrql(这个数组是以向量除于16做索引):) v/ s$ S4 a0 V6 i# U) [
8006a304 00 ff ff 01 02 04 05 06-07 08 09 0a 1b 1c 1d 1e
& g3 P* p0 p. T/ U y6 k4 l5 c; b
HalpIRQLtoTPR:2 a! O- B% h5 G+ B7 v( T
8006a1e4 00 3d 41 41 51 61 71 81-91 a1 b1 b1 b1 b1 b1 b1
. G, G* w* M* j* d t% O, G8006a1f4 b1 b1 b1 b1 b1 b1 b1 b1-b1 b1 b1 c1 d1 e1 ef ff ^# ~( J8 l9 q
, {$ \3 n9 j1 w5 k0 n. T. ~
HalpINTItoVector:. W6 y6 I6 C( k
8006ada0 00 b3 61 51 a2 b2 62 91-a1 b1 71 81 52 82 72 92
# b! T; K: S# w) j: a/ E- p F8006adb0 00 a3 83 93 00 00 00 00-00 00 00 00 00 00 00 00" x* R! u* W1 B8 U4 m
7 P! M) |& z4 f$ c! u" O( o+ @6 Y- s% q; KHalVectorToINTI:( b; q5 w5 b& @' {9 f d4 Z
8006a204 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
% x6 D1 s; l2 m3 I8006a214 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff% u7 P3 B4 `) B
8006a224 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
) y. d& m7 A% H, c f" O8006a234 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff% P7 Y( s2 E* b6 h( l1 I
8006a244 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff' A$ K' d8 {; V* ?+ l
8006a254 ff 03 0c ff ff ff ff ff-ff ff ff ff ff ff ff ff
9 I% ]. M6 v, {( z; v% v8006a264 ff 02 06 ff ff ff ff ff-ff ff ff ff ff ff ff ff8 E+ K5 m, }9 s/ e0 L3 _
8006a274 ff 0a 0e ff ff ff ff ff-ff ff ff ff ff ff ff ff# Y5 B! R8 i6 K) E1 Z1 g. s
8006a284 ff 0b 0d 12 ff ff ff ff-ff ff ff ff ff ff ff ff
/ m' g8 A* d" {; J- t+ A1 T" x8006a294 ff 07 0f 13 ff ff ff ff-ff ff ff ff ff ff ff ff, }0 k/ I5 x& S. R0 e
8006a2a4 ff 08 04 11 ff ff ff ff-ff ff ff ff ff ff ff ff
, v, P7 o# o/ i& Z( p9 v8006a2b4 ff 09 05 01 ff ff ff ff-ff ff ff ff ff ff ff ff
2 J8 U" w% @3 B! \- l4 ]8006a2c4 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
* U- L+ D; |$ Q0 B5 O, @ B8006a2d4 ff 08 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
7 W+ B) T! l. y* ^- M/ K' h8006a2e4 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff! @3 X) R, j d$ |- u* d+ a1 W
8006a2f4 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff 4 Z/ y1 U. N8 p* @2 ]
* Z D: |# C. }: U3 D# l; V% o. X r( n7 q
vBucket:
6 ^. b4 a+ Z) S8006ae30 02 02 02 03 03 03 03
- ^& i% x* n$ O8 E. g
7 E7 I6 D% j; Z, V 举个例子来说明一下,在我虚拟机里SCSI Controller的IRQ是17(注意,已经大于16了),到重定向表中查找第17项,得到中断向量为0xa3,再看IDT,0xa3对应处理例程是SCSIPORT!ScsiPortInterrupt。
( a; `3 E, F6 W7 [7 C+ _- G, W) x, a2 _. u: ? D C: }
vBucket数组干啥用的?它就是用来分配新的向量。分配算法很简单,当要分配一个新的向量时,就在vBucket数组从右到左搜索最小的一个数i,该数对应在vBucket中索引为Index,新向量为(0x50+Index*16+i+1),新向量对应的IRQL为(4+i+1),同时会把vBucket中这个i加1,i不等大于16。象给出的这个vBucket,下一次计算时i=2, index=2。不过这些用于硬件的向量在IO系统初始化时调用HalpGetSystemInterruptVector分配好了,然后通过IoConnectInterrupt把IDT中注册的向量位置的例程注册为中断处理程序。这里并不是每个注册的向量都会对应中断处理程序,象上面给出的例子中,0xa1、0xa2、0xb1等向量就没有对应。
# X0 n9 J7 }3 M2 U8 x% F
" p6 z7 S0 ^2 t9 _6 r s, @ IRQL机制为内核同步提供了很大的便利,既对驱动开发者隐藏了底层中断机制,也方便了驱动开发者的内核同步。LINUX从2.5内核开始引进的软中断和任务队列等机制,很大程度上也来自windows这套机制的借鉴。6 _3 [% |# A2 r R
1 _$ q/ |* O6 t8 b( S M 终于考完试,解放了,呵呵。这个东西其实还有很多可写的,只是没空再深入去分析了。在未来的64位系统里,APIC这种基于中断引脚的机制很快也要被SAPIC这种基于消息的更强大的机制所取代 |
|