Linux系统总线之PCI扫描流程

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总线访问,x860xCF8,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设备的初始化流程应该是

  1. 通过io总线配置pci配置空间
  2. 通过io总线读取pci配置空间信息,获取内存映射空间的映射地址信息
  3. 通过读写内存映射空间实现数据传输

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, &region, res)
            ### write to io base and limit
        >>> pci_setup_bridge_mmio()
            >>>  res = bus->resource[1]
            >>> pcibios_resource_to_bus(bridge, &region, res)
             ### write to mem base and limit
        >>> pci_setup_bridge_mmio_pref()
             >>> res = bus->resource[2]
            >>>  pcibios_resource_to_bus(bridge, &region, 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, &region, res)
            ### write to io base and limit
        >>> pci_setup_bridge_mmio()
            >>>  res = bus->resource[1]
            >>> pcibios_resource_to_bus(bridge, &region, res)
             ### write to mem base and limit
        >>> pci_setup_bridge_mmio_pref()
             >>> res = bus->resource[2]
            >>>  pcibios_resource_to_bus(bridge, &region, res)
            ###  write to pref mem base and limit

7. 小结

linux pci 扫描流程

  1. BIOS先扫描,但是扫描的结果可能不能直接用。
  2. kernel扫描分两步, 扫描设备和分配资源。
  3. 扫描设备会从主桥开始,按照深度优先方式扫描所有设备,如果遇到一个桥设备则扩展一个总线号,并对该总线进行扫描。
  4. 扫描会获取pci总线拓扑和各个设备所需要的资源信息。
  5. 分配资源也从主桥开始,按照深度优先方式扫描所有设备,并按需为设备分配资源和更新配置信息。
  6. 桥根据其下设备所分配的资源更新配置范围,以供pci总线路由。
  7. x86域的地址资源和pci域的地址资源是对等映射,所以分配需考虑到两者不能冲突。

refer to:

  1. 理解linux pci 扫描流程
  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值