33、PCI 设备的枚举、地址空间与中断机制详解

PCI 设备的枚举、地址空间与中断机制详解

1. PCI(e) 总线枚举

PCI(e) 总线枚举过程看似复杂,实则简单。下面我们通过具体步骤来了解。

在步骤 I 中,PCIe - to - PCI 桥下游被分配总线号 9,此总线后有一个 3 功能端点。在 BDF 表示法中,它们分别标识为 09:00:00、09:00:01 和 09:00:02。由于端点标志着分支的深度,这使我们可以进行回溯,进入步骤 J。

在回溯阶段,进入步骤 J,找到 3 端口交换机的第三个也是最后一个下游虚拟桥,其总线被赋予总线号 10。此总线后有一个端点,在 BDF 格式中标识为 0a:00:00,这标志着枚举过程结束。

下面是枚举过程的 mermaid 流程图:

graph LR
    A[步骤 I] --> B[分配总线号 9]
    B --> C[发现 3 功能端点]
    C --> D[BDF 标识: 09:00:00, 09:00:01, 09:00:02]
    D --> E[回溯到步骤 J]
    E --> F[找到下游虚拟桥]
    F --> G[分配总线号 10]
    G --> H[发现端点]
    H --> I[BDF 标识: 0a:00:00]
    I --> J[枚举结束]
2. PCI 地址空间

PCI 目标设备根据内容或访问方法可实现多达三种不同类型的地址空间,分别是配置地址空间、内存地址空间和 I/O 地址空间。配置和内存地址空间是内存映射的,即它们从系统地址空间分配地址范围,对这些地址范围的读写操作不指向 RAM,而是直接从 CPU 路由到设备,而 I/O 地址空间则不是。

地址空间类型 特点
配置地址空间 用于访问设备配置,存储设备基本信息,由操作系统用于设置设备操作参数。PCI 上为 256 字节,PCIe 扩展到 4KB 寄存器空间。部分地址空间标准化,前 64 字节为标准配置头,后 192 字节为用户定义配置空间。使用配置读/写命令传输数据。
I/O 地址空间 用于与 x86 架构的 I/O 端口地址空间兼容,PCIe 规范不鼓励使用,未来可能会弃用。优点是不占用系统内存空间地址范围,32 位系统可访问完整 4GB RAM。使用 I/O 读/写命令传输数据。
内存地址空间 早期因处理器内存地址空间有限,使用 I/O 地址空间访问 I/O 设备寄存器。随着系统内存空间限制减小,I/O 地址空间局限性凸显,出现了内存映射 I/O(MMIO)。PCI 设备通过基地址寄存器(BAR)暴露内存区域,一个设备最多可有 6 个 BAR。
3. 基地址寄存器(BAR)

BAR 即基地址寄存器,是 PCI 概念,设备通过它告知主机所需内存量及其类型。这是从系统内存映射中获取的内存空间,而非实际物理 RAM。BIOS 或操作系统负责为目标设备分配请求的内存空间。

主机处理外围设备存储空间的步骤如下:
1. 外围设备通过某种方式告知系统其有多个存储区间和 I/O 地址空间,每个区间大小及其本地地址(本地且内部,从 0 开始)。
2. 系统软件了解外围设备数量和存储区间类型后,为这些区间分配“物理地址”(实际为逻辑地址,常称为“总线地址”),并建立这些区间与总线的连接。

4. 中断分配

PCI Express 有三种中断类型:
- 传统中断(Legacy interrupts,也叫 INTx 中断):旧 PCI 实现中唯一可用的机制。
- 基于消息的中断(MSI):扩展了传统机制,增加了可能的中断数量。
- 扩展 MSI(MSI - X):进一步扩展和增强 MSI,可将单个中断定向到不同处理器,适用于高速网络应用。

5. PCI 传统 INT - X 中断

传统中断管理基于 PCI INT - X 中断线,由最多四条虚拟中断线(INTA、INTB、INTC 和 INTD)组成,这些中断线由系统中所有 PCI 设备共享。处理中断的步骤如下:
1. 设备断言其 INT# 引脚之一以生成中断。
2. CPU 确认中断并轮询连接到该 INT# 线(共享)的每个设备(实际是其驱动程序),调用其中断处理程序。服务中断所需时间取决于共享该线的设备数量。设备的中断服务例程(ISR)可通过读取设备内部寄存器来检查中断是否源自该设备,以确定中断原因。
3. ISR 采取行动服务中断。

下面是传统 INT - X 中断处理流程的 mermaid 流程图:

graph LR
    A[设备生成中断] --> B[断言 INT# 引脚]
    B --> C[CPU 确认中断]
    C --> D[轮询设备]
    D --> E[调用中断处理程序]
    E --> F[ISR 检查中断源]
    F --> G[ISR 服务中断]
6. 基于消息的中断类型 - MSI 和 MSI - X

MSI 和 MSI - X 是两种基于消息的中断机制。MSI 允许设备通过 PCI Express 协议层发送少量描述中断的数据到特殊的内存映射 I/O 地址,根复合体负责将相应中断传递给 CPU。

使用 MSI 实现中断的步骤如下:
1. 设备通过向上游发送 MSI 内存写操作生成中断。
2. CPU 确认中断并根据 MSI 向量调用相应的设备 ISR。
3. ISR 采取行动服务中断。

MSI 机制最初在 PCI 2.2 标准中定义,设备可分配 1、2、4、8、16 或最多 32 个中断。其配置步骤由 PCI 控制器驱动程序为 PCI Express 设备执行:
1. 启动时进行总线枚举,内核 PCI 核心代码扫描 PCI 总线以发现设备。
2. 搜索能力寄存器集,直到找到 MSI 能力寄存器集(能力 ID 为 05h)。
3. 配置设备,为设备的消息地址寄存器分配内存地址。
4. 检查设备消息控制寄存器中的多消息能力字段,确定设备希望分配的特定事件消息数量。
5. 分配数量等于或小于设备请求的消息,至少分配一个消息。
6. 将基本消息数据模式写入设备的消息数据寄存器。
7. 设置设备消息控制寄存器中的 MSI 使能位,使其能够使用 MSI 内存写操作生成中断。

MSI - X 是 PCI MSI 在 PCIe 中的扩展,可支持至少 64 到最多 2048 个中断。它为每个中断提供单独的目标地址和数据字,解决了 MSI 中单地址的限制问题。此外,MSI - X 使能的端点还包含用于屏蔽和保持待处理中断的应用逻辑,以及用于地址和数据对的内存表。

7. 传统 INTx 模拟

由于 PCIe 声称与传统并行 PCI 向后兼容,因此需要支持基于 INTx 的中断机制。PCIe 通过使用带内信令机制(即 MSI)虚拟化 PCI 物理中断信号。每条物理线有两种状态(断言和去断言),PCIe 为每条线提供两条消息,即 assert_INTx 和 deassert_INTx 消息,共八种消息类型。这样,INTx 中断就像 MSI 和 MSI - X 一样通过 PCIe 链路传播。这种向后兼容性主要用于 PCI 到 PCIe 桥接芯片,使 PCI 设备在 PCIe 系统中无需修改驱动程序即可正常工作。

PCI 设备的枚举、地址空间与中断机制详解

8. Linux 内核 PCI 子系统与数据结构

Linux 内核支持 PCI 标准,并提供了处理此类设备的 API。在 Linux 中,PCI 实现大致可分为以下主要组件:
- PCI BIOS :这是与架构相关的部分,负责启动 PCI 总线初始化。ARM 特定的 Linux 实现位于 arch/arm/kernel/bios32.c 。PCI BIOS 代码与 PCI 主机控制器代码以及 PCI 核心进行交互,以执行总线枚举和资源(如内存和中断)的分配。BIOS 执行成功完成后,系统中的所有 PCI 设备将被分配可用的 PCI 资源,其相应的驱动程序(称为从设备或端点驱动程序)可以使用 PCI 核心提供的功能来控制它们。这里完成了两项重要的 PCI 配置任务:一是扫描总线上的所有 PCI 设备,对其进行配置并分配内存资源;二是配置设备(即预留资源(内存)并分配 IRQ,但不进行初始化,初始化由设备驱动程序完成)。PCI BIOS 可以选择跳过资源分配(例如,在 PC 场景中,如果在 Linux 启动之前已经分配了资源)。
- 主机控制器(根复合体) :这部分是特定于 SoC 的(位于 drivers/pci/host/ ,例如,对于 r - car SoC,位于 drivers/pci/controller/pcie - rcar.c )。一些 SoC 可能使用来自同一供应商(如 Synopsys DesignWare)的相同 PCIe IP 块,这些控制器可以在同一目录(如 drivers/pci/controller/dwc/ )中找到。例如,i.MX6 的 PCIe IP 块来自该供应商,其驱动程序实现于 drivers/pci/controller/dwc/pci - imx6.c 。这部分处理 SoC(有时是板级)特定的初始化和配置,并可能调用 PCI BIOS。它为 BIOS 和 PCI 核心提供 PCI 总线访问和功能回调函数,这些函数将在 PCI 系统初始化和访问 PCI 总线进行配置周期时被调用。此外,它还提供可用内存/IO 空间、INTx 中断线和 MSI 的资源信息。

下面是 Linux 内核 PCI 子系统组件关系的 mermaid 流程图:

graph LR
    A[PCI BIOS] --> B[PCI 主机控制器]
    A --> C[PCI 核心]
    B --> C
    C --> D[PCI 设备驱动]
9. 各组件作用总结
组件名称 作用
PCI BIOS 启动 PCI 总线初始化,与主机控制器和核心交互进行总线枚举和资源分配,完成设备扫描、配置及资源预留和 IRQ 分配。
主机控制器(根复合体) 处理 SoC 特定初始化和配置,提供总线访问和回调函数,提供资源信息。
PCI 核心 协调各组件工作,为设备驱动提供功能支持。
设备驱动 控制 PCI 设备,完成设备特定的初始化工作。
10. 整体流程梳理

从 PCI 总线枚举开始,系统逐步为 PCI 设备分配资源,确定其地址空间和中断方式。在这个过程中,PCI BIOS 和主机控制器起到了关键的初始化和资源分配作用。设备通过 BAR 告知主机所需内存,主机为其分配相应资源。在中断分配方面,传统的 INTx 中断、MSI 和 MSI - X 各有特点,适用于不同的应用场景。而 PCIe 通过模拟 INTx 中断实现了与传统 PCI 设备的向后兼容。

整个流程的步骤如下:
1. 进行 PCI 总线枚举,确定设备的总线号和端点标识。
2. 为设备分配地址空间,包括配置地址空间、I/O 地址空间和内存地址空间。
3. 设备通过 BAR 告知主机所需内存,主机分配相应资源。
4. 确定设备的中断类型,可选择传统 INTx 中断、MSI 或 MSI - X。
5. 若为 PCIe 系统,模拟 INTx 中断以支持传统 PCI 设备。
6. Linux 内核的 PCI BIOS 和主机控制器完成初始化和资源分配,设备驱动进行设备特定初始化。

下面是整体流程的 mermaid 流程图:

graph LR
    A[PCI 总线枚举] --> B[分配地址空间]
    B --> C[BAR 告知内存需求]
    C --> D[主机分配资源]
    D --> E[确定中断类型]
    E --> F[PCIe 模拟 INTx 中断]
    F --> G[Linux 内核初始化与分配]
    G --> H[设备驱动初始化]

通过对 PCI 设备的枚举、地址空间、中断分配以及 Linux 内核 PCI 子系统的详细介绍,我们对 PCI 设备在系统中的工作原理和管理方式有了更深入的了解。这些知识有助于我们在实际开发中更好地配置和使用 PCI 设备,解决可能遇到的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值