linux中断数据结构图,北桥.PCI.linuxPCI中断处理

2008.8.9  , rev 2009.4.13

最开始只是想搞明白cpu 的load save指定的地址是如何正确的分别送到PCI和内存控制器的.....*感性认识 Dell 630

*PCI  和北桥

*P35 芯片组

*PCI 和DMI, PCI 地址/内存地址 decode 简单原理

*MCH-P35 内存影射配置

*P35 北桥对pci 配置 cycle的处理流程

*ICH9  PCI中断配置

*恶补APIC

*恶补linux PCI中断处理

Dell 630

手头只有这个机器,就从这个机器为例吧, 但是除了这个章节以后的都采用P35芯片组为例, 因为P35手册说的更详细.

lshw -X (需要安装lshw-gtk), 类似windows下的设备管理按照链接排序的输出, 比较能够反映系统设备的链接情况. Dell630 采用了独立的NV显卡,显然采用了PM965芯片组.

uid-25940085-id-3291255.html

这个图仅做为一个参考,能获取一个大致的印象.

PCI bus 和北桥

从上面linux内核暴露的数据看,几乎所有的设备都出现在PCI总线上. 从intel的芯片手册看来, 确实是这样的. 下面参考P35的芯片手册,大概学习一下.

MCH的简介可以参考:    MCH 负责 CPU, RAM, AGP, PCI (PCIE),  southbridge 之间的通讯. 带有图形芯片的北桥又叫做GMCH. 系统组成图中,MCH总是画在上方, 上北下南么,北桥的叫法由此而来.

下面这个图是从PCI2.2 规范中摘出的一个系统组成图:

uid-25940085-id-3291255.html

uid-25940085-id-3291255.html

北桥,就是MCH(memory controller hub), 由一个memory controler 和一个host-2-PCI的bridge组成. 在intel x86芯片体系下,内存存控制器从配置的角度看也是接到PCI总线的。

P35 芯片组我的机器是p35芯片组的,可以在这里找到芯片手册:

uid-25940085-id-3291255.html

uid-25940085-id-3291255.html

这里首先搞清楚北桥芯片内的几个主要设备:

Device 0:

内存控制器和Host Bridge. MCH物理上链接CPU, 在MCH内部,所有设备看起来都是链接(或通过)到PCI bus

0的.从配置的角度可以说CPU直接链接PCI host Bridge, host bridge之后是PCI bus 0.

这个个设备包含了标准pci头信息,PCI Express基址寄存器,, DRAM 控制寄存器(包括thermal/throttling),

DMI配置寄存器, 以及其他 (G)MCH 特定的寄存器.Device 1:

Host-PCI Express Bridge. 逻辑上PCI express也作为一个“virtual” PCI-to-PCI bridge

出现在PCI bus #0 上.是标准的PCI-PCI桥设备, 并且包含 PCI Express/PCI 的配置寄存器..Device 2: 内部集成的显卡控制器.逻辑上出现在PCI bus0, 物理上包含2D, 3D的控制寄存器.Device 3: Manageability Engine Device. ME Control.感觉如下理解这些关系比较好:

从配置上是CPU ->host bridge --> PCI bus 0 (hostbridge/memcontroler,

pci express, internal grapics, ME).  物理上,这些设备都在MCH(北桥)内,共同提供出host

interface, memory interface,  pci express interface,  Control

link/DMI接口(到ICH9) (其他接口略).

在cpu进行内存或着IO访问的时候, 北桥负责正确的decode不同地址范围到不同设备. 这些地址范围大多可以配置, 配置这些decode寄存器的方式就是通PCI bus0上的,上面提到的各个设备来进行.

纯属猜测: 北桥在decode这些地址的时候直接用这些定义地址的寄存器, 不会有什么属于哪个设备的顾虑, 他们毕竟都在北桥内,只是export 的配置方式是通过PCI而已.

P35 -PCI 和DMI关系

uid-25940085-id-3291255.htmlMCH 到南桥ICH (i/o controller hub)的通讯是通过intel自己的DMI总线(direct media interface)进行的.先来看看PCI地址空间内的详细分布:

uid-25940085-id-3291255.htmlPCI

这段地址空间内有一部分是交给 DMI 来 decode 的,另外一部分是给PCIE的配置空间用的,最后还有APIC/BIOS/FSB

Interrupt占用了一小部分,  这个DMI从物理上链接P35和ICH9, P35 +ICH9 芯片组中, DIM和PCI可以说密不可分,

P35 MCH中有4个设备, 剩余的设备全在ICH9中, ICH9的大部分设备也是挂在PCI bus 0上的的,包括usb这些外部总线.

上图中显示 DMI interface的decode 方式是subtractive decode. 这个的含义对于理解MCH的工作很有意义..

Subtractive

decode: 没有被其他设备positively 声明为属于别人的地址,4 个cycle的延迟之后,将被这个设备decode,

Positive

decode:  只转发/decode 自己声明的部分.

Negative

decode: 转发/decode 不属于自己地址范围的部分, 考虑设备从secondary 侧访问primary 侧的情形.

恩, 其实不复杂,

在MCH中的设备总是positive decode, 包括对内存的访问, 然后如果MCH没有响应, 4 cycle后, 送DMI处理,

DMI经过自己的信号转换,到ICH9中再还原这个地址访问, 看看有没有合式的的设备decode这个地址. PCI和DMI的关系这是很重要的一条.

P35 MCH memory map

MCH

的一个重要功能就是提供到host cpu 的物理连接, 从cycle角度看, MCH把CPU发起的I/O cycles decoded 到

PCI Express, DMI, 或者 (G)MCH 的配置空间. CPU 发起的memory cycles 被 decoded 到 PCI

Express, DMI, or 系统内存.另外MCH提供了bus

snooping的功能:从PCIE设备发起的,以及从DMI发起的到系统SDRAM的内存访问在host bus上被snoop(PCI

Express 设备访问noncacheable system memory是不需要被 snoop 的)给出Memory map的多是SOC系统,而PC上的memory map需要看芯片组手册才有相关的描述. Intel的P35-MCH 文档中给出一副描述 Memory Map 的图,如下:

uid-25940085-id-3291255.html

有了上面的分析, 对这个图还算有点感觉. 关键的几个配置寄存器: TOLUD, TOUUD都是在device 0这个设备中的.

然后看看北桥对pci 配置 cycle的处理流程, 应该很清楚了.

注:PCI Express 的bus编号是纳入pci系统的需要统一编号.MCG D1中的寄存器 Sec bus#/sub bus# 分别是pci 分给这个pci -pci express 桥的子bus编号和子bus中最大的编号..

uid-25940085-id-3291255.html

uid-25940085-id-3291255.html

ICH9  PCI中断配置

初看之下发现ICH9(其实从ICH2开始就有)提供了8条PCI 中断请求线,  感到非常诧异, pci 不是只有INTA-D 4个吗. 难道PCI标准不是这个意思.

后仔细考量发现PCI标准说的是设备有4个pin 可以链接中断线, 并且给了个implment notes(Revision 2.3 p.14 ), 来建议如何链接这个4个pin到系统中断控制器. 关键是,这个例子的前提是假设有4个未用的IRQ(系统中断控制器引脚), 得来的.

虽不太清楚ICH的多出来的PIRQ[E..H]到底何方神圣, 起码PCI规范没有定死PCI设备的4个pin如何接到中断控制器. 仔细查看ICH9的芯片手册, 其内部设备没有使用PIRQ[E..H], 关于这几根中断线, 手册说明如下:

1. 首先如果不用这几根中断线的话, 可以用做GPIO

2. 在非APIC模式, 可以路由到IRQ 3, 4, 5, 6, 7, 9, 10, 11, 12, 14 or 15 , 每个都有单独的路由寄存器. (也包括PIRQ[A..D])

3. 在APIC模式下, 这些引脚直接链接到IOAPIC: PIRQ[E-H]#-> IRQ[20-23].

ICH9包含的寄存器对PCI中断的描述很详尽:

1) 每个设备都有一个寄存器描述其4个中断PIN, INTA..D#链接到那条中断引线:PIRQ[A-H]#, 比如Device 25 Interrupt Route Register [ICH9, P371], 一个16bit的寄存器, 描述这个信息.

uid-25940085-id-3291255.html2) 最后还有一个PIR[pci interrupt router],来控制pci中断引线接入的IRQ,  先是ICH9, P144页对Steering PCI Interrupts的描述:

ICH9

有PIRQx Route Control registers, 位于Device 31:Function 0 的offset 60–63h

and 68–6Bh , 可以将PIRQA#-PIRQH# 路由到IRQ  3–7, 9–12, 14 or 15.  如不需要可以禁止.

当将PIRQx#路由到一个IRQ后,需要将其ELCR设置为level 触发. PIRQx#是active low的, ICH9

将其翻转后接到PIC, 如此就不能和SERIRQ 的active high 设备共享同一中断, active low 可以. [RFC]

而寄存器描述好难找, 在p426, p428, 名字叫做PIRQ[n]_ROUT—PIRQ, Routing Control

Register. 不复杂就不列到这里了.

在APIC模式下,就比较简单了, 如果配合MSI就更为简单.

恶补APIC

linux

关于APIC有许多名词,上面说到了INTx#,  PIRQx#, IRQx#, APIC又引入了GSI, global system

interrupt. GSI对应于PIC的IRQ. 千万别忘记GSI/IRQ到cpu的verctor也是需要一个简单的影射的. 下图就是GSI

vs IRQ.

uid-25940085-id-3291255.html

在透过一个bridge的时候, INTx#对应的新的pin是通过下面的方法换算的,这个东西称作是PCI-PCI Bridge Swizzle. 典型的x86 上的bridge换算方式如下,这个应该是PCI规范impliment notes 提到的方式:

new_pin = (child_slot + child_pin) % 4

LVT

LVT 是local APIC 自己的中断源配置表, LAPCI自己能产生 APIC timerer, 热量探测,性能计数溢出, APIC错误等中断. 通过LVT可以为其分配vector.

IOAPIC PRT RTE

IOAPIC 有PRT配置表, 其表项就是RTE. 其中可以配置对应中断送达的CPU, 极性, 触发方式, 屏蔽, 还有我们最关心的vector.

LAPIC

在MP系统, 初始化后LAPIC 被分配唯一一个ID,可以通过寄存器读取. 这个ID也是IOAPIC中断送达目标CPU的标识. LAPIC可以不连续,但是必须唯一.

LAPIC中也有类似PIC的 :IRR 收到单未提交cpu,  ISR:送达CPU但未完成,  TMR: 这个不一样, 是处理中的中断之触发模式.

具体的APIC细节繁复,需要看专门的著作了.对于linux, 也恶补一番.

恶补Linux PCI 中断

*PIC 时代 $PIR表的获取方式, 非APIC模式有效, 主流计算机上已经过时了...

x86 32bit有个配置选项, biosirq, 意为通过BIOS的PIR表(pci 中断路由表) ,来探测PCI中断. 可以搜搜看pcibios_get_irq_routing_table, 是PCI BIOS提供的获取$PIR地质的函数.

pcibios_irq_init 里边写的很明白, 如果是用apic模式就把PIR表指针置为空, 作废掉这个表.

* APIC模式下中断路由的主要数据结构

先说探测完成后如何找到一个pci设备的中断: IO_APIC_get_PCI_irq_vector, 就是这个函数, 顺这个线索, 可以知道apic模式下PCI中断获取的主要数据结构在arch/x86/kernel/io_apic_32.c(2.6.27):

/** # of IRQ routing registers*/intnr_ioapic_registers[MAX_IO_APICS]; (32bit下为64)/* I/O APIC entries */structmp_config_ioapic mp_ioapics[MAX_IO_APICS];int nr_ioapics;/* MP IRQ source entries */structmp_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; (256)/* # of MP IRQ source entries */intmp_irq_entries;

外提下 全局位图io_apic_irqs, 其含义是IRQ<16的IRQ中哪些IRQ是链接到io APIC的.

原因是即便使用了APIC模式, 某些板子的有些中断也没有链接到IO APIC, 不可通过IO APIC路由. 下面的函数值得一读,

可以大致了解这些个表的主要内容.

* 从一个设备计算其irq的过程

intIO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) /*通过IOAPIC查找bus/slot/pin 链接的IRQ*/{int apic, i, best_guess = -1;apic_printk(APIC_DEBUG, "querying PCI -> IRQ mapping bus:%d, ""slot:%d, pin:%d.\n", bus, slot, pin);if (test_bit(bus, mp_bus_not_pci)) {printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus);return -1;}for (i = 0; i < mp_irq_entries; i++) { /*查找所有探测到的irq entry*/int lbus = mp_irqs[i].mp_srcbus;for (apic = 0; apic < nr_ioapics; apic++) /*这个irq entry 对应的ioapic entry*/if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic ||mp_irqs[i].mp_dstapic == MP_APIC_ALL)break;if (!test_bit(lbus, mp_bus_not_pci) &&/*必须是PCI bus*/!mp_irqs[i].mp_irqtype &&  /*iqr type */(bus == lbus) && /*bus/slot/pin相等*/(slot == ((mp_irqs[i].mp_srcbusirq >> 2) & 0x1f))) { /*MP SPEC 指出srcbusirq中是slot pin*/int irq = pin_2_irq(i, apic, mp_irqs[i].mp_dstirq);/*dstirq是IOAPIC的引脚编号,即INTIx#*/            /*计算所得是对应PIC模式irq的GSI*/if (!(apic || IO_APIC_IRQ(irq)))continue;if (pin == (mp_irqs[i].mp_srcbusirq & 3)) /*低2bit 是pin*/return irq;/** Use the first all-but-pin matching entry as a* best-guess fuzzy result for broken mptables.*/if (best_guess < 0)best_guess = irq;}}return best_guess;}

*驱动request irq的背后

pci设备中有irq这一项, 注册给linux就行. 这个背后有点内容.  首先是APIC建立的"硬件链接":

pci 中断pin:INTx# --> IOAPIC INTINx#  :RTE 指定cpu的vector ---> X86的intr gate

linux软件:

x86 intr gate -> do_IRQ -> 驱动根据dev.irq 注册的处理函数

1)什么时候给IOAPIC RTE分配vector?   参考函数:setup_IO_APIC_irqs -> assign_irq_vector.2) 什么时候吧gate 指向do_IRQ的? 还是setup_IO_APIC_irqs ->ioapic_register_intr -> set_intr_gate(vector, interrupt[irq]);  而interrup已经准备好了, 参考 arch/x86/kernel/entry_32.S 如下代码:

/** Build the entry stubs and pointer table with* some assembler magic.*/.section .rodata,"a",@progbitsENTRY(interrupt).textENTRY(irq_entries_start)RING0_INT_FRAMEvector=0.rept NR_IRQSALIGN.if vectorCFI_ADJUST_CFA_OFFSET -4.endif1:    pushl $~(vector)CFI_ADJUST_CFA_OFFSET 4jmp common_interrupt.previous.long 1b.textvector=vector+1.endrEND(irq_entries_start)

然后  common_interrupt跳转到do_IRQ去.

3) dev->irq这个值到底是什么?   PIC时代是IRQ, APIC时代也是IRQ, 或者叫做GSI. 多个apic会连续的编码成GSI,就是这个irq. (注意, 一个IOAPIC自己的引脚编号叫做INTIx#)

uid-25940085-id-3291255.html

* 最后看看linux如何获取ioapic的各种entry

参考early_acpi_boot_init, acpi_boot_init. APIC定义了一堆堆的表格, 还有MP spec, 上面提到的管理表格其实类似MP spec定义的表格. 具体的就算了. 搂一眼就得了.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值