pciconf.c 函数调用流程、执行流程与架构梳理
一、文件整体定位
该文件是通用PCI总线配置核心实现,主要完成PCI/PCIe设备的扫描、配置空间读写、中断路由、地址空间(IO/MEM)分配、PCIe参数(MPS/MRRS)配置、桥设备管理等核心功能,适配龙芯(LOONGSON)2K/3C6000/LS7A等平台,遵循BSD许可协议。
核心目标
- 枚举系统中所有PCI/PCIe设备并构建设备树;
- 为设备分配IO/Memory地址空间,配置BAR(Base Address Register);
- 处理PCI桥设备的总线编号、地址窗口映射;
- 配置PCIe关键参数(最大有效载荷MPS、最大读请求MRRS);
- 处理中断路由、IOMMU设备管理等平台相关逻辑。
二、核心数据结构(隐式梳理)
从代码中提炼关键数据结构,理解架构基础:
| 结构名 | 核心作用 |
|---|---|
pci_device |
描述PCI设备实例,包含总线/设备/功能号(BDF)、配置空间标签、类码、父设备、桥信息、BAR窗口等 |
pci_bus |
描述PCI总线,包含总线号、带宽、延迟参数、地址空间范围、设备列表等 |
pci_win |
描述PCI地址窗口(BAR/桥窗口),包含地址、大小、对齐、所属设备、寄存器偏移等 |
pci_intline_routing |
中断路由信息,关联设备与中断线 |
三、函数调用流程(核心链路)
1. 顶层入口:PCI总线扫描初始化(隐含入口)
代码中未直接展示main入口,但核心触发点是_pci_scan_dev(扫描单条总线上的设备),而_pci_scan_dev被_pci_query_dev(桥设备处理时)调用,整体扫描流程的顶层触发逻辑为:
_setup_pcibuses() ------[初始化总线]→ _pci_scan_dev() ------[扫描设备]→ _pci_query_dev() ------[查询设备]→ _pci_query_dev_func()
2. 核心函数调用链路(按执行顺序)
阶段1:设备扫描与枚举
无效
有效
是
否
_pci_scan_dev(父设备, 总线号, 设备号, 初始化标志)
_pci_query_dev(父设备, 总线号, 设备号, 初始化标志)
_pci_make_tag(生成PCI配置空间标签)
_pci_conf_read(读取设备ID寄存器)
设备是否有效(ID≠0/0xFFFF)
返回
读取设备头类型(BHLC_REG)
是否多功能设备?
遍历0-7功能号
处理单功能设备
_pci_query_dev_func(父设备, PCI标签, 初始化标志)
阶段2:设备属性解析与初始化(_pci_query_dev_func)
是
否
是
否
_pci_query_dev_func
读取设备类码/ID,打印设备信息(VERBOSE模式)
分配pci_device结构体,填充BDF/标签/类码等基础信息
_pci_setupIntRouting(配置中断路由)
关闭设备命令寄存器(禁止IO/MEM/主模式,初始化阶段)
更新总线属性(66MHz/快速背靠背/设备响应时间)
是否是PCI桥设备?
处理桥设备:分配总线号、初始化子总线pci_bus结构体
_pci_bus_insert(插入总线链表)
_pci_scan_dev(递归扫描桥的次级总线)
计算桥的IO/MEM窗口大小,插入父设备窗口链表
是否是SR-IOV设备?(非2K平台)
处理VF设备,计算VF的BDF,更新子总线号
处理普通设备:配置BAR寄存器
遍历BAR寄存器(0-5/ROM)
_pci_conf_write(写入全1测试大小)
_pci_conf_read(读取BAR掩码,计算地址空间大小)
_insertsort_window(插入地址窗链表,按对齐排序)
阶段3:PCIe参数配置(龙芯2K/LS7A平台)
pcie_write_mps(配置最大有效载荷)
pcie_get_branch_min_mps(获取分支最小MPS)
pcie_get_mpss(读取设备支持的最大MPS)
pcie_set_mps(设置设备MPS为分支最小值)
pcie_write_mrrs(配置最大读请求)
pcie_get_readrq(读取桥的MRRS)
pcie_set_readrq(设置设备/桥的MRRS)
阶段4:地址空间分配(_pci_setup_windows)
失败
成功
_pci_setup_windows(分配IO/MEM地址)
遍历设备MEM窗口链表
_pci_allocate_mem(分配内存地址)
分配是否成功?
使用PCI_BIGMEM_ADDRESS分配大内存地址
写入设备BAR寄存器/桥窗口寄存器
遍历IO窗口链表
_pci_allocate_io(分配IO地址)
写入设备BAR寄存器/桥窗口寄存器
处理VGA设备ROM地址(特殊逻辑)
3. 辅助函数调用链路
| 函数名 | 被调用位置 | 核心作用 |
|---|---|---|
_pci_roundup |
桥窗口计算 | 地址/大小向上取整到指定粒度(如IO窗口取整到0x1000,MEM到0x100000) |
_pci_device_insert |
_pci_query_dev_func | 将设备插入父设备的子设备链表(维护设备树) |
_pci_bus_insert |
桥设备处理 | 将新总线插入全局总线链表 |
_insertsort_window |
BAR/桥窗口处理 | 按对齐大小插入排序窗口链表(大对齐优先,保证地址分配连续) |
is_power_of_2/ffs/fls |
PCIe参数配置/地址计算 | 辅助判断2的幂、计算位位置(MPS/MRRS配置)、地址对齐 |
set_pcie_port_type |
桥设备处理 | 识别PCIe设备类型,填充pci_device的pcie_type/pcie_cap字段 |
四、文件内执行流程(按代码块逻辑)
1. 预处理与全局变量初始化
- 定义编译宏(PCIVERBOSE/LOONGSON_2K/LS7A等),控制不同平台逻辑;
- 初始化全局变量:
have_vga(VGA设备标签)、monarch_mode(总线主模式)、pci_roots(根总线数)、_pci_head(设备链表头)、_pci_bushead(总线链表头)等; - 定义工具宏(MIN/MAX/PRINTF)、内联函数(ffs/fls,位操作)。
2. PCIe参数配置函数(独立模块)
pcie_get_mpss:读取设备支持的最大有效载荷(MPS);pcie_get_mps:读取当前配置的MPS;pcie_set_mps:设置MPS(校验合法性:128~4096,2的幂);pcie_get_readrq/pcie_set_readrq:读取/设置最大读请求(MRRS);pcie_get_branch_min_mps:获取设备分支(父桥链)的最小MPS;pcie_write_mps/pcie_write_mrrs:龙芯平台核心逻辑,将设备/桥的MPS/MRRS配置为链路最小值(保证PCIe链路兼容)。
3. 设备扫描核心逻辑(_pci_scan_dev/_pci_query_dev/_pci_query_dev_func)
- 生成PCI配置空间标签(_pci_make_tag);
- 读取设备ID,过滤无效设备;
- 处理多功能设备,遍历所有功能号;
- 分配并初始化pci_device结构体,维护设备树;
- 处理桥设备:分配次级总线号,递归扫描子总线,计算地址窗口;
- 处理普通设备:解析BAR寄存器,计算地址空间大小,插入窗口链表;
- 平台相关逻辑:SR-IOV(VF设备)、IOMMU设备收集(龙芯3C6000)。
4. 地址空间分配(_pci_setup_windows)
- 遍历设备的MEM/IO窗口链表;
- 调用
_pci_allocate_mem/_pci_allocate_io分配地址(支持向上/向下分配); - 分配失败时使用
PCI_BIGMEM_ADDRESS作为备用地址; - 写入设备BAR寄存器或桥的窗口寄存器(MEMBASE/IOBASE);
- 特殊处理VGA设备的ROM地址(兼容传统BIOS)。
5. 辅助工具函数
- 中断路由:
_pci_setupIntRouting/_pci_getIntRouting(代码中未展示实现,但调用链路存在); - 内存分配:
pmalloc/pfree(封装的内存分配函数); - 打印函数:
_pci_bdfprintf/_pci_tagprintf(VERBOSE模式下打印设备BDF信息)。
四、架构设计梳理
1. 分层架构
| 层级 | 核心功能 | 核心函数 |
|---|---|---|
| 配置空间层 | 读写PCI配置空间寄存器(底层硬件交互) | _pci_conf_read/_pci_conf_write |
| 设备枚举层 | 扫描总线/设备/功能,构建设备树 | _pci_scan_dev/_pci_query_dev |
| 设备属性层 | 解析设备类码、中断、BAR、桥参数,维护总线属性 | _pci_query_dev_func |
| 地址分配层 | 为设备/BAR/桥窗口分配IO/MEM地址,写入硬件寄存器 | _pci_allocate_mem/_pci_setup_windows |
| PCIe适配层 | 配置PCIe链路参数(MPS/MRRS),保证链路兼容性 | pcie_write_mps/pcie_write_mrrs |
| 平台适配层 | 龙芯2K/3C6000/LS7A平台特殊逻辑(IOMMU、VGA、SR-IOV) | is_ls3c6000_pcie/set_pcie_port_type |
2. 链表管理设计
- 设备链表 :
_pci_head为根,通过pci_device.parent/next/bridge.child维护父子/兄弟关系,形成PCI设备树; - 总线链表 :
_pci_bushead为根,_pci_bus_insert插入新总线,管理所有PCI总线; - 地址窗口链表 :每个设备/桥维护
iospace/memspace链表,_insertsort_window按对齐大小排序,保证地址分配高效。
3. 平台适配设计
- 宏控区分平台:
LOONGSON_2K/LOONGSON_3C6000/LS7A,隔离不同平台的特殊逻辑; - 弱符号函数:
pci_alloc_fixmemio/pci_get_busno,允许平台重定义; - IOMMU管理:3C6000平台收集IOMMU设备范围,2K平台跳过;
- VGA适配:区分BMC VGA(AST2050)、AMD VGA、龙芯7A VGA,处理ROM地址。
4. 初始化策略
- 初始化阶段关闭设备的IO/MEM/主模式(避免干扰扫描),配置完成后再启用;
- 桥设备递归扫描:扫描完桥的次级总线后,再计算桥的地址窗口大小;
- 地址分配粒度:桥窗口按0x1000(IO)/0x100000(MEM)取整,保证硬件兼容性;
- VERBOSE分级:通过
PCIVERBOSE宏控制打印粒度,便于调试。
五、关键设计亮点
- 兼容性优先:PCIe MPS/MRRS配置为链路最小值,保证不同设备间的PCIe链路兼容;
- 资源高效分配:地址窗口按对齐大小排序,向上/向下分配地址,最大化利用地址空间;
- 平台解耦:通过宏控和弱符号实现平台差异化,核心逻辑通用;
- 设备树构建:基于链表的设备/总线树结构,清晰维护PCI拓扑关系;
- 鲁棒性处理:地址分配失败时使用备用大内存地址,设备ID读取前加延迟(避免硬件未就绪)。
六、总结
该文件是典型的PCI总线配置框架实现,核心逻辑遵循PCI/PCIe规范,同时深度适配龙芯平台特性。整体执行流程围绕"总线扫描→设备枚举→属性解析→地址分配→硬件配置"展开,通过分层设计、链表管理、平台宏控实现了通用性与平台适配的平衡。核心链路清晰,从顶层的总线扫描到底层的配置空间读写,形成了完整的PCI设备初始化闭环。