x86架构的中断类型以及实现方式上有很多种。从中断控制器模块上分,x86中有8259中断控制器、Local APIC 以及 I/O APIC ,另外在PCI/PCIE中的存在MSI中断。从类型上来分,有硬件中断和软件中断之分,有可屏蔽中断和不可屏蔽中断之分。这部分的分类,前者是按照中断源来分的,可以是软件主动触发(通过INT等指令),也可以是模块内部或者外部硬件触发的;后者主要根据针对中断是否要被处理(大部分中断都可以通过设置来配置成可屏蔽或不可屏蔽)。从实现上来分,有IDT(InterruptDescriptor Table)和IVT(Interrupt Vector Table)之分。这部分的分类主要是根据x86模式来分的,IVT主要用于实模式,而IDT主要用于保护模式及之后的模式。IVT一般放在系统地址从0x0开始的4K空间,且一个Vector到实现代码之间的连接是比较直接的,Vector指针直接指向代码段。而IDT表中的项还有一些转换才指向到代码,这也跟保护模式相适应。IVT或者IDT都可以通过IDTR寄存器来获取,而通过指令LIDT/SIDT可以读写IDTR寄存器。 1.x86 中断目前为止 x86的中断控制器常用的为APIC(高级可编程中断控制器),而8259中断控制器随着Intel处理器的发展已经渐渐的被淘汰了,所以我们在这里也就不讨论了。 我们在说明中断控制器的时候,我们首先先来了解一下x86的中断处理过程,我们从CPU内部从里向外看,首先中断的处理的整体过程就是当发生了一个中断或者异常的时候,CPU去调用中断处理函数,而我们在进行中断初始化的时候,会提供中断向量表,正是由于这个表项的存在CPU才能依据表来查询那个中断对应那个中断服务函数,那么x86架构的CPU是如何获得这个中断服务函数的地址呢,是通过 IDT (中断描述符表) 。中断描述符表(IDT)为每一个异常或中断向量对应的例程或任务分配了一个门描述符。由于只有256个中断或异常向量,所以IDT的表项也就256个,每一个中断向量对应一个描述符。所以我们通过中断描述符IDT就可以得到我们的中断服务函数的地址,而IDT表的基地址则是存放在IDTR寄存器中的,通过 IDTR 寄存器找到 IDT 表,在通过 IDT表查到中断服务函数的入口地址,整个过程的整体如下图:
本地中断: 温度监控产生的中断(Thermal Sensor interrupts); APIC检测内部错误错误时而产生的中断(APIC internal error interrupts); 本地直连 IO 设备 (Locally connected I/O devices) 通过 LINT0 和 LINT1 引脚发来的中断; 指定性能计数器在溢出时产生的中断(Performance monitoring counter interrupts); CPU的核间中断(IPI): 外部中断: 其实我们可以将这三种类别的中断当做是三种不同的消息格式,其目的是为了告诉CPU我的等待被处理的中断信息(例如中断号是什么,中断的类型是什么,边沿触发的还是电平触发,上升沿触发还是下降沿触发等)。而以上三种不同的中断类型也对应了三种不同的消息格式(虽然说是三种类型的中断消息,当其实内容是大致一样的)。本地中断使用本地向量表(LVT)来产生消息,五个寄存器分别对应五种本地中断;CPU的核间中断使用的是ICR寄存器产生消息;外部中断通过 I/O APIC中的 IOREDTBL[0:23] 产生,当I/OAPIC某个管脚接收到中断信号后,会根据该管脚对应的RTE,格式化出一条中断消息,发送给某个CPU的LAPIC。每一种消息类型的格式如下图所示: 本地中断使用本地向量表(LVT)来产生消息 这样我们就了解了三种中断消息的类型和格式,也就是说,我们CPU在接收到的中断可能来自于自己本地,也可能来自于其他CPU,也可能是外设外部的中断,这些中断在发送到处理器后,Local APIC会通过中断消息中包含的中断向量(Vector,三种消息的第八位表示Vector)来判定中断的优先级,最后将优先级最高的那个中断消息送如CPU,接着CPU会经过如下的过程来判定是否接受这个中断: 这样x86整体的中断处理过程我们就分析完了,总结一下:通过中断消息来告知处理器有中断(这个消息来自于本地中断、IPI核间中断、I/O APIC的外部中断中的其中一种),然后在本地APIC中去对这个消息进行判定(中断优先级、是否是特殊的中断(NMI / SMI / INIT / ExINT)等),当确定好一个中断消息送入CPU后,就会通过这个消息获取这个中断的属性(例如:触发模式、中断向量、空闲模式等),通过获取的中断向量在IDT表中通过基址加偏移的方式去获得最终的中断服务函数的地址,之后去处理。 2.Local APIC对于APIC来说,分为两个,一个为Local APIC,一个为I/O APIC,在 Intel Xeon 系列的处理器上,二者的关系如下图所示
而对于 P6 家族的处理器来说,二者的关系则如下图所示:
每个本地APIC都由一组APIC寄存器和相关联的硬件组成,这些硬件控制中断向处理器内核的传递以及IPI消息的生成。 APIC寄存器是存储器映射的,可以使用MOV指令进行读写。上面的处理器单元在多线程处理器中指的是逻辑处理器,每个逻辑处理器都有自己的Local APIC,每个Local APIC都对应一组寄存器。这组寄存器可以是映射到系统地址(MMIO方式)中,也可以是在MSR寄存器中,这取决于Local APIC的模式,目前一般有xAPIC和x2APIC两种模式,后者使用MSR寄存器。对于奔腾4以及Intel 至强处理器使用的是xAPIC体系,而P6系列的处理器则是使用的APIC体系,XAPIC体系的local APIC与 I/O APIC之间通过系统总线通信,而APIC体系则是通过APIC总线通信,我们可以认为XAPIC是APIC体系的扩展,而为了提高处理器的可寻址性还有X2APIC ,而X2APIC 又是对XAPIC的扩展 , x2APIC体系结构为xAPIC体系结构提供了向后兼容性,并为将来的英特尔平台创新提供了向前的可扩展性。 ·本地连接的I / O设备 — 这些中断是由直接连接到处理器的本地中断引脚(LINT0和LINT1)的I / O设备声明的边沿或电平而产生的。 I / O设备也可以连接到8259型中断控制器,该控制器又通过本地中断引脚之一连接到处理器。 ·外部连接的I / O设备 — 这些中断的产生是由连接到I / O APIC的中断输入引脚的I / O设备声明的边沿或电平。中断作为I / O中断消息从I / O APIC发送到系统中的一个或多个处理器。 ·处理器间中断(IPI) — Intel 64或IA-32处理器可以使用IPI机制来中断系统总线上的另一个处理器或一组处理器。 IPI用于软件自中断,中断转发或抢先式调度。 ·APIC计时器生成的中断 — 可以对本地APIC计时器进行编程,以在达到已编程的计数时将本地中断发送至与其关联的处理器。 ·性能监视计数器中断 — 当性能监视计数器溢出时,P6家族,奔腾4和Intel Xeon处理器提供了向其关联的处理器发送中断的能力 )。 ·热传感器中断 — 当内部热传感器跳闸时,奔腾4和Intel Xeon处理器提供了向自己发送中断的能力(请参见第14.7.2节“热监控器”)。 ·APIC内部错误中断 — 当本地APIC中识别出错误情况(例如尝试访问未实现的寄存器)时,可以对APIC进行编程,以将中断发送到其关联的处理器。处理”) 在这7个中断当中,LINT0和LINT1的中断;APIC计时器生成的中断;性能监视计数器中断;热传感器中断;APIC内部错误中断这5个中断我们称为本地中断,在本地向量表中为每个本地中断源提供了一个单独的条目,该条目允许为每个中断源建立特定的中断传递协议。 例如,如果将LINT1引脚用作NMI引脚,则可以设置本地向量表中的LINT1条目,以将具有2号向量的中断(NMI中断)传递给处理器内核。 从P6系统的处理器开始,我们可以使用 CPUD 指令来检测本地的APIC的存在与否 ,通过查阅《Intel软件开发手册 卷二 指令集参考》的CPUID指令可知,我们可以在EAX段寄存器中使用操作数 1h 执行CPUID指令,当我们在EAX设置为01H的情况下执行CPUID时,我们可以从EBX 和 EDX寄存器中获取一些关于Local APIC的消息 从EBX的高8字节获取的ID 2.1.1 使能或禁止 Local APIC《Intel软件开发手册》给出了两种方法来设计 Local APIC 。第一种为通过在IA32_APIC_BASE MSR寄存器中配置APIC全局启用/禁用标志来设置;第二种是使用伪中断向量(SVR)寄存器中的软件启用/禁用标志来配置APIC。 IA32_APIC_BASE MSR寄存器中配置 伪中断向量(SVR)寄存器中配置 通过设置伪中断向量(SVR)寄存器的第6位,我们可以使能或者禁止APIC。如果IA32_APIC_BASE [11]为1,则软件可以随时通过清除虚假中断向量寄存器中的APIC软件启用/禁用标志来临时禁用本地APIC,而当本地APIC处于软件禁用状态时,可以通过将APIC软件启用/禁用标志设置为1随时重新启用它。 2.1.2 重定位 Local APIC 寄存器基地址奔腾4,英特尔至强和P6系列处理器通过修改IA32_APIC_BASE MSR的24位基址字段中的值,可以将APIC寄存器的起始地址从FEE00000H重定位到另一个物理地址。 提供APIC体系结构的扩展是为了帮助解决与现有系统的内存映射的冲突,并允许MP系统中的各个处理器将其APIC寄存器映射到物理内存中的不同位置。 2.2 Local APIC 寄存器上一章提到的IA32_APIC_BASE MSR的24位基址字段中存放的值就是我们Local APIC 寄存器的基地址,Local APIC 的寄存器的宽度为为32位,64位或256位。 全部在128位边界上对齐。 应该使用128位对齐的32位加载或存储访问所有32位寄存器。 一些处理器可能支持某些APIC寄存器的加载和少于32位的存储。 但是这个只是针对于一些特殊模型架构的,不能保证在所有处理器上都能正常工作。该寄存器的这段地址上电默认值为0xFEE00000,如果我们不去修改该值,那么Local APIC寄存器就是从这个地址开始。 其中256位的寄存器是以下的几个: ISR:In-Service Register; TMR:Trigger Mode Register; IRR:Interrupt Request Register; 这些寄存器是跟中断个数对应的,系统中最多有256个中断,而上述寄存器中的每一个位都表示一个中断的状态。 当一个中断触发之后,对应的IRR位就被置位,不过此时中断并没有被CPU处理,只是在排队中,直到CPU要开始处理这个中断时,该位清零,而对应ISR位被设置,表示CPU开始处理这个中断了。当中断处理完成之后,会写EOI(End Of Interrupt)寄存器(也在上面的表中),这样Local APIC就会清零ISR对应的位。TMR寄存器表示中断的触发方式。 2.2.1 Local APIC ID 寄存器Local APIC ID 寄存器中存储的值为APIC的ID号,它是逻辑处理器在系统中的唯一标识,而在不同的APIC模式下, Local APIC ID 寄存器也略有不同. 另外,这些位还有一些细分,总共可以分为Cluster ID / Package ID / Core ID / SMT ID,这四层从高位到低位,而在系统中范围是从大到小的。Cluster是一组物理处理器,Package表示一个物理处理器,Core表示处理器中的一个核,SMT表示一个逻辑处理器。 这四层的架构其实包含了Intel多线程处理器中的两大个特性,即Hyper-Threading(又叫SimultaneousMulti-Threading SMT)和Multi-Core。前者表示的是单个核里面里面有两个执行单元(线程),而后者表示一个处理器里面有多个核。所以提到Intel的处理器说4核8线程,就是应用了上述两者技术的结果,即一个处理器里面有4个核,每个核有两个线程. 2.2.2 Local APIC版本寄存器
2.2.3 局部向量表(LVT)本地向量表(LVT)允许软件将本地的中断传递到处理器它由以下32位APIC寄存器,每一个本地中断对应一个寄存器 · 温度监控器寄存器(FEE0 0330H)-指定温度传感器产生中断时的中断传送。该LVT条目是特定于实现的,而不是体系结构的。如果实现,它将始终位于基地址FEE0 0330H。 · 性能计数器寄存器(FEE0 0340H)-指定性能计数器在溢出时产生中断时的中断传送。该LVT条目是特定于实现的,而不是体系结构的。如果实现,则不能保证它位于基地址FEE0 0340H。 · LINT0寄存器(FEE0 0350H)—指定在LINT0引脚上发出中断信号时的中断传递。 · LINT1寄存器(FEE0 0360H)—指定在LINT1引脚上发出中断信号时的中断传递。 · 错误寄存器(FEE0 0370H)-当APIC检测到内部错误时指定中断传递。
这5个寄存器会存在些许的差异,但是大体的信息都是一样的。 2.2.4 ICR寄存器ICR全称Interrupt Command Register,它用于逻辑处理器之间的通信,使用的中断称为IPI(Inter Processor Interrupt)。 ·将中断发送到另一个处理器。 ·允许处理器将其收到但未提供服务的中断转发给另一个处理器进行服务。 ·指示处理器中断自身(执行自中断)。 ·向其他处理器传递特殊IPI,例如启动IPI(SIPI)消息。 通过此功能生成的中断通过系统总线(用于Pentium 4和Intel Xeon处理器)或APIC总线(用于P6系列和Pentium处理器)传递到系统中的其他处理器。 处理器发送最低优先级IPI的这项功能是基于一些特殊情况下的,BIOS和操作系统软件应避免使用。 中断命令寄存器(ICR)是一个64位本地APIC寄存器,分为ICR_Low和ICR_High两部分,分别位于Base0x300(Low)和Base + 0x310(High),写入ICR_Low即可发送一个IPI,它允许处理器上运行的软件指定处理器间中断(IPI)并将其发送到系统中的其他处理器。 软件必须设置ICR来指示要发送的IPI消息的类型以及目标处理器。 (ICR的所有字段都是软件读写的,但传送状态字段是只读的。)写入ICR的低位双字的动作会导致IPI被发送。 Voctor : 正在发送的中断的向量号 Delivery Mode : 指定要发送的IPI的类型。该字段也称为IPI消息类型字段。
Destination Mode : 选择物理(0)或逻辑(1)目标模式 Delivery Status : 指示IPI传递状态,如下所示: 0(空闲)表示此本地APIC已完成发送所有以前的IPI。 Level : 对于INIT级别取消声明传送模式,该标志必须设置为0;否则,该对于所有其他交付模式,必须将其设置为1。 (此标志在Pentium 4和Intel Xeon处理器中没有意义,并且始终以1.发出。) Trigger Mode : 使用INIT级别取消断言传送模式时选择触发模式:边沿(0)或级别(1)。 对于所有其他交付模式,它将被忽略。 (此标志在Pentium 4和Intel Xeon处理器中没有意义,并且始终以0发出。) Destination Shorthand : 指示是否使用速记符号来指定中断的目的地,以及如果是这样,则使用哪个速记。 目标速记用于代替8位目标字段,并且可以由软件通过一次写入ICR的低位双字来发送。 为以下情况定义了快捷方式:软件自中断,到系统中所有处理器(包括发送者)的IPI,到系统中所有处理器(不包括发送者)的IPI。
Destination Field : 指定一个或多个目标处理器。 仅当目标速记字段设置为00B时,才使用此字段。 如果将目标模式设置为物理模式,则位56到59包含奔腾和P6系列处理器的目标处理器的APIC ID,位56到63包含奔腾4和Intel Xeon处理器的目标处理器的APIC ID。 如果将目标模式设置为逻辑模式,则对8位目标字段的解释取决于系统中所有进程中本地APIC的DFR和LDR寄存器的设置。 3. I/O APIC
3.1 I/O APIC寄存器I/O APIC的寄存器分为两个类型的寄存器,第一种是用于访问 I/O APIC 寄存器的内存映射寄存器(Table 1所示),第二种是实际的 I/O APIC的寄存器(Table 2所示) 3.1.1 I/O APIC 寄存器的内存映射寄存器我们可以查阅到在Linux中对于I/O APIC寄存器提供的读写方法
在这段代码中,结构体 io_apic 实际上表示的就是IOREGSEL和IOWIN这组寄存器。其中index表示IOREGSEL,其偏移地址为00;data表示IOWIN,偏移为10,所以我们可以看到在下面两个读写函数中,首先完成的就是对于这个结构体首地址的绑定,该基地址的获取通过io_apic_base函数获取。而在后面的代码中,无论我们是读取还是写入都需先向 index 需要访问的I/O APIC的寄存器,然后才是具体的读写操作,结构体中的 unused 为填充,没有实际的意义。 3.1.2 IOAPICVER
3.1.3 IOAPICARBAPICARB寄存器包含IOAPIC的总线仲裁优先级。 写入IOAPIC ID寄存器时,将加载该寄存器。 APIC使用单线仲裁来赢得总线所有权。 轮换优先级方案用于仲裁。 仲裁的胜者将成为最低优先级的代理,并假定仲裁ID为0。 除仲裁ID为15的代理外,所有其他代理将其仲裁ID递增1。 ID为15的代理获取获胜者的仲裁ID,并将其递增1。 仅对于成功传输的消息,更改(递增或假定)仲裁ID(除非是低优先级消息,即使消息未成功传输,仲裁ID也会更改)。 如果未报告该消息的校验和错误或接受错误,则消息发送成功。 在“级别触发具有断言的INIT”消息期间,APICARB寄存器始终装载有IOAPIC ID。 3.1.4 IOREDTBL寄存器组 [23:0]
|
|