全部博文(44)
分类: LINUX
2008-04-28 21:15:09
中断探测内核路径
路径
MP spec的相关代码位于arch/i386/kernel/mpparse.c中,主函数是get_smp_config(),它被setup_arch()调用,用MP table的信息配置系统。我们从get_smp_config()开始,看看Linux是怎么做的:
笔者:在此之前,我建议先跳文章末尾,阅读MP spec对硬件中断系统的规定
1、首先检查系统支持PIC模式还是Virtual Wire模式。如果支持PIC模式,全局变量pic_mode=1,否则pic_mode=0。接着获得LAPIC地址,存入mp_lapic_addr全局变量。最后检查mp table是否提供了默认配置。这些信息都可以通过MP Floating Pointer entry得到。
和 Default Configuration
MP Floating Pointer entry有两个字段用于描述一些特殊的属性,如下表:
字段 |
偏移(bytes:bits) |
长度(bit) |
描述 |
MP FEATURE INFORMATION BYTE1 |
11 |
8 |
全0:使用MP configuration table 不为0:使用Default configuration |
MP FEATURE INFORMATION BYTE2 |
12:0 12:7 |
7 1 |
Bit0-6:预留 Bit7:IMCRP,该位置一,PIC mode被实现;否则Virtual Wire Mode被实现 |
表2-3 MP Floating Pointer Structure(只摘录的部分字段)
Default Configuration是MP spec为了简化BIOS设计,给特定的平台提供的默认配置。它对平台有如下假设:
u 系统支持两个CPU
u CPU为Intel兼容指令集
u LAPIC位于默认地址FEE0_0000H
u LAPIC ID从0开始连续分配
u 系统有一个IOAPIC位于FEC0_0000H
u 系统实现了PIC mode或Virtual Wire Mode
u 根据平台总线类型、APIC类型的不同,默认配置又分7种情况,这里我们只关心一种,即平台总线为PCI+ISA、APIC是集成型时的情况。
笔者:MP spec给了两种APIC类型,一种是82489DX,一种是Integrated类型。个人认为目前流行的属于Integrated。
来看一下默认配置表,图中画框的是我们讨论的情况:
图2-2 Default Configuration
该配置有几个需要注意的地方,INTIN0(IOAPIC的0管脚)接的是PIC,INTIN2接的是IRQ0,即PIT;IRQ2不存在,因为PIC为两片8259a,其中第二片8259a接第一片的IRQ2管脚。
2、 如果平台使用Default Configuration,调用construct_default_ISA_mptable()。该函数会对CPU、总线、中断等使用默认配置,我们只关心和中断相关的部分,流程如下:
a、 注册IOAPIC。由于Default Configuration中只有一个IOAPIC,故其配置如下:
ioapic.mpc_type = MP_IOAPIC;
ioapic.mpc_apicid = 2;
ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01;
ioapic.mpc_flags = MPC_APIC_USABLE;
ioapic.mpc_apicaddr = 0xFEC00000;
MP_ioapic_info(&ioapic);
注意这里IOAPIC ID为2,是从LAPIC ID后最小的数字分配的(Default Configuration有两个CPU)。MP_ioapic_info()用于把该struct mpc_config_ioapic存入mp_ioapics数组。
b、 调用construct_default_ioirq_mptable()配置ISA中断。该函数首先配置struct mpc_config_intsrc中各字段,除dstirq。如下:
intsrc.mpc_type = MP_INTSRC;
intsrc.mpc_irqflag = 0; /* conforming */
intsrc.mpc_srcbus = 0;
intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid;
intsrc.mpc_irqtype = mp_INT;
其中mpc_irqflag=0表示中断的触发模式、管脚极性由中断所在的总线决定。接着配置各个ISA中断,即设置dstirq。内核为了正确的配置PCI中断,使用了ELCR(Edge/Level Controller Register,用于判断某IRQ的触发方式,详情见ICH9 spec),在使用前,检查ELCR是否可用。
、1、2、13是什么?
Kernel检查ELCR是否可用的方式,是判断IRQ0、1、2、13是否有level触发,因为这4个IRQ只能为edge触发。那么,它们是什么?IRQ0是PIT timer;IRQ1是键盘;IRQ2是连接的slave 8259a;IRQ13是协处理器,也就是老式的浮点运算单元。实际上还有一个可用于检查,即IRQ8——RTC,它也是edge触发。
设置dstirq代码如下(简化后):
for (i = 0; i < 16; i++) {
if (i == 2)
continue;
if (ELCR_fallback) {
if (ELCR_trigger(i))
intsrc.mpc_irqflag = 13;
else
intsrc.mpc_irqflag = 0;
}
intsrc.mpc_srcbusirq = i;
intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */
MP_intsrc_info(&intsrc);
}
IRQ2不设置,IRQ0连接到IOAPIC管脚2。其它IRQx被identify map到0号IOAPIC的1~15脚。此外,如果ELCR检查到该IRQ接的是PCI中断,则重新设置mpc_irqflag配置触发方式。最后调用MP_intsrc_info()把各ISA中断配置存入mp_irqs数组。
笔者:这里ELCR_trigger(i)返回1表示该IRQ是level触发。从内核的注释看,此时为PCI中断。令人费解的是intsrc.mpc_irqflag = 13代表了高电平有效、电平触发。而PCI应该是低电平有效、电平触发。内核错了?
最后,还要描述一下0号管脚接PIC的情况,如下:
intsrc.mpc_irqtype = mp_ExtINT; /*注意和前面的MP_INT类型区分*/
intsrc.mpc_srcbusirq = 0;
intsrc.mpc_dstirq = 0; /* 8259A to INTIN0 */
MP_intsrc_info(&intsrc);
c、 注册mpc_config_lintsrc 。Default configuration中LINT0接ExtINT,LINT1接NMI。调用MP_lintsrc_info注册(仅打印信息,无实用)。
3、 如果系统未实用默认配置,就需要解析MP table。该任务在smp_read_mpc()中完成,它将MP table的各个entry分别存入mp_ioapics、mp_irqs等数组。
4、 至此,平台上各IOAPIC、中断源的信息应已存储到各相关数组里。Kernel再做最后一次检查,如果mp_irq_entries == 0,说明MP table没有报告中断源,construct_default_ioirq_mptable()将系统设置到ISA模式。