x86架构中的外部中断结构-Part 1:中断控制器的演化

      本文主要讲解了x86体系架构从外部设备接受中断的过程,本文是系列文章的第一部分,试图回答以下问题:

  • 什么是PIC以及它的用途是什么?
  • 什么是APIC以及它的用途是什么?LAPICI/O APIC的目的是什么?
  • APIC,xAPIC以及x2APIC之间的区别在哪儿?
  • 什么是MSI? MSI以及MSI-X之间的存在哪些区别?
  • $PIR, MPtable, ACPI tables的用途是什么?

       如果你想知道上述问题的答案,或者仅仅想简单了解一下中断控制器的发展过程,耐心看完本文,你一定会有收获。

原文地址:

     本文是翻译的国外大佬的文章,根据自己的理解稍加修改了语序,由于自己也在学习阶段,翻译难免有纰漏,如果感觉有些地方读不懂,建议可以去看看原文,并向我提出翻译修改的建议,谢谢。

《External Interrupts in the x86 system. Part 1. Interrupt controller evolution》

Introduction

       对于那些不了解中断(interrupt)是啥的同学,这里引用Wikipedia的内容进行简单的介绍:

       在系统编程中,中断是由硬件或软件向CPU(处理器)发出的一种信号,提示CPU需要立即处理一些高优先级任务。 中断提醒CPU注意高优先级任务,同时停止CPU当前正在执行的代码。 随后CPU暂停当前运行的代码、保存程序状态、转而去执行一个中断处理程序(或中断服务程序,ISR)来响应中断事件。 中断是暂时的,在中断处理程序完成后,处理器恢复到之前运行的代码。

        中断一般分为两类:硬件中断和软件中断。

  • 硬件中断:外设一般使用硬件中断向操作系统传递自身需要运行的信号,在计算机内部,外部设备通过向处理器发送电子警报信号实现硬件中断,例如磁盘控制器,或者外部设备,当按下键盘上的键或移动鼠标时会触发硬件中断,从而使处理器读取按键值或鼠标位置。启动硬件中断的行为被称为中断请求(IRQ)。
  • 软件中断:软件中断是由处理器本身的异常,或者是指令集中特殊指令在执行时引起的中断。 前者通常称为陷阱(trap)或异常(exception),用于处理程序执行期间发生的错误或事件,这些错误或事件无法在程序内处理。 例如,数字除以零,会引发除零异常,由此产生软件中断。

       本文主要讲解的是硬件中断IRQ

       中断的目的是什么? 举个例子,在网卡传输数据包的过程中,我们希望数据包到达后立即对来自网卡的传入数据包执行操作,而不是cpu连续询问网卡«我的数据包到达了吗?»,轮询方式会浪费cpu处理的时间,在这个案例之下可以使用外部硬件中断 IRQ,网卡的中断线应该连接到 CPU 的 INTR 线,网卡每收到一个数据包,就通过中断线发出信号,通知cpu数据包已经收到,然后cpu停下手头的工作,去读取这个数据包。

         但是如果有很多外部设备我们该怎么办?不可能将每个设备的中断线直接连接到CPU上,虽然通信效率高,但 在 CPU 上制作大量的 INTR 引脚是不现实的。

 

x86架构中的外部中断结构-Part 1:中断控制器的演化_第1张图片

       为了解决多个外设的中断线连接CPU的问题,一个特殊的芯片被设计出来,这就是中断控制器(an interrupt controller)。

PIC(可编程中断控制器 Programm Interrupt Controller

      第一个中断控制器芯片是 Intel 8259 PIC。 它有 8 条输入线(IRQ0-7)和 1 条输出线(连接中断控制器和 CPU 的 INTR 线)。 当其输入线上的任意一个设备发生中断时,8259 将通过 INTR 线发出中断信号。 于是CPU 知道某个设备请求了中断,处理器将询问 PIC的 8 条输入线(IRQx),并判断哪条是此中断的来源。 这个轮询有一些开销,但现在我们有 8 条中断线而不是 1 条。

     x86架构中的外部中断结构-Part 1:中断控制器的演化_第2张图片

         很快 8 条输入线就不够用了。 为了增加中断线的总数,两个 8259 控制器(主控制器master和从控制器slave)以级联方式进行连接(Dual PIC)。

           0 7 IRQ 由第一个 Intel 8259 PIC(主)处理,从 8 15 IRQ 由第二个 Intel 8259 PIC(从)处理。 只有主设备直接连接到 CPU 并可以发出中断信号。 如果第 8-15 行有中断,第二个 PIC(从机)将通过 IRQ2 线向主机发送中断信号,然后主机向 CPU 发送中断信号。 这种级联中断占用了 16 条线路中的 1 条,但对所有外部设备总共产生了 15 条中断线(8+7

          x86架构中的外部中断结构-Part 1:中断控制器的演化_第3张图片

     上述中断控制器级联方案很快被开发社区采用,现在当有人谈论PIC(可编程中断控制器)时,通常是指Dual PIC system即双PIC级联系统。过了一段时间,8259改进为8259A。有了这个控制器,芯片组中就包含了双PIC系统。当外部设备连接的主总线是ISA时,这个系统就足够了。不同的设备需要连接到不同的IRQ线路,因为ISA总线的中断是不可共享的。       设备中断几乎都按照如下标准进行映射:   

Example (from here):

IRQ 0 — system timer

IRQ 1 — keyboard controller

IRQ 2 — cascade (interrupt from slave controller)

IRQ 3 — serial port COM2

IRQ 4 — serial port COM1

IRQ 5 — parallel port 2 and 3 or sound card

IRQ 6 — floppy controller

IRQ 7 — parallel port 1

IRQ 8 — RTC timer

IRQ 9 — ACPI

IRQ 10 — open/SCSI/NIC

IRQ 11 — open/SCSI/NIC

IRQ 12 — mouse controller

IRQ 13 — math co-processor

IRQ 14 — ATA channel 1

IRQ 15 — ATA chanel2

Intel 8259芯片的配置和工作通过I如下I/O端口进行:

     

Chip

Register

I/O port

Master PIC

Command

0x0020

Master PIC

Data

0x0021

Slave PIC

Command

0x00A0

Slave PIC

Data

0x00A1

8259A更加完整的说明文档在此链接here

         后来,外设数量超过了15台,PCI总线也逐渐替代了ISA总线,相比于静态的ISA总线,PCI总线上的设备可以动态的加入到系统中,同时总线上的中断可以共享,这样可以实现利用一条中断线IRQ连接多个设备。最后,为了解决中断线不足的问题,设计者决定将所有PCI设备的中断分配到PIRQ线(Programmable Interrupt Request 可编程中断请求)。

         例如,假设我们在PIC控制器和20个PCI设备上有4条空闲的中断线。我们可以将来自5台设备的中断合并成一条PIRQx线,并将这些PIRQx线连接到PIC控制器。在这种情况下,如果一条PIRQx线路上出现中断,处理器必须询问与该线路相连的所有设备的中断情况,确定对哪个设备响应。由此将PCI中断线与PIRQx线连接的设备称为PIR路由器。使用这种方法必须要确保PIRQx线路没有连接到ISA总线中断上(这会产生冲突),同时要确保PIRQx线路分布是均衡的(我们连接到一条线路的设备越多,CPU在响应哪个设备的中断时需要轮询的次数就越多)。

           x86架构中的外部中断结构-Part 1:中断控制器的演化_第4张图片

 

        Note:上图展示了PCI设备->PIR的连接过程,在实际电路中,它们之间的连接更加复杂。实际上,每个PCI设备有4条中断线(INTA、INTB、INTC、INTD)和多达8个中断函数,其中每个函数只能有一个INTx中断。每个中断函数将使用哪条INTx线取决于芯片组配置。

       从本质上讲,中断函数彼此间是独立的。例如,一个PCI设备可以具有Smbus控制器函数、SATA控制器函数和LPC桥接函数。从操作系统(OS)的角度来看,每个函数就像一个单独的设备,有自己的配置空间(PCI配置)。 PIC控制器的中断路由信息由BIOS发送给操作系统,BIOS主要通过$PIR 、3Ch寄存器、3Dh寄存器来对PCI 进行配置。

       $PIR 表规范可以在微软提供的网站查询,或者从 PCI BIOS Specification获取相关信息。

APIC

        最后一种方法主要用于多处理器系统。本质上,PIC只能向一个CPU发送中断,在多处理器系统中,需要均衡的向CPU通知中断。这个问题的解决方案是添加一种新的PIC接口----APIC(Advanced PIC)。

       每一个处理器都添加一个特殊的控制器LAPIC(local APIC),以及提供外部设备中断路由的I/O APIC控制器。这些所有的控制器都连接在APIC 总线上,设备连接示意图如下。

   x86架构中的外部中断结构-Part 1:中断控制器的演化_第5张图片

         当一个外部中断信号输入到 APIC的I/O口上,这个控制器会将该中断信号传送到系统中任意一个CPU上的LAPIC上,在上述传输方式中,APIC控制器可以均衡的将中断信号传入不同的处理器上。第一款APIC芯片是82489DX,它是一个单独的芯片,内部含有LAPIC和APIC,对于上图中的双处理器系统,需要三个这样的芯片:两个用于LAPIC,一个用于I/O APIC。后来,LAPIC功能直接包含在处理器中,APIC部分与82093AA芯片分离。

         APIC 82093AA芯片有24个输入,APIC体系架构最多可支持16个CPU。中断0-15用于旧的ISA中断,以便与旧的系统兼容,中断16-23用于所有PCI设备。通过这种划分,ISA和PCI中断之间的所有冲突都可以轻松避免。随着空闲中断线数量的增加,也可以用来增加PIRQx线的数量。APIC和LAPIC编程可以通过MMIO完成。LAPIC寄存器通常放在地址0xFEE00000上,APIC寄存器放在地址0xFEС00000上,因此可以需要重新配置它们。与PIC的情况一样,最初独立的芯片后来会成为芯片组(处理器)的一部分。

        APIC 体系结构的下一代是xAPIC (x -extended),可以完全向后兼容,并且系统支持的CPU个数增加到了256。xAPIC的下一代体系架构是x2APIC.该系统可以支持的CPUs数量增加到了  2^32。可以在xAPIC模式下运行,也可以在新的x2APIC模式下运行。在这种新模式下,控制器编程不是通过MMIO完成的,而是通过MSR寄存器(速度快得多)。根据该链接(this link,),IOMMU来支持该模式的编程。

        值得一提的是,系统中可能有多个APIC控制器。例如,南桥中的APIC负责24次中断,北桥中的另一个APIC负责32次中断。在APIC系统中,中断通常被称为GSI(全局系统中断)。因此,上述南北桥系统整体的GSIs为0-55。我们如何确定CPU内部是否含有LAPIC,以及它支持哪种APIC体系结构?我们可以通过检查CPUID中的标志位来确定。

        为了帮助OS 发现 LAPIC 和 APIC硬件的存在,以及使用它们。BIOS可以通过MPtable(旧方法)或ACPI表(在本例中是MADT表)来提供有关它们的信息,并加载到OS中。除了上述信息,MPtable和ACPI都应该包含具体的中断路由信息。这包含着哪个设备具体使用哪个中断线的信息(类似于$PIR表),以及相互映射关系。

        我们可以通过查看官方文档(official specification)来了解MPtable,早些时候,该规范出现在英特尔网站上,但目前只能在存档版本中找到。ACPI规范可在UEFI网站上找到(当前版本为6.2)。

MSI

        随着APIC的发展,所有设备的中断线导致系统变得异常复杂并且增加了错误概率。同时PCI express总线也取代了PCI总线,PCIe总线简化了中断系统,该总线中没有中断线,但是为了向下兼容,中断信号(INTx#)用一种单独的消息进行模拟。PCI总线中,中断的连接是通过物理布线实现的。而PCIe总线中的中断是逻辑连接的,并通过PCIe 桥接器进行传输。PCI express引入了一种全新的中断传递方法——MSI(消息信号中断)。在这种方式中,设备只需将数据写入CPU LAPICMMIO区域中的一个特殊位置,即可发出有关中断的信号。信号传输示意图如下:

        x86架构中的外部中断结构-Part 1:中断控制器的演化_第6张图片

       早些时候,单个PCI设备只包含4个中断,但是现在可以提供多达32个中断。在MSI的情况下,没有共享的中断线:每个中断自然地对应于它的设备。 MSI中断还解决了另一个问题。例如,一个设备进行一个内存写入事务,并希望通过中断发出其完成的信号。但是,写入事务在传输过程中可能会在总线上延迟(设备无法知道)。在这种情况下,中断信号可能提前到达CPU,因此处理器将读取尚未生效的数据。如果使用MSI,MSI信息将以与数据消息相同的方式传输,因此不能提前发送,从而不会出现冲突的情况。

       需要注意的是,没有LAPIC,MSI中断将无法工作,但是MSI可以取代APIC(正如上图所示,进一步简化了电路的设计)。随和一段时间的发展,MSI扩展到了MSI-X,现在每个设备最多可以处理2048个中断,并且可以指定哪一个CPU去处理哪一个中断,这对网卡等高负载设备都非常的有用。MSI不需要单独的BIOS表来支持,但外设需要指明它自身与哪一个MSI信号相对应,这些信息都需要包含在外设的驱动程序中。

Сonclusion

   在本文中,我们学习了有关中断控制器演变的相关知识,并大致了解了x86中断外设的一些基本知识。

    在Part2部分中,我们将进行练习,了解如何在Linux中使用前面提到的每个中断控制器。

    在Part3部分,我们将研究coreboot代码,并查看芯片组中需要哪些设置才能实现正确的中断路由信息。

Links:

  • Interrupt Controllers (Stuff in the Middle)
  • What do the different interrupts in PCIe do?
  • Reducing Interrupt Latency Through the Use of Message Signaled Interrupts

Acknowledgments

Special thanks to Jacob Garber from the coreboot community for helping me with this article translation.

Special thanks to Alex's suggestion for Computer architecture review

  

        

 

你可能感兴趣的:(x86架构中的外部中断结构-Part 1:中断控制器的演化)