Linux系统总线之PCI扫描流程
- 1. 域
- 2. pci设备
- 3. PCI桥
- 4. pci配置空间
- 5. pci内存映射空间
- 6. 扫描
- 6.1 BIOS pci 扫描
- 6.2 kernel扫描
- 6.2.1 扫描流程
- 6.2.2 扫描设备
- 6.2.3 分配资源
- 6.2.3.1 pci_reboot_init
- 6.2.3.2 pcibus_class_init
- 6.2.3.3 pci_driver_init()
- 6.2.3.4 acpi_pci_init()
- 6.2.3.5 pci_arch_init() /* X86 arm */
- 6.2.3.6 pci_slot_init()
- 6.2.3.7 acpi_pci_root_init()
- 6.2.3.8 acpi_pci_link_init()
- 6.2.3.9 pci_subsys_init()
- 6.2.3.10 pcibios_assign_resources()
- 6.2.4 kernel扫描关键的四个函数
- 6.2.4.1 pci_scan_child_bus()
- 6.2.4.2 pci_bus_add_devices()
- 6.2.4.3 __pci_bus_size_bridges()
- 6.2.4.4 __pci_bus_assign_resources()
- 7. 小结
1. 域
域英文叫domain
域之原义指地方的范围,后逐渐演变为数学、生物、科技等学科的某类单位的分类词语。
在内核中认为域是一个使用同一编址方式的范围。
比如:
pci域,使用pci地址进行寻址
x86域,使用x86物理地址进行寻址
域有主导者,访问都通过它进行
pci域,主导者是pci root,如果需要访问一个pci设备,就需要通过主导者pci root进行,它能识别的地址是pci地址
x86域,主导者是x86 cpu,如果需要访问一个内存,就需要通过主导者x86 cpu进行,它能识别的地址是x86物理地址
如果引申一下有物理地址,线性地址,逻辑地址,虚拟地址。
pci中所以所有的寻址都是使用pci地址
2. pci设备
pci有三种设备,有一种叫cardbus没接触过不提,所以认为两种,普通设备和桥设备
普通设备的功能是系统需要的具体功能,具体功能最终也会归到数据传输,数据处理上面去。
如果一个pci设备是这么组成的
pci总线接口 – 内存 – 网络数据处理器 – 网络接口
网络数据通过网络接口进到网络数据处理器,处理后放到pci设备的内存上,再通过pci总线接口将数据发送到pci总线上,于是另一个pci设备就能够获取网络数据,反方向就是发送网络数据
很明显,这个pci设备就是一个pci网卡
类似的
pci总线接口 – 内存 – 视频数据处理器 – 视频接口
一个pci显卡
pci总线接口 – 内存 – 音频数据处理器 – 音频接口
一个pci声卡
3. PCI桥
桥设备的功能是扩展pci总线,根据pci协议,一个总线上支持32个dev,每个dev支持8中功能,数量不够多,扩展功能是必要的。
命令lspci -t
查看拓扑
uos@uos-PC:~$ lspci -t
-[0000:00]-+-00.0
+-04.0
+-04.1
+-05.0
+-05.1
+-07.0
+-08.0
+-08.1
+-08.2
+-0a.0-[01]----00.0
+-0b.0-[02]----00.0
+-0d.0-[03]----00.0
+-0f.0-[04]--+-00.0
| \-00.1
+-13.0-[05]----00.0
+-16.0
\-17.0
可以看到pci总线0下有多个设备
- 普通设备
00.0
,04.0
,04.1
,05.0
,05.1
, ,07.0
,08.0
,08.1
,08.2
,16.0
,17.0
, - 桥设备
0a.0
,0b.0
,0d.0
,0f.0
,13.0
, - 桥
0a.0
扩展总线01
,桥0b.0
扩展总线02
,桥0d.0
扩展总线03
,桥0f.0
扩展总线04
,桥13.0
扩展总线05
; - 总线
01
下有设备01:00.0
,总线02
下有设备02:00.00
,总线03
下有设备03:00.00
,总线05
下有设备04:00.00
,04:00.01
,总线05
下有设备05:00.00
正好和lspci
对应上了
uos@uos-PC:~$ lspci
00:00.0 Host bridge: Loongson Technology LLC Hyper Transport Bridge Controller
00:04.0 USB controller: Loongson Technology LLC OHCI USB Controller (rev 01)
00:04.1 USB controller: Loongson Technology LLC EHCI USB Controller (rev 01)
00:05.0 USB controller: Loongson Technology LLC OHCI USB Controller (rev 01)
00:05.1 USB controller: Loongson Technology LLC EHCI USB Controller (rev 01)
00:07.0 Audio device: Loongson Technology LLC HDA (High Definition Audio) Controller
00:08.0 SATA controller: Loongson Technology LLC SATA AHCI Controller
00:08.1 SATA controller: Loongson Technology LLC SATA AHCI Controller
00:08.2 SATA controller: Loongson Technology LLC SATA AHCI Controller
00:0a.0 PCI bridge: Loongson Technology LLC PCI-to-PCI Bridge (rev 02)
00:0b.0 PCI bridge: Loongson Technology LLC PCI-to-PCI Bridge (rev 02)
00:0d.0 PCI bridge: Loongson Technology LLC PCI-to-PCI Bridge (rev 02)
00:0f.0 PCI bridge: Loongson Technology LLC PCI-to-PCI Bridge (rev 02)
00:13.0 PCI bridge: Loongson Technology LLC PCI-to-PCI Bridge (rev 02)
00:16.0 System peripheral: Loongson Technology LLC SPI Controller
00:17.0 ISA bridge: Loongson Technology LLC LPC Controller
01:00.0 USB controller: Etron Technology, Inc. EJ188/EJ198 USB 3.0 Host Controller
02:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 15)
03:00.0 SATA controller: ASMedia Technology Inc. ASM1062 Serial ATA Controller (rev 02)
04:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Oland [Radeon HD 8570 / R5 430 OEM / R7 240/340 / Radeon 520 OEM] (rev 87)
04:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Oland/Hainan/Cape Verde/Pitcairn HDMI Audio [Radeon HD 7000 Series]
05:00.0 Non-Volatile memory controller: Device 1dbe:5216 (rev 01)
4. pci配置空间
pci是一种总线,特点是灵活可配置,因为它有一个专门的区域叫配置空间。
配置空间通过io
总线访问,x86
是0xCF8
,0xCFC
,一个是控制端口一个是数据端口,不同平台地址不一样。
可以通过命令可查看cat /proc/ioports
uos@uos-PC:~$ sudo cat /proc/ioports
请输入密码:
验证成功
0000-0cf7 : PCI Bus 0000:00
...
0cf8-0cff : PCI conf1
0d00-ffff : PCI Bus 0000:00
桥设备和普通设备的配置空间内容不一样
桥设备的配置空间需要说明pci总线的拓扑,内存映射的地址范围等等
普通设备的配置空间需要说明该设备 内存映射的地址范围,pci的中断号等等
5. pci内存映射空间
例如一个pci声卡
pci总线接口 – 内存 – 音频数据处理器 – 音频接口
音频数据处理器处理完的数据存放到pci设备的内存中,前面说pci配置空间说明内存映射空间地址范围,即将这块内存映射到pci总线域的哪块地址,这样在pci总线域中访问这块地址就是访问这块内存,继而在pci总线上访问到这块内存从而获取其中的音频数据。
root@uos-PC:/proc# lspci -v -s 04:00.0
04:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Oland [Radeon HD 8570 / R5 430 OEM / R7 240/340 / Radeon 520 OEM] (rev 87) (prog-if 00 [VGA controller])
Subsystem: Bitland(ShenZhen) Information Technology Co., Ltd. Oland [Radeon HD 8570 / R7 240/340 OEM]
Flags: bus master, fast devsel, latency 0, IRQ 64, NUMA node 0
Memory at e0040000000 (64-bit, prefetchable) [size=256M]
Memory at e0051300000 (64-bit, non-prefetchable) [size=256K]
I/O ports at 6000 [size=256]
Expansion ROM at e0051340000 [disabled] [size=128K]
Capabilities: [48] Vendor Specific Information: Len=08 <?>
Capabilities: [50] Power Management version 3
Capabilities: [58] Express Legacy Endpoint, MSI 00
Capabilities: [a0] MSI: Enable+ Count=1/1 Maskable- 64bit+
Capabilities: [100] Vendor Specific Information: ID=0001 Rev=1 Len=010 <?>
Capabilities: [150] Advanced Error Reporting
Capabilities: [200] #15
Capabilities: [270] #19
Kernel driver in use: amdgpu
Kernel modules: radeon, amdgpu
这里面的信息都是从配置空间获取的,可以看到有2个内存映射空间
Memory at e0040000000 (64-bit, prefetchable) [size=256M]
Memory at e0051300000 (64-bit, non-prefetchable) [size=256K]
I/O ports at 6000 [size=256]
只是第三个有些特殊是io port
的映射区间,另外的两个都是真实的内存映射区间,第一个个对应的就是显卡上的256MB
显存
6. 扫描
经过前面内容,可以猜想pci设备的初始化流程应该是
- 通过io总线配置pci配置空间
- 通过io总线读取pci配置空间信息,获取内存映射空间的映射地址信息
- 通过读写内存映射空间实现数据传输
6.1 BIOS pci 扫描
最先进行pci扫描的是x86上的BIOS,扫描的结果是各个pci普通设备和pci桥都被配置完成,但是这种配置有可能是不能用的。
原因需要回到域的概念,简单的考虑,x86系统中存在两个域,x86域和pci域,由于x86系统的特殊性,两个域被设计成对等映射的关系。
也就是说,虽然x86域中使用x86地址进行访问,pci域中使用pci地址进行访问,但是两者对同一个实体的地址是相等的。这样带来的问题是两个域的地址不能重合,需要分开。
root@uos-PC:/home/uos# cat /proc/iomem
00200000-0effffff : System RAM
00200000-00d40c63 : Kernel code
00d40c64-0166513b : Kernel data
0166513c-0173ffff : reserved
01740000-0182130f : Kernel bss
01821310-0588ffff : reserved
06000000-0601bfff : reserved
06800000-0681bfff : reserved
07000000-0701bfff : reserved
07800000-0781bfff : reserved
10080000-10080007 : serial
10080100-10080107 : serial
10080200-10080207 : serial
10080300-10080307 : serial
10090000-10090007 : LOON0004:00
10090000-10090007 : LOON0004:00
10090100-10090107 : LOON0004:01
10090100-10090107 : LOON0004:01
10090200-10090207 : LOON0004:02
10090200-10090207 : LOON0004:02
10090300-10090307 : LOON0004:03
10090300-10090307 : LOON0004:03
10090400-10090407 : LOON0004:04
10090400-10090407 : LOON0004:04
10090500-10090507 : LOON0004:05
10090500-10090507 : LOON0004:05
100a0000-100a000f : LOON0006:00
100a0000-100a000f : LOON0006:00
100a0100-100a010f : LOON0006:01
100a0100-100a010f : LOON0006:01
100a0200-100a020f : LOON0006:02
100a0200-100a020f : LOON0006:02
100a0300-100a030f : LOON0006:03
100a0300-100a030f : LOON0006:03
100d000c-100d0013 : ACPI PM1a_EVT_BLK
100d0014-100d0017 : ACPI PM1a_CNT_BLK
100d0018-100d001b : ACPI PM_TMR
100d0028-100d002f : ACPI GPE0_BLK
100d0100-100d01ff : LOON0001:00
100d0100-100d01ff : LOON0001:00
100e0000-100e0bff : LOON0002:00
100e0000-100e0bff : LOON0002:00
1fe001e0-1fe001e7 : serial
90200000-bfffffff : System RAM
c0020000-fcaaffff : System RAM
f3ac0000-f9d5bfff : reserved
fcafc000-fd343fff : System RAM
fd34c000-fd357fff : System RAM
fd3c0000-fe43bfff : System RAM
fe000000-fe43bfff : reserved
fe498000-27fffffff : System RAM
fe498000-ffffffff : reserved
23a000000-23dffffff : reserved
23ffe8000-23ffeffff : reserved
27fff0000-27fffbfff : reserved
27fffc000-27fffffff : reserved
e0040000000-e007fffffff : PCI Bus 0000:00
e0040000000-e004fffffff : PCI Bus 0000:04
e0040000000-e004fffffff : 0000:04:00.0
e0050000000-e0050ffffff : 0000:00:16.0
e0051000000-e00510fffff : PCI Bus 0000:01
e0051000000-e0051007fff : 0000:01:00.0
e0051000000-e0051007fff : xhci-hcd
e0051100000-e00511fffff : PCI Bus 0000:02
e0051100000-e0051103fff : 0000:02:00.0
e0051100000-e0051103fff : r8168
e0051104000-e0051104fff : 0000:02:00.0
e0051104000-e0051104fff : r8168
e0051200000-e00512fffff : PCI Bus 0000:03
e0051200000-e005120ffff : 0000:03:00.0
e0051210000-e00512101ff : 0000:03:00.0
e0051210000-e00512101ff : ahci
e0051300000-e00513fffff : PCI Bus 0000:04
e0051300000-e005133ffff : 0000:04:00.0
e0051340000-e005135ffff : 0000:04:00.0
e0051360000-e0051363fff : 0000:04:00.1
e0051360000-e0051363fff : ICH HD audio
e0051400000-e00514fffff : PCI Bus 0000:05
e0051400000-e005141ffff : 0000:05:00.0
e0051420000-e0051423fff : 0000:05:00.0
e0051420000-e0051423fff : nvme
e0051500000-e005150ffff : 0000:00:07.0
e0051500000-e005150ffff : Loongson HDA
e0051510000-e0051517fff : 0000:00:04.0
e0051510000-e0051517fff : ohci_hcd
e0051518000-e005151ffff : 0000:00:04.1
e0051518000-e005151ffff : ehci_hcd
e0051520000-e0051527fff : 0000:00:05.0
e0051520000-e0051527fff : ohci_hcd
e0051528000-e005152ffff : 0000:00:05.1
e0051528000-e005152ffff : ehci_hcd
e0051530000-e0051531fff : 0000:00:08.0
e0051530000-e0051531fff : ahci
e0051532000-e0051533fff : 0000:00:08.1
e0051532000-e0051533fff : ahci
e0051534000-e0051535fff : 0000:00:08.2
e0051534000-e0051535fff : ahci
e0051536000-e0051536fff : 0000:00:0a.0
e0051537000-e0051537fff : 0000:00:0b.0
e0051538000-e0051538fff : 0000:00:0d.0
e0051539000-e0051539fff : 0000:00:0f.0
e005153a000-e005153afff : 0000:00:13.0
e005153b000-e005153bfff : ls-spi.0
e005153b000-e005153bfff : 0000:00:16.0
e005153b000-e005153bfff : ls-spi io
efe00000000-efe1fffffff : PCI ECAM
后面一段基本都是pci设备,前面一段基本都是ram,两者没有重合部分。
回到BIOS配置可能不可用的问题,如果kernel
将系统内存也安排到了BIOS配置pci的地址区域,出现重合,系统就乱了。
6.2 kernel扫描
kenel会自行再扫描一次,并合理安排pci地址空间和x86空间的地址。
内核有个配置选项与此相关
CONFIG_PCI_BIOS=n
CONFIG_PCI_MMCONFIG=y
CONFIG_PCI_DIRECT=y
但是龙芯平台上没有这三个配置项。
6.2.1 扫描流程
root@uos-PC:/boot# cat System.map-4.19.0-loongson-3-desktop | grep pci | grep __initcall
900000000172b230 t __initcall_pci_realloc_setup_params0
900000000172b3e0 t __initcall_pcibus_class_init2
900000000172b3e8 t __initcall_pci_driver_init2
900000000172b4d0 t __initcall_acpi_pci_init3
900000000172b678 t __initcall_pci_slot_init4
900000000172b780 t __initcall_pcibios_init4
900000000172b988 t __initcall_pci_apply_final_quirks5s
900000000172bd58 t __initcall_pci_proc_init6
900000000172bd60 t __initcall_pcie_portdrv_init6
900000000172bd68 t __initcall_pci_hotplug_init6
900000000172bd78 t __initcall_loongson_pci_driver_init6
900000000172bd80 t __initcall_loongson_ppci_driver_init6
900000000172bdc8 t __initcall_serial_pci_driver_init6
900000000172be70 t __initcall_mvumi_pci_driver_init6
900000000172be90 t __initcall_ahci_pci_driver_init6
900000000172bec8 t __initcall_stmmac_pci_driver_init6
900000000172c100 t __initcall_pci_resource_alignment_sysfs_init7
900000000172c108 t __initcall_pci_sysfs_init7
这些都是在kernel启动过程中会执行的pci相关函数,有些明显不是通用的可以忽略
整个扫描流程分为两部分 扫描设备 和 分配资源
6.2.2 扫描设备
从主桥开始扫
pcibios_scan_root(0)
>>> pci_scan_bus_parented()
>>> pci_scan_child_bus()
>>> for (devfn = 0; devfn < 0x100; devfn += 8)
pci_scan_slot(bus, devfn);
前面有说一个总线下支持32个dev,每个dev支持8个fn,这里就是扫描总线下所有的dev
所有的dev有普通pci设备,还有pci桥
pci桥会扩展出另一个总线号,其下可能挂有其他设备,所以还需要扫描找到的pci桥
>>> pci_scan_single_device()
>>> pci_scan_device()
>>> pci_setup_device()
>>> pci_read_irq()
>>> pci_read_bases()
>>> __pci_read_base()
>>> pci_size()
获取pci设备信息,中断号,内存映射区域大小和地址等等
>>> pci_device_add()
>>> list_add_tail(&dev->bus_list, &bus->devices)
扫到的设备全存放在 bus->devices
链表中,后面会有很多扫描依赖这个链表
常见的比如: list_for_each_entry(dev, &bus->devices, bus_list)
就是循环该bus下所有pci设备
>>> pci_scan_bridge()
>>> pci_find_bus()
>>> pci_add_new_bus()
桥扩展出来的总线给一个新的总线号
>>> pci_scan_child_bus()
递归扫描新扫到的pci桥
这样是扫描到桥就往深处走,称深度优先扫描,将所有的pci设备扫描完毕,并获取内存映射区域信息
6.2.3 分配资源
从主桥开始
__pci_bus_assign_resources()
>>> pbus_assign_resources_sorted()
>>> __dev_sort_resources()
>>> pdev_sort_resources()
>>> pci_resource_alignment()
>>> resource_alignment()
>>> resource_size()
将bus下所有dev所需要占用的资源都放入一个链表上–head
>>> __assign_resources_sorted()
>>> assign_requested_resources_sorted()
>>> resource_size()
>>> pci_assign_resource()
>>> pci_resource_alignment()
>>> __pci_assign_resource()
>>> pci_bus_alloc_resource()
### allocate a resource from a parent bus
从bus所含资源中给链表head
上所注明资源分配空间
>>> allocate_resource()
>>> find_resource()
在制定范围内寻找合适的空间[min, max]
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
max = PCIBIOS_MAX_MEM_32;
匹配方式可看resource_clip()
,移动最小匹配
>>> __request_resource()
分配
>>> request_resource_conflict()
### request and reserve an I/O or memory resource
>>> pci_update_resource()
>>> pci_resource_bar()
更新配置空间
>>> adjust_resources_sorted()
>>>
>>> __pci_bus_assign_resources()
### recursion
>>> pci_setup_bridge()
更新桥的配置空间,桥所包含地址范围
>>> __pci_setup_bridge()
>>> pci_setup_bridge_io()
>>> res = bus->resource[0]
>>> pcibios_resource_to_bus(bridge, ®ion, res)
### write to io base and limit
>>> pci_setup_bridge_mmio()
>>> res = bus->resource[1]
>>> pcibios_resource_to_bus(bridge, ®ion, res)
### write to mem base and limit
>>> pci_setup_bridge_mmio_pref()
>>> res = bus->resource[2]
>>> pcibios_resource_to_bus(bridge, ®ion, res)
### write to pref mem base and limit
下面列出较具体流程
6.2.3.1 pci_reboot_init
>>> dmi_check_system(pci_reboot_dmi_table)
只有apple相关产品会做一些修正,不关心
6.2.3.2 pcibus_class_init
>>> class_register(&pcibus_class)
>>> release_pcibus_dev()
>>> pci_bus_remove_resources()
注册pci class
6.2.3.3 pci_driver_init()
>>> bus_register(&pci_bus_type)
>>> bus_register(&pci_bus_type)
>>> pci_bus_match()
>>> pci_match_device()
>>> pci_match_id()
>>> pci_match_one_device()
### id->vendor == dev->vendor, id->device == dev->device
>>> pci_uevent()
>>> add_uevent_var()
### add key value string to the environment buffer
>>> pci_device_probe()
>>> pci_match_device()
>>> pci_call_probe()
>>> local_pci_probe()
>>> ddi->drv->probe(ddi->dev, ddi->id)
>>> pci_device_remove()
>>> drv->remove(pci_dev)
>>> pci_device_shutdown()
>>> drv->shutdown(pci_dev)
>>> pci_msi_shutdown(pci_dev)
>>> pci_msix_shutdown(pci_dev)
>>> pci_dev_attrs
>>> pci_bus_attrs
>>> bus_rescan_store()
>>> b = pci_find_next_bus()
>>> pci_rescan_bus(b)
注册pci驱动,但是这里注意 pci_rescan_bus(b)
在pci hotplug
中是很有用的,文章linux重新扫描pci总线中有详细描述。
6.2.3.4 acpi_pci_init()
从ACPI获取信息,ACPI还是很复杂的,可以简单看作一堆表示系统相关信息的表
6.2.3.5 pci_arch_init() /* X86 arm */
>>> pci_direct_probe()
>>> request_region(0xCF8, 8, "PCI conf1")
这里就是保留io总线访问配置空间的资源,0xCF8
0xCFC
>>> pci_mmcfg_early_init()
>>> __pci_mmcfg_init()
>>> acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg)
### mmconfig address is coming from ACPI table
MMCONFIG
对于访问pcie设备配置空间很有作用,因为PCI的配置空间是256
,而PCIE的配置空间是4K
,多出的部分io总线访问不了,需要mmconfig
进行访问
>>> pci_parse_mcfg()
>>> pci_mmconfig_add()
>>> pci_mmcfg_reject_broken()
>>> is_mmconf_reserved()
>>> pci_mmcfg_arch_init()
>>> x86_init.pci.arch_init()
>>> pci_acpi_init()
### about pci irq
>>> pci_pcbios_init()
>>> pci_find_bios()
### find BIOS32 0xe0000 - 0xfffff
>>> pci_direct_init()
### raw_pci_ops = &pci_direct_conf1
>>> dmi_check_pciprobe()
>>> dmi_check_skip_isa_align()
6.2.3.6 pci_slot_init()
>>> pci_slots_kset = kset_create_and_add("slots", NULL, &pci_bus_kset->kobj)
6.2.3.7 acpi_pci_root_init()
>>> acpi_hest_init()
>>> pci_acpi_crs_quirks()
>>> acpi_bus_register_driver(&acpi_pci_root_driver)
### about irq
6.2.3.8 acpi_pci_link_init()
>>> acpi_bus_register_driver(&acpi_pci_link_driver)
### about irq
6.2.3.9 pci_subsys_init()
>>> pci_legacy_init()
>>> pci_root_bus = pcibios_scan_root(0)
>>> bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd)
>>> b = pci_create_bus(parent, bus, ops, sysdata)
>>> b->subordinate = pci_scan_child_bus(b)
### ...
>>> pci_bus_add_devices(pci_root_bus)
>>> pci_bus_add_device()
### insert newly discovered PCI devices
>>> pci_bus_add_child()
### add a child bus
>>> pcibios_fixup_peer_bridges()
>>> x86_init.pci.init_irq()
>>> pcibios_init()
>>> pcibios_resource_survey()
>>> pcibios_allocate_bus_resources()
### for PCI_BRIDGE_RESOURCES, for bridge
>>> pci_claim_resource()
>>> pci_find_parent_resource()
>>> request_resource_conflict()
>>> __request_resource()
>>> pcibios_allocate_bus_resources()
### recursion
>>> pcibios_allocate_resources()
### from PCI_STD_RESOURCES to PCI_STD_RESOURCE_END
>>> pci_claim_resource()
>>> e820_reserve_resources_late()
>>> ioapic_insert_resources()
>>> insert_resource(&iomem_resource, ioapic_resources)
6.2.3.10 pcibios_assign_resources()
>>> pci_assign_unassigned_resources()
>>> __pci_bus_size_bridges()
>>> __pci_bus_size_bridges()
>>> pci_bridge_check_ranges()
>>> pbus_size_io()
>>> pbus_size_mem()
>>> __pci_bus_assign_resources()
>>> pbus_assign_resources_sorted()
>>> __pci_bus_assign_resources()
>>> pci_setup_bridge()
>>> __pci_setup_bridge()
>>> pci_setup_bridge_io()
>>> pci_setup_bridge_mmio()
>>> pci_setup_bridge_mmio_pref()
>>> pci_enable_bridges()
>>> pci_bus_dump_resources()
6.2.4 kernel扫描关键的四个函数
6.2.4.1 pci_scan_child_bus()
>>> pci_scan_slot()
### scan a PCI slot on a bus for devices
>>> pci_scan_single_device()
>>> pci_get_slot()
### check devfn is exist or not under bus
>>> pci_scan_device()
### check VID and DID to decide devfn is exist or not
>>> alloc_pci_dev()
>>> pci_setup_device()
>>> pci_fixup_device(pci_fixup_early, dev)
### do early fixup here
>>> pci_read_bases()
### read pci bar 0 - 5
>>> __pci_read_base()
### read a pci bar for region and size
>>> pci_device_add()
>>> pci_fixup_device(pci_fixup_header, dev)
>>> list_add_tail(&dev->bus_list, &bus->devices)
### scan all devices under one bus, then add them (dev->bus_list) to bus->devices
>>> pci_iov_bus_range()
>>> pcibios_fixup_bus()
>>> pci_read_bridge_bases()
>>> pci_bus_remove_resources(child)
>>> pci_read_bridge_io(child)
>>> pci_read_bridge_mmio(child)
>>> pci_read_bridge_mmio_pref(child)
>>> pci_bus_add_resource()
>>> pci_scan_bridge()
### find a bridge, then want to find bus under the bridge in system.
>>> pci_find_bus()
### find the bus
>>> pci_do_find_bus()
>>> pci_do_find_bus()
### recursion
>>> pci_add_new_bus()
### do not find the bus, so add a new one
>>> pci_scan_child_bus()
### recursion
>>> pci_fixup_parent_subordinate_busnr()
6.2.4.2 pci_bus_add_devices()
>>> pci_bus_add_device()
>>> pci_bus_add_devices()
### recursion
>>> pci_bus_add_child()
6.2.4.3 __pci_bus_size_bridges()
### if it is bridge, recursion
>>> __pci_bus_size_bridges()
### for bridge
>>> pci_bridge_check_ranges()
### check support 64bit or not
>>> pbus_size_io()
### calc io resource of all devices under this bus
>>> pbus_size_mem()
### calc mem resource of all devices under this bus
6.2.4.4 __pci_bus_assign_resources()
>>> pbus_assign_resources_sorted()
>>> __dev_sort_resources()
>>> pdev_sort_resources()
>>> pci_resource_alignment()
>>> resource_alignment()
>>> resource_size()
>>> __assign_resources_sorted()
>>> assign_requested_resources_sorted()
>>> resource_size()
>>> pci_assign_resource()
>>> pci_resource_alignment()
>>> __pci_assign_resource()
>>> pci_bus_alloc_resource()
### allocate a resource from a parent bus
>>> allocate_resource()
>>> find_resource()
>>> __request_resource()
>>> request_resource_conflict()
### request and reserve an I/O or memory resource
>>> __request_resource()
>>> pci_update_resource()
>>> pci_resource_bar()
>>> adjust_resources_sorted()
>>>
>>> __pci_bus_assign_resources()
### recursion
>>> pci_setup_bridge()
>>> __pci_setup_bridge()
>>> pci_setup_bridge_io()
>>> res = bus->resource[0]
>>> pcibios_resource_to_bus(bridge, ®ion, res)
### write to io base and limit
>>> pci_setup_bridge_mmio()
>>> res = bus->resource[1]
>>> pcibios_resource_to_bus(bridge, ®ion, res)
### write to mem base and limit
>>> pci_setup_bridge_mmio_pref()
>>> res = bus->resource[2]
>>> pcibios_resource_to_bus(bridge, ®ion, res)
### write to pref mem base and limit
7. 小结
linux pci 扫描流程
- BIOS先扫描,但是扫描的结果可能不能直接用。
- kernel扫描分两步, 扫描设备和分配资源。
- 扫描设备会从主桥开始,按照深度优先方式扫描所有设备,如果遇到一个桥设备则扩展一个总线号,并对该总线进行扫描。
- 扫描会获取pci总线拓扑和各个设备所需要的资源信息。
- 分配资源也从主桥开始,按照深度优先方式扫描所有设备,并按需为设备分配资源和更新配置信息。
- 桥根据其下设备所分配的资源更新配置范围,以供pci总线路由。
- x86域的地址资源和pci域的地址资源是对等映射,所以分配需考虑到两者不能冲突。
refer to:
- 理解linux pci 扫描流程