而这个PCI桥的Secondary Bus在接收Dock设备的请求时仍然使用正向译码方式。PCI桥使用的正向译码方式与PCI设备使用的正向译码方式有所不同。值得注意的是,PCI总线并没有规定HOST主桥使用正向还是负向译码方式接收这个存储器读写总线事务,但是绝大多数HOST主桥使用正向译码方式接收来自下游的存储器读写总线事务。......
2023-10-20
在Linux系统中,PCI设备使用的irq号存放在pdev→irq参数中,该参数在Linux设备驱动程序进行初始化时,由pci_enable_device函数设置。本书在第12.3.2节曾简要介绍过这个函数,下文进一步说明如何使用该函数设置PCI设备的irq号。pci_enable_device函数将依次调用__pci_enable_device_flags→do_pci_enable_device→pcibios_enable_device函数设置PCI设备使用的irq号。
pcibios_enable_device函数将调用pcibios_enable_irq函数,设置PCI设备使用的irq号。如果处理器系统使能了ACPI机制,pcibios_enable_irq函数将被赋值为acpi_pci_irq_enable。acpi_pci_irq_enable函数在./drivers/acpi/pci_irq.c文件中,其实现过程如源代码15-1所示。
源代码15-1 acpi_pci_irq_enable函数
该函数首先调用acpi_pci_irq_lookup→acpi_pci_irq_find_prt_entry函数,从acpi_prt_list链表中获得一个acpi_prt_entry结构的Entry。在acpi_prt_list链表中存放PCI总线的中断路由表,本章将在第15.1.2节进一步介绍该表。在这个Entry中,存放PCI设备使用的Segment、Bus、Device和Function号,PCI设备使用的中断请求信号(INTA#~INTD#)和GSI(Global System Interrupt)号。
这段程序在获得Entry后,将判断Entry→link是否为空,如果为空,表示当前x86处理器系统使用I/O APIC管理外部中断,而不是使用8259A。在Intel的ICH9中集成了两个中断控制器,一个是8259A,另一个是I/O APIC。Linux x86通过软件配置,决定究竟使用哪个中断控制器,在绝大多数情况下,Linux x86使用I/O APIC而不是8259A管理外部中断请求200。本章不再关心8259A中断控制器,因此也不再关心Entry→link不为空的处理情况[25]。
这段程序在获得GSI号之后,将调用acpi_register_gsi函数,将GSI号转换为系统软件使用的irq号。acpi_register_gsi函数使用三个入口参数,分别为GSI号,中断触发方式和采用电平触发时的极性。其中PCI设备使用低电平触发方式。
acpi_register_gsi函数执行完毕后,将为PCI设备分配一个irq号,这个irq号是系统软件使用的,之后PCI设备的驱动程序可以使用request_irq函数将中断服务例程与irq号建立映射关系;该函数还将设置I/O APIC的REDIR_TBL表,将GSI号与REDIR_TBL表中的中断向量建立对应关系,同时初始化与操作系统相关的irq结构[26]。为了深入理解acpi_register_gsi函数,读者需要理解GSI号、I/O APIC的REDIR_TBL表、IRQ_PIN引脚和Linux使用的irq号之间的对应关系。
GSI号是ACPI规范引入的,用于记录I/O APIC的IRQ_PIN引脚号的参数。如果x86处理器系统使用I/O APIC管理外部中断请求,而且在这个处理器系统中具有多个I/O APIC控制器,那么GSI号与I/O APIC中断引脚号的对应关系如图15-1所示。
图15-3 GSI和IO APIC中断引脚号的对应关系
假设在一个x86处理器系统中存在3个I/O APIC,其中有两个I/O APIC的外部中断引脚数为24根,另外一个I/O APIC的外部中断引脚数为16根。其中GSI号的0~23与I/O APIC1的IRQ_PIN0~23对应;GSI号的24~39与I/O APIC2的IRQ_PIN0~15对应;而GSI号的40~55与I/O APIC3的IRQ_PIN0~23对应。ACPI规范为统一起见使用GSI号描述外部设备与I/O APIC中断引脚的连接关系。
I/O APIC的IRQ_PIN引脚与外部设备的中断请求引脚相连,如I/O APIC1的IRQ_PIN16与某个PCI设备的INTA#相连。值得注意的是,PCI设备的INTA#信号首先与LPC的PIRQA#信号相连,而PIRQA#信号再与I/O APIC1的IRQ_PIN16相连。其中I/O APIC集成在ICH中,因此这些IRQ_PIN引脚并没有从ICH中引出。
REDIR_TBL表中存放对IRQ_PIN引脚的描述,一个I/O APIC具有多少个IRQ_PIN引脚,REDIR_TBL表就由多少项组成。该表的每一个Entry由多个字段组成,其中本节仅对这个Entry的Ve ctor字段感兴趣,Vector字段是这个Entry的第7~0位,存放对应IRQ_PIN引脚使用的中断向量。
在Linux系统中,与IRQ_PIN引脚对应的中断向量由acpi_register_gsi函数设置,当x86处理器系统使用I/O APIC管理外部中断时,acpi_register_gsi函数将调用mp_register_gsi函数。mp_register_gsi函数在./drivers/acpi/boot.c文件中定义,其实现机制如源代码15-2所示。我们假定在Linux系统中使能了CONFIG_X86_32选项。
源代码15-2 mp_register_gsi函数
这段程序首先根据GSI号,使用mp_find_ioapic和mp_find_ioapic_pin函数,确定当前PCI设备与I/O APIC中断控制器的哪个IRQ_PIN引脚相连(GSI号与I/O APIC和IRQ_PIN引脚的对应关系如图15-1所示)。(www.chuimin.cn)
然后mp_register_gsi函数调用io_apic_set_pci_routing函数设置I/O APIC中的寄存器。在Linux x86的源代码中,mp_register_gsi函数调用io_apic_set_pci_routing函数时,有一个并不恰当的处理,在mp_register_gsi函数中使用GSI号作为io_apic_set_pci_routing函数的第二个入口参数,但是io_apic_set_pci_routing函数要求的这个输入参数是irq号。
在Linux x86系统中,irq号是一个纯软件[27]概念,而这段代码的作用实际上是令GSI号直接等于irq号。笔者认为这种方法并不十分恰当,因为GSI号用来描述I/O APIC的IRQ_PIN输入引脚,而irq号是设备驱动程序用来挂接中断服务例程的。
本节在此强调这个问题,主要为了读者辨明GSI号和irq号的关系,目前在Linux x86系统中,PCI设备使用的GSI号与irq号采用了“直接相等”[28]的一一映射关系,实际上,GSI号并不等同于irq号。在系统软件的实现中,两者只要建立一一映射的对应关系即可,并不一定要“直接相等”。还有一点需要提醒读者注意,就是不同的PCI设备可以共享同一个GSI号,即共享I/O APIC的一个IRQ_PIN引脚,从而在Linux系统中共享同一个irq号。
io_apic_set_pci_routing函数调用__io_apic_set_pci_routing→setup_IO_APIC_irq操作I/O APIC中的寄存器。setup_IO_APIC_irq是一个重要函数,如源代码15-3所示。
源代码15-3 setup_IO_APIC_irq函数
该函数首先调用assign_irq_vector→_assign_irq_vector函数将外部设备使用的GSI号与I/O APIC中REDIR_TBL表建立联系,并将其结果记录到CPU的vector_irq表中。这个步骤非常重要,在Linux x86系统中,如果存在多个CPU,那么每一个CPU都有一个vector_irq表,这张表中包含了vector号与irq号的对应关系。这张表也是处理器硬件与系统软件联系的桥梁。
处理器硬件并不知道irq号的存在,而仅仅知道vector号,而Linux x86系统使用的是irq号。在处理外部中断请求时,Linux系统需要通过vector_irq表将vector号转换为irq号才能通过irq_desc表找到相关设备的中断服务例程。
setup_ioapic_entry函数将初始化entry参数。该参数是一个IO_APIC_route_entry类型的结构。而ioapic_register_intr函数调用set_irq_chip_and_handler_name函数设置irq_desc[irq]变量,并将这个变量的chip参数设置为ioapic_chip,handle_irq参数设置为handle_fasteoi_irq,这个步骤对于Linux x86中断处理系统非常重要。
ioapic_write_entry函数将保存在entry参数中的数据写入到与GSI号对应的REDIR_TBL表中,该函数将直接操作I/O APIC的寄存器。
由以上描述,我们可以发现当acpi_pci_irq_enable函数执行完毕后,Linux系统将GSI号与irq号建立映射关系,同时又将irq号与I/O APIC中的vector号进行映射,并将这个映射关系记录到vector_irq表中,这个映射表由操作系统使用。之后该程序还将初始化I/O APIC的REDIR_TBL表,将PCI设备使用的GSI号与I/O APIC的vector号联系在一起。
在x86处理器系统中,PCI设备的INTx引脚首先与LPC的PIRQA~H引脚直接相连,而LPC中的PIRQA~H引脚将与I/O APIC的IRQ_PIN16~23引脚相连。当PCI设备通过INTx引脚提交中断请求时,最终将传递到IRQ_PIN16~23引脚。而I/O APIC接收到这个中断请求后,将根据REDIR_TBL表与“IRQ_PIN16~23引脚”对应的Entry向Local APIC发送中断请求消息,处理器通过Local APIC收到这个中断请求后,将执行中断处理程序进一步处理这个来自PCI设备的中断请求。
Linux x86系统使用do_IRQ函数处理外部中断请求,该函数在./arch/x86/kernel/irq.c文件中,如源代码15-4所示。
源代码15-4 do_IRQ函数
do_IRQ函数首先获得vector号,这个vector号由I/O APIC传递给Local APIC,并与某个IRQ_PIN引脚对应,其描述在I/O APIC的REDIR_TBL表中。vector号是一个硬件概念,x86处理器系统在处理外部中断请求时,仅仅知道vector号的存在,而不知道irq号。
Linux x86系统通过vector_irq表,将vector号转换为irq号,之后执行handle_irq函数进一步处理这个中断请求。对于PCI设备,这个handle_irq函数将调用handle_fasteoi_irq函数,而handle_fasteoi_irq函数将最终执行PCI设备使用的中断服务例程。handle_fasteoi_irq函数的源代码在./kernel/irq/chip.c文件中,本节对该函数不做进一步分析。
在PCI设备的Linux驱动程序中,将使用request_irq函数将其中断服务例程挂接到系统中断服务处理程序中。
有关PCI Express体系结构导读的文章
而这个PCI桥的Secondary Bus在接收Dock设备的请求时仍然使用正向译码方式。PCI桥使用的正向译码方式与PCI设备使用的正向译码方式有所不同。值得注意的是,PCI总线并没有规定HOST主桥使用正向还是负向译码方式接收这个存储器读写总线事务,但是绝大多数HOST主桥使用正向译码方式接收来自下游的存储器读写总线事务。......
2023-10-20
而桥设备的主要作用是管理下游的PCI总线,并转发上下游总线之间的总线事务。PCI总线规范将PCI主从设备统称为PCI Agent设备。PCI规范也没有规定如何设计HOST主桥。在PCI总线中,还有一类特殊的设备,即桥设备。本书重点介绍PCI桥,而不介绍其他桥设备的实现原理。PCI桥的出现使得采用PCI总线进行大规模系统互连成为可能。其中对PCI设备配置空间的访问可以从上游总线转发到下游总线,而数据传送可以双方向进行。......
2023-10-20
PCI桥可以采用Combining、Merging和Collapsing三种方式,优化数据通过PCI桥的效率。PCI桥进行这种Combining操作时需要注意数据传送的“顺序”。使用PCI桥的Collapsing方式是,具有某些条件限制,在多数情况下,PCI桥不能使用Collapsing方式合并多个存储器写总线事务。PCI规范仅是提出了Collapsing方式的概念,几乎没有PCI桥支持这种数据合并方式。......
2023-10-20
图12-11 Capric卡的DMA写过程首先处理器填写Capric卡的WR_DMA_ADR、WR_DMA_SIZE和DCSR2寄存器,经过延时D0之后,这些命令陆续到达Capric卡。Capric卡收到处理器的DMA写请求后,将向RC连续发送存储器写TLP,并由RC将数据写入到主存储器。处理器收到MSI报文后,将执行中断处理程序,Capric卡的中断处理例程通过RC读取中断控制状态寄存器INT_REG,并结束整个DMA写操作。首先处理器填写Capric卡的寄存器启动DMA读。......
2023-10-20
在一段程序中,存在大量的分支预测指令,因而在某种程度上增加了指令Fetch的难度。但是分支预测单元并不会每次都能正确判断分支指令的执行路径,这为指令Fetch制造了不小的麻烦,在这个背景下许多分支预测策略应运而生。在PowerPC处理器中,条件转移指令“bc”表示Taken;而“bc-”表示Not Taken。BTB的功能相当于存放转移指令的Cache,其状态机转换也与Cache类似。转移指令B执行完毕后,将实际执行结果Rc更新到BHR寄存器中,并同时更新PHT中对应的Entry。......
2023-10-20
如图8-6所示,Detect状态由Detect.Quiet、Detect.Active两个子状态组成。在正常情况下,PCIe链路将从Detect状态迁移到Polling状态。而在Detect状态中,PCIe设备的发送逻辑TX将直接进入到“Electrical Idle”状态,并不会使用Idle序列通知对端设备的接收逻辑RX。当PCIe设备处于Detect.Quiet状态超过12ms之后,或者检测到PCIe链路上的任何一个Lane退出“Electrical Idle”状态时,PCIe设备将进入Detect.Active状态。......
2023-10-20
而PCI总线的突发传送仍然存在缺陷。为此PCI-X总线使用基于数据块的突发传送方式,发送端以ADB为单位,将数据发送给接收端,一次突发读写为一个以上的ADB。采用这种方式,接收端可以事先预知是否有足够的接收缓冲,接收来自发送端的数据,从而可以及时断连当前总线周期,以节约PCI-X总线的带宽。因此在PC领域和嵌入式领域很少有基于PCI-X总线的设备,PCI-X设备仅在一些高端服务器上出现。因此本节不对PCI-X总线做进一步描述。......
2023-10-20
MSI Capability结构共有四种组成方式,分别是32和64位的Message结构,32位和64位带中断Masking的结构。MSI Capability寄存器的结构如图10-1所示。图10-1 MSI Capability结构●Capability ID字段记载MSI Capability结构的ID号,其值为0x05。表10-1 MSI Cabalibities结构的Message Control字段[67] 此时PCI设备配置空间Command寄存器的“Interrupt Disable”位为1。当MSI En able位有效时,该字段存放MSI报文使用的数据。该字段需要与Mask Bits字段联合使用。......
2023-10-20
相关推荐