x86中断控制器

UP的APIC的建立过程

讲述UP的中断建立过程。内核版本2.6.9-78;ich8,cpu:intel-530。

许多内容不讲,一带而过。

内核config:

# CONFIG_SMP is not set

# CONFIG_PREEMPT is not set

CONFIG_PREEMPT_VOLUNTARY=y

CONFIG_X86_UP_APIC=y

CONFIG_X86_UP_IOAPIC=y

CONFIG_X86_LOCAL_APIC=y

CONFIG_X86_IO_APIC=y

内核流程

smp_init:main.c

#ifndef CONFIG_SMP

#ifdef CONFIG_X86_LOCAL_APIC

static void __init smp_init(void)

{

APIC_init_uniprocessor();

}

#else

#define smp_init() do { } while (0)

#endif

那么整个开始就是:APIC_init_uniprocessor().

APIC_init_uniprocessor

int __init APIC_init_uniprocessor (void)

{

//condition false

if (enable_local_apic < 0)

clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);

//condition false

if (!smp_found_config && !cpu_has_apic)

return -1;

/*

 * Complain if the BIOS pretends there is one.

 */

if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {

printk(KERN_ERR "BIOS bug, local APIC #%d not detected!.../n",

boot_cpu_physical_apicid);

return -1;

}

verify_local_APIC();

connect_bsp_APIC();

setup_local_APIC();

//如果nmi watchdog使用local apic。因为在setup_local_APIC中已经设置,所以这里需要检验

if (nmi_watchdog == NMI_LOCAL_APIC)

check_nmi_watchdog();

//所有条件都满足,即执行setup_IO_APIC()

#ifdef CONFIG_X86_IO_APIC

if (smp_found_config)

if (!skip_ioapic_setup && nr_ioapics)

setup_IO_APIC();

#endif

setup_boot_APIC_clock();

verify_local_APIC()

读取local APIC寄存器中的相关信息,判断是否是local apic。这部分没有什么难点。

connect_bsp_APIC

X86中断三种模式

1:pic_mode

2:virtual wire mode

3:APIC mode

具体看 intel mp spec。

系统处于virtual wire mode。

取决与:get_smp_config函数中的:

if (acpi_lapic && acpi_ioapic) {

printk(KERN_INFO "Using ACPI (MADT) for SMP configuration information/n");

return;

}

由于系统选用ACPI所以,这个条件都满足。即LAPCI、IOAPIC都存在,所以直接退出。则刚启动时系统处于virtual wire mode,而不是pic mode。

Virtual wire mode有分为两种情况:

我们这里是:

IOAPIC并没有使用,而是使用8259A中断控制器。LAPIC是使用的。

INTR直接链接LINIT0,配置位EXTINT即外部PIC中断源。

Code

系统重启时处于PIC模式,这里需要转换成APIC模式。但是我们并不处于PIC模式,所以condition false。

if (pic_mode) {

/*

 * Do not trust the local APIC being empty at bootup.

 */

clear_local_APIC();

/* PIC mode, enable APIC mode in the IMCR, i.e.connect BSP's local APIC to INT and NMI lines.*/

apic_printk(APIC_VERBOSE, "leaving PIC mode, "

"enabling APIC mode./n");

outb(0x70, 0x22);

outb(0x01, 0x23);

}

//这个函数没用

enable_apic_mode();

setup_local_APIC

这个函数是配置LAPIC,使其起作用。LAPIC本身含有6个LVT即本地中断。一个定时器、performence counte、LINT0、LINIT1、error 。

我们仅仅关心LINT0、LINT1。Local APIC自己的中断,可以链接外部设备,不知道可以链接什么设备。

这里又要区别BSP、AP。Multi processor的环境下:BSP即第一个启动的cpu;ap:是其他cpu。

/*   Set up LVT0, LVT1:

 * set up through-local-APIC on the BP's LINT0. This is not strictly necessery in pure symmetric-IO mode, but sometimes  we delegate interrupts to the 8259A. */

/* TODO: set up through-local-APIC from through-I/O-APIC? --macro  */

value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;

//ma_add:区分bsp和ap即cpu id,如果是bsp那么smp_processor_id=0。pic_mode不符合,但是value=0即LVTO在virtual wire mode下面是不能被屏蔽的。所以condition true

if (!smp_processor_id() && (pic_mode || !value)) {

value = APIC_DM_EXTINT;//中断是PIC

apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d/n",smp_processor_id());

} else {

//AP不能使用LINT0

value = APIC_DM_EXTINT | APIC_LVT_MASKED;

apic_printk(APIC_VERBOSE, "masked ExtINT on CPU#%d/n",smp_processor_id());

}

apic_write_around(APIC_LVT0, value);

/* only the BP should see the LINT1 NMI signal, obviously.*/

//LINT1:必须设置成NMI模式,因为硬件链接上LINT1与NMI相连。看上图

if (!smp_processor_id())

value = APIC_DM_NMI;

else

value = APIC_DM_NMI | APIC_LVT_MASKED;

//如果NMI watchdog的中断源是local apic。那么在这里设置,其实是使用了LOCAL APIC中的LVT中的performance counter作为中断源。这里注意:如果使用LAPIC作为nmi_watchdog那么,有几个cpu就有几个中断源。这里与IOAPIC是不同的。

if (nmi_watchdog == NMI_LOCAL_APIC)

setup_apic_nmi_watchdog();

setup_IO_APIC

这个函数是最复杂的函数,即启动IOAPIC,同时将8259A的中断源映射到IOAPIC,这样以后中断就不走8259A,而是通过IOAPIC。即系统进入APIC模式。

enable_IO_APIC

情况IOAPIC的相关寄存器信息。主要是24个PRT表。

系统有多少个IOAPIC,每一个IOAPIC中都多个pin即有多少个PRT表。每一个PRT表是64位。

具体可以看ich8的datasheet。

setup_IO_APIC_irqs

将8259A的irq与IOAPIC的pin联系起来。由于比较复杂,这里不详细讲述。

通过打印可以看出,系统在这里将8259A的15个irq都屏蔽掉了。

if (IO_APIC_IRQ(irq)) {

vector = assign_irq_vector(irq);

entry.vector = vector;

ioapic_register_intr(irq, vector, IOAPIC_AUTO);

if (!apic && (irq < 16))

disable_8259A_irq(irq);

}

此时中断不经过8259A,而是经过IOAPIC。即系统完成从virtual wire mode到APIC的转换。

init_IO_APIC_traps

没有看什么意思。

check_timer

这个函数很有意思,但是不知道为什么要检测timer的函数?即检测timer中断通过IOAPIC是否正常或者通过8259A或者vitual wire 模式正常。

由于我们的系统仅仅走一部分,所以我们讲相关部分。

//首先关中断:cli

local_irq_save(flags);

/*

 * get/set the timer IRQ vector:

 */

disable_8259A_irq(0);

vector = assign_irq_vector(0);

set_intr_gate(vector, interrupt[0]);

/*

 * Subtle, code in do_timer_interrupt() expects an AEOI

 * mode for the 8259A whenever interrupts are routed

 * through I/O APICs.  Also IRQ0 has to be enabled in

 * the 8259A which implies the virtual wire has to be

 * disabled in the local APIC.

 */

 //ma-add:禁用LVTO:前面LVTO与8259A的IRQ链接作为cpu的中断源

apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);

//aeoi?为什么不懂:在do_IRQ中会执行disable_8259A_irq

init_8259A(1);

//function:do_timer_interrupt,需要ack

timer_ack = 1;

//ma_add:contidition is ture

if (timer_over_8254 > 0)

enable_8259A_irq(0);

//判断Timer IRQ作为mp_INT,是否连接至IO APIC

pin1 = find_isa_irq_pin(0, mp_INT);

//Timer作为mp_EXTINT,是否连接至IO APIC

pin2 = find_isa_irq_pin(0, mp_ExtINT);

if (pin1 == 0)

timer_uses_ioapic_pin_0 = 1;

//打印信息:TIMER: vector=0x31 pin1=2 pin2=-1

//timer中断的vector是0x31,链接到IOAPIC的pin2.关于8259A的irq与IOAPIC的pin的对应关系在后面的打印信息中会说明。

printk(KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d/n", vector, pin1, pin2);

//ma-add:IOAPIC的pin1 mapped 8259A的irq0即timer irq

if (pin1 != -1) {

/*

 * Ok, does IRQ0 through the IOAPIC work?

 */

 //开启IOAPIC中断,重定向表已经在前面设定了:仔细看这个函数是将所有的IOAPIC都unmask,即此时中断就走IOAPIC,而由于前面8259A已经都关闭了,但是在这个函数中又将enable_8259A_irq(0),但是LINT0被mask,所以timer不能经过8259A走LINT0到cpu。但是timer中断有两条路径到APIO:直接和经过8259A。其实8259A的总的中断INTR是连到IOAPIC的pin0,但是IOAPIC是mask该pin。所以即使timer通过两条路径到APIO,其中一条是不通的。所以不会发生重复中断问题。

unmask_IO_APIC_irq(0);

if (timer_irq_works()) {

//ma_add:use NMI_NO_APIC,must unmask APIC_LVTO。this contdition:timer irq using two way:LINTO and IO_APIC。这里做过测试如果在check_nmi_watchdog()后将LVT0,masked。则NMI watchdog停止。所以可以说明:如果nmi_watchdog是IOAPIC,那么在virtual wire mode下面是timer定时器一个中断有两个作用:一个经过8259A和LINIT0,作为NMI的中断源通知cpu;而另一个作为一般的timer 中断走IOAPIC。通过打印信息发现:MP系统如果使用nmi_watchdog使用IOAPIC,那么也仅仅会有一个NMI中断源,这不同于nmi_watchdog=LAPIC.

if (nmi_watchdog == NMI_IO_APIC) {

disable_8259A_irq(0);

setup_nmi();

enable_8259A_irq(0);

check_nmi_watchdog();

}

if (disable_timer_pin_1 > 0)

clear_IO_APIC_pin(0, pin1);

goto out;

}

clear_IO_APIC_pin(0, pin1);

printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC/n");

}

print_IO_APIC

通过打印相关信息,对于理解IOAPIC很有用。

下面是系统的相关打印:

IOAPIC的PRT表的打印信息

IOAPIC的pin0被mask,表明8259A的INRQ是不能被IOAPIC所接受,即不会发生重复中断现象.

在这里我们也能看到timer中断是pin2,vector是0x31.

8259A IRQ与IOAPIC pin的对应关系

从上面可以看出:整个8259A的INTR是连到IOAPIC pin0;由于8259A的IRQ2是级联,所以没有.

IRQ超过16应该是中断共享.

图示

用图表示vitual wire mode的情况.

1:LINE2、LINE4都能使用

2:外部中断是通过LINE1、8259A、LINE3与LINTIN0与LAPIC相连。这是是指LAPIC已经运行?如果在这之前怎么处理?

3:等待LINE2建立链接之后,LINE3和LINE1关闭。同时,8259A的INTR链接到IOAPIC 的pin0,但是在配置时将pin0 mask,所以即使8259A可以发出中断,IOAPIC也不会接收处理,不会发生中断重复问题。此时中断走的线路是LINE2、IOAPI、LINE4。

4:但是如果NMI watchdog是IOAPIC,那么timer interrupt分布从LINE1、8259A、LINE3、LINTIN0以NMI方式进入CPU;而已普通的中断方式从LINE2、IOAPIC、LINE4与LAPIC通信进入cpu。测试如下:如果启动NMI_watchdog测试成功后,将LINTIN0 mask后,发现NMI watchdog 中断不再发生,这说明NMI watchdog走的路线是LINTIN0.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值