|
|
楼主 |
发表于 2008-2-27 14:35:05
|
显示全部楼层
从IRQ到IRQL(APIC版)
来自:http://www.nsfocus.net/index.php ... o=view&mid=25346 H4 ]4 A) D; e3 L: l
: I; n8 C/ v* a6 u6 z1 M) Z* I7 O' v从IRQ到IRQL(APIC版)4 d; w J& M4 G& ]. b
) \* g, h" @% ?( C/ H作者:SoBeIt3 j4 u0 V: H( M3 u3 e% b
出处:https://www.xfocus.net/bbs/index.php?act=ST&f=2&t=45502
+ S f7 t- u9 N" [! W0 V日期:2005-02-04
" U+ d( {( z W0 s' v- P6 P1 @2 x* L% {: U5 { B6 n; @
事实上,老久的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那样虚拟一个中断控制器要简单得多了。
0 O0 r% `( y/ y7 c1 W" V$ D9 g& l, O; m- K
现在来简单介绍一下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接收处理器间中断消息。
. O( o6 F- m0 K6 |: j& ]3 ~
& d f+ t. j5 |2 o 和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个中断向量,可以进一步细粒度地区分中断的优先级。
0 G4 E* @4 V6 h# M, {7 T0 l; D* }& l/ ~; z7 g9 B* j
在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提供了一个固定的中断向量:
* R6 ]7 ]' c2 H* P1 u0 q- f+ `# r
6 }6 u5 w. N/ _( H#define ZERO_VECTOR 0x00 // IRQL 00
! Z ~1 ~' j E9 }#define APC_VECTOR 0x3D // IRQL 01 l- H4 D% V' H( @5 t
#define DPC_VECTOR 0x41 // IRQL 02
( Q+ Z9 i0 s; x) o# j#define APIC_GENERIC_VECTOR 0xC1 // IRQL 27
8 j+ m! F. P1 w$ Q4 X1 F! S9 D#define APIC_CLOCK_VECTOR 0xD1 // IRQL 283 g8 Y( U# i2 i* X
#define APIC_SYNCH_VECTOR 0xD1 // IRQL 28; V b! }: b( Z8 n2 Q
#define APIC_IPI_VECTOR 0xE1 // IRQL 295 G( o {/ z5 e6 x% u( K
#define POWERFAIL_VECTOR 0xEF // IRQL 302 v/ g( L) A! V% m5 I
#define APIC_PROFILE_VECTOR 0xFD // IRQL 319 [, {/ ?% }. {* }; d
& r! s$ Y/ H# F( \& R1 g0 J
2 o' q% S& ~' s, e现在看一下一些重要的数据:
- _6 e, D3 ]- y+ a* G! p
/ ^/ w- [0 R$ N这是我写的代码输出的IO APIC重定向表内容:: Q+ S4 z3 E4 c9 h" j9 J0 Y
( u7 l; c9 q7 c9 J& }Redirect Table Index: 0x176 k3 ^0 n. S4 G
Redirect Table[ 0]: ff0 y. R2 k0 T( H1 A
Redirect Table[ 1]: b3! f5 l- s' I8 P7 [( `0 a
Redirect Table[ 2]: ff
6 F l0 u. n, g& ^Redirect Table[ 3]: 51
4 V7 w( u, Y, C3 {: XRedirect Table[ 4]: ff
' E3 p, u# l [: p1 bRedirect Table[ 5]: ff. I2 E6 ^! A8 M. J- J" h
Redirect Table[ 6]: 62
: |9 e& d$ I& S) ]4 `8 W3 `Redirect Table[ 7]: ff6 U. @# j! r' \( e" Q
Redirect Table[ 8]: d1
+ h ~, e3 k. h4 R# v- uRedirect Table[ 9]: b1
" q* v3 C* {4 L8 O- g: P, r6 |Redirect Table[ a]: ff$ E8 F+ ?# T* \7 z# S
Redirect Table[ b]: ff
5 G7 \5 y$ l) o4 ~Redirect Table[ c]: 52
; w4 V5 m& q: u) L1 H: A1 hRedirect Table[ d]: ff
7 W* N& n9 t% Z' a) |0 mRedirect Table[ e]: ff0 D; A& c4 ~' t7 o* A; g8 h" U
Redirect Table[ f]: 92
9 h% ^0 F. Z3 P. J: n- ^Redirect Table[10]: ff
6 u+ E- @6 s/ |6 [! q: S- ORedirect Table[11]: a3: ~% z* D6 Q) R+ N0 c! a$ v: h
Redirect Table[12]: 83
A+ c1 [$ [+ ^$ j: p3 S, c, LRedirect Table[13]: 932 l9 @, k* {& K! n. z0 D
Redirect Table[14]: ff, ^: z, I7 I1 T6 A6 R, N2 k
Redirect Table[15]: ff7 d) v' J0 ?% F8 Z; A
Redirect Table[16]: ff
5 F8 L- g# B0 S1 [# RRedirect Table[17]: ff. I# A* b% H0 l. i) ~' i* s1 F
" q6 `; N& F0 O/ f这是IDT表中被注册的向量:
' Y* u+ J8 ^" T; e* H& F% T
9 g1 C; F, {7 h2 v1f: 80064908 (hal!HalpApicSpuriousService)
8 E8 Y0 A# A! w! `! ?" ~37: 800640b8 (hal!PicSpuriousService37)
% z F1 J( I! _: S3d: 80065254 (hal!HalpApcInterrupt)
% i* ~) m4 ~& e: Z. {41: 800650c8 (hal!HalpDispatchInterrupt)' z5 f/ i: y3 L7 f4 k
50: 80064190 (hal!HalpApicRebootService)( b' o% ]6 H$ {9 v) |* c) P
51: 817f59e4+ H; ~# _" h: o) W9 Z- W* P
(Vector:51,Irql:4,SyncIrql:4,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:serial!SerialCIsrSw(f3c607c7)), w& E; A7 d4 W9 p8 \5 N1 \
52: 817f5044 1 b0 v$ s5 n( ~# r
(Vector:52,Irql:4,SyncIrql:a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042MouseInterruptService(f3c57a2c))% u% y( o; q' x' e& v0 V) w) I; S
83: 817d2d44 6 T9 Y m4 _3 @' I- U# p( i
(Vector:83,Irql:7,SyncIrql:7,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:NDIS!ndisMIsr(bff1b794))1 B+ \( i6 o# K
92: 81821384
8 h. W U. N# Z; Q(Vector:92,Irql:8,SyncIrql:8,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:atapi!ScsiPortInterrupt(bff892be))8 }2 [( m/ K. \5 ^/ O V
93: 8185ed64 . A1 y. ~* |$ r- W2 {& U1 l
(Vector:93,Irql:8,SyncIrql:8,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:uhcd!UHCD_InterruptService(f3f0253e))
1 d" T- U- Q5 \ z# K+ _a3: 8186cdc4
1 S+ b {! S5 g" t(Vector:a3,Irql:9,SyncIrql:9,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:SCSIPORT!ScsiPortInterrupt(bff719f0))
5 h: h; G# q. E; V7 w* _$ Zb1: 818902e4 1 p ~ j0 v- d$ {$ _' y8 V
(Vector:b1,Irql:a,SyncIrql:a,Connected:TRUE,No:0,ShareVector:TRUE,Mode:LevelSensitive,ISR:ACPI!ACPIInterruptServiceRoutine(bffe14b4))1 a g6 G) Q/ K. O1 g+ R- q: e
b3: 81881664 , X3 y8 e2 y" Y( l7 ]) u
(Vector:b3,Irql:a,SyncIrql:a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042KeyboardInterruptService(f3c51918))
! k S* R6 K" W( O6 a1 e9 _c1: 800642fc (hal!HalpBroadcastCallService)
3 }* p( T! f2 T- n% U7 E, @- pd1: 80063964 (hal!HalpClockInterrupt)
6 g7 c: ^% b8 Q& Me1: 80064858 (hal!HalpIpiHandler)
2 m- A, U# N6 g/ \( ~e3: 800645d4 (hal!HalpLocalApicErrorService)% D5 @' P, N. O: ^
fd: 80064d64 (hal!HalpProfileInterrupt)9 ?- x; O% f+ T' A) ~1 r; d
fe: 80064eec (hal!HalpPerfInterrupt): L) G9 T* {8 i6 j
( t- F, i7 J3 W9 q+ `" f象a3、b1这类输出内容很多的是被硬件注册的中断向量,而象d1、e3这种输出内容少的是注册为了的HAL内部使用的中断向量和本地APIC中断向量9 f( `1 ^& f' l& k. R6 \
& i- u6 w) D. z这是几个重要的数组:# Z' E4 X3 o3 }$ O
8 I6 t% o7 _1 m+ ]HalVectorToIrql(这个数组是以向量除于16做索引):
; d2 j" A+ Q$ D3 G- C2 V* J9 Y8006a304 00 ff ff 01 02 04 05 06-07 08 09 0a 1b 1c 1d 1e& o, Q, B: W# u" W- z" E W
a# _1 V0 j [( uHalpIRQLtoTPR:
7 l+ z G+ ?: q; y8006a1e4 00 3d 41 41 51 61 71 81-91 a1 b1 b1 b1 b1 b1 b1" i7 ?* H! G$ f9 ?2 `% I- P
8006a1f4 b1 b1 b1 b1 b1 b1 b1 b1-b1 b1 b1 c1 d1 e1 ef ff
# I, ^7 {1 c+ e# |( n/ ?8 h- W$ }$ U0 P- { i" W+ n8 H5 ?
HalpINTItoVector:
* z" N* n V& S; W4 S% _( e8 @! t8006ada0 00 b3 61 51 a2 b2 62 91-a1 b1 71 81 52 82 72 92$ |$ I p0 M3 e
8006adb0 00 a3 83 93 00 00 00 00-00 00 00 00 00 00 00 008 C- H' d2 [, P$ `4 n8 x* n
3 [# L9 v9 j9 c3 z
HalVectorToINTI:
& Q1 F- I4 u% P9 \6 a- N8006a204 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff& Y9 ~- G' e7 N& l% H
8006a214 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff- N6 @5 @2 m, [, V
8006a224 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
5 b( \" c7 y1 }) u- Z: ~8006a234 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
* }! B- K+ a6 d7 S' t8006a244 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff! ^3 h) {/ m( ]/ o! d& m/ g! _
8006a254 ff 03 0c ff ff ff ff ff-ff ff ff ff ff ff ff ff
" x6 C% ^ y. ^. c' m8006a264 ff 02 06 ff ff ff ff ff-ff ff ff ff ff ff ff ff
+ E" m# L! h' N4 J% n8006a274 ff 0a 0e ff ff ff ff ff-ff ff ff ff ff ff ff ff
# R' z E, n( [& t- i- P" {8006a284 ff 0b 0d 12 ff ff ff ff-ff ff ff ff ff ff ff ff' K& K+ T) d' i/ T4 \) {2 e8 |7 C
8006a294 ff 07 0f 13 ff ff ff ff-ff ff ff ff ff ff ff ff
/ ^0 U) \4 }+ W8006a2a4 ff 08 04 11 ff ff ff ff-ff ff ff ff ff ff ff ff
1 T. ]: i! p& I% d8006a2b4 ff 09 05 01 ff ff ff ff-ff ff ff ff ff ff ff ff( V1 ]# S- ^. l% f* E: V
8006a2c4 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
5 z' o; ? K8 l; i7 j( m& y8006a2d4 ff 08 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff' E5 `9 ]. p. @% A- S5 M
8006a2e4 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff
0 R |5 @2 m8 ]5 ~$ w8006a2f4 ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff
8 L6 B. o5 `& ]4 M2 P: t2 ^5 t% \! m4 [9 D0 z5 {4 X+ q5 y
9 N' P4 E. V! S3 x
vBucket:/ Y" y6 {0 V7 Y1 s0 _/ Z( m
8006ae30 02 02 02 03 03 03 033 h* q) w3 P0 v# Q5 Q) d' Z
, Q9 P& X/ b+ ^ 举个例子来说明一下,在我虚拟机里SCSI Controller的IRQ是17(注意,已经大于16了),到重定向表中查找第17项,得到中断向量为0xa3,再看IDT,0xa3对应处理例程是SCSIPORT!ScsiPortInterrupt。
8 D8 ?$ H% F& F: v# l
. y/ t2 `1 P3 Z7 L' i3 D 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等向量就没有对应。
' k8 v( o% w7 ]3 [4 D: T, ? T- ?( a( T" J& I
IRQL机制为内核同步提供了很大的便利,既对驱动开发者隐藏了底层中断机制,也方便了驱动开发者的内核同步。LINUX从2.5内核开始引进的软中断和任务队列等机制,很大程度上也来自windows这套机制的借鉴。& a$ A" z' u0 q) @5 \* e7 A( o8 B
2 r, ^7 \8 A* g; ^2 }' e 终于考完试,解放了,呵呵。这个东西其实还有很多可写的,只是没空再深入去分析了。在未来的64位系统里,APIC这种基于中断引脚的机制很快也要被SAPIC这种基于消息的更强大的机制所取代 |
|