PCI ( 外设部件互连标准 )

优质
小牛编辑
126浏览
2023-12-01

Peripheral Component Interconnect ( PCI ),好像它的名字暗示的一样,是描述如何通过一个结构化和可控制的方式把系统中的外设组件连接起来的一个标准。标准的 PCI Local Bus 规范描述了系统组件电气连接的方法和它们行为的方法。本章探讨 Linux 核心如何初始化系统的 PCI 总线和设备。

图 6.1 是一个 PCI 基础的系统的逻辑图。 PCI 总线和 PCI-PCI 桥( bridge )是系统组件联系在一起的粘合剂。 CUP 和 video 设备连在主要的 PCI 总线, PCI 总线 0 。一个特殊的 PCI 设备, PCI-PCI 桥把主总线连接到次 PCI 总线, PCI 总线 1 。按照 PCI 规范的术语, PCI 总线 1 描述成为 PCI-PCI 桥的下游而 PCI 总线 0 是桥的上游。连接在次 PCI 总线上的是系统的 SCSI 和以太网设备。物理上桥、次要 PCI 总线和这两种设备可以在同一块 PCI 卡上。系统中的 PCI-ISA 桥支持老的、遗留的 ISA 设备,本图显示了一个超级 I/O 控制芯片,控制键盘、鼠标和软驱。

6.1 PCI Address Space ( PCI 地址空间)

CPU 和 PCI 设备需要访问它们所共享的内存。这些内存让设备驱动程序控制这些 PCI 设备并在它们之间传递信息。一般地共享的内存包括设备的控制和状态寄存器。这些寄存器用于控制设备和读取它的状态。例如: PCI SCSI 设备驱动程序可以读取 SCSI 设备的状态寄存器,判断它是否可以向 SCSI 磁盘写一块信息。或者它可以写入控制寄存器让它关闭的设备开始运行。

CPU 的使用的系统内存可以用作这种共享内存,但是如果这样的话,每一次 PCI 设备访问内存, CPU 都不得不停顿,等待 PCI 设备完成。对于内存的访问通常有限制,同一时间只能有一个系统组件允许访问。这会使得系统速度降低。允许系统的外部设备在一个不受控的方式下访问主内存也不是一个好主意。这会非常危险:一个恶意的设备会让系统非常不稳定。

外部设备由它们自己的内存空间。 CPU 可以访问这些空间,但是设备对于系统内存的访问受到严格的控制,必须通过 DMA ( Direct Memory Access 直接内存存取)通道。 ISA 设备可以访问两种地址空间: ISA I/O (输入 / 输出)和 ISA 内存。 PCI 由三中: PCI I/O 、 PCI 内存和 PCI 配置空间( configuration space )。 CPU 可以访问所有的地址空间其中 PCI I/O 和 PCI 内存地址空间由设备驱动程序使用而 PCI 配置空间由 Linux 和心中的 PCI 初始化代码使用。

Alpha AXP 处理器没有对于除了系统地址空间之外的地址空间的天生的访问模式。它需要使用支持芯片来访问象 PCI 配置空间这样的其他地址空间。它使用了一个地址空间的映射方案,从巨大的虚拟地址空间中偷出一部分映射到 PCI 地址空间。

6.2 PCI Configuration Headers ( PCI 配置头)

系统中的每一个 PCI 设备,包括 PCI-PCI 桥都由一个配置数据结构,位于 PCI 配置地址空间中。 PCI 配置头允许系统识别和控制设备。这个头位于 PCI 配置地址空间的确切位置依赖于设备使用的 PCI 拓扑。例如,插在 PC 主板一个 PCI 槽位的一个 PCI 显示卡配置头会在一个位置,而如果它被插到另一个 PCI 槽位则它的头会出现在 PCI 配置内存中的另一个位置。但是不管这些 PCI 设备和桥在什么位置,系统都可以发现并使用它们配置头中的状态和配置寄存器来配置它们。

通常,系统的设计使得每一个 PCI 槽位的 PCI 配置头都有一个和它在板上的槽位相关的偏移量。所以,举例来说,板上的第一个槽位的 PCI 配置可能位于偏移 0 而第二个槽位的在偏移 256 (所有的头都一样长度, 256 字节),依此类推。定义了系统相关的硬件机制使得 PCI 配置代码可以尝试检查一个给定的 PCI 总线上的所有可能的 PCI 配置头,试图读取头中的一个域(通常是 Vendor Identification 域)得到一些错误,从而知道那些设备存在而那些设备不存在。 PCI Local Bus 规范描述了一种可能的错误信息:试图读取一个空的 PCI 槽位的 Verdor Identification 和 Device Indentification 域时候返回 0xFFFFFFFF 。

图 6.2 显示了 256 字节的 PCI 配置头的布局。它包括以下域:

参见 include/linux/pci.h

Vendor Identification 唯一的数字,描述这个 PCI 设备的发明者。 Digital 的 PCI Vendor Identification 是 0x1011 而 Intel 是 0x8086 。

Device Identification 描述设备自身的唯一数字。例如 Digital 的 21141 快速以太网设备的设备标识符是 0x0009 。

Status 此域给除了设备的状态,它的位的含义由 PCI Local Bus 规范规定。

Command 系统通过写这个域控制这个设备。例如:允许设备访问 PCI I/O 内存。

Class Code 标识了设备的类型。对于每一种设备都有标准分类:显示、 SCSI 等等。对于 SCSI 的类型编码是 0x0100 。

Base Address Registers 这些寄存器用于确定和分配设备可以使用的 PCI I/O 和 PCI 内存的类型、大小和位置。

Interrupt Pin PCI 卡的物理管脚中的 4 个用于向 PCI 总线传递中断。标准中把它们标记为 A 、 B 、 C 和 D 。 Interrupt Pin 域描述了这个 PCI 设备使用那个管脚。通常对于一个设备来说这时硬件决定的。就是说每一次系统启动的时候,这个设备都使用同一个中断管脚。这些信息允许中断处理子系统管理这些设备的中断。

Interrupt Line PCI 配置头中的 Interrupt Line 域用于在 PCI 初始化代码、设备驱动程序和 Linux 的中断处理子系统之间传递中断控制。写在这里的数字对于设备驱动程序来讲是没有意义的,但是它可以让中断处理程序正确地把一个中断从 PCI 设备发送到 Linux 操作系统中正确的设备驱动程序的中断处理代码处。 Linux 如何处理中断参看第 7 章。

6.3 PCI I/O and PCI Memory Address ( PCI I/O 和 PCI 内存地址)

这两种地址空间用于设备和 CPU 上运行的 Linux 核心的它们的设备驱动程序通讯。例如: DECchip 21141 快速以太网设备把它的内部寄存器映射到了 PCI I/O 空间。然后它的 Linux 设备驱动程序通过读写这些寄存器来控制设备。显示驱动程序通常使用大量的 PCI 内存空间来放置显示信息。

直到 PCI 系统建立起来并使用 PCI 配置头中的 Command 域打开了设备对于这些地址空间的访问为止,设备都无法访问这些空间。应该注意的是只有 PCI 配置代码读写 PCI 配置地址, Linux 的设备驱动程序只是读写 PCI I/O 和 PCI 内存地址。

6.4 PCI-ISA Bridges ( PCI-ISA 桥)

这种桥把对于 PCI I/O 和 PCI 内存地址空间的访问转换成为 ISA I/O 和 ISA 内存访问,用来支持 ISA 设备。现在销售的多数系统都包括几个 ISA 总线插槽和几个 PCI 总线插槽。这种向后的兼容的需要会不断减少,将来会有只有 PCI 的系统。在早期的 Intel 8080 基础的 PC 时代,系统中的 ISA 设备的 ISA 地址空间( I/O 和内存)就被固定下来。甚至一个 S5000 Alpha AXP 基础的计算机系统的 ISA 软驱驱动器的 ISA I/O 地址也会和第一台 IBM PC 一样。 PCI 规范保留了 PCI I/O 和 PCI 内存的地址空间中的较低的区域保留给系统中的 ISA 外设并使用一个 PCI-ISA 桥把所有对于这些区域的 PCI 内存访问转换为 ISA 访问。

6.5 PCI-PCI Bridges ( PCI-PCI 桥)

PCI-PCI 桥是特殊的 PCI 设备,把系统中的 PCI 总线粘和在一起。简单系统中只有一个 PCI 总线,当时单个 PCI 总线可以支持的 PCI 设备的数量有电气限制。使用 PCI-PCI 桥增加更多的 PCI 总线允许系统支持更多的 PCI 设备。这对于高性能的服务器尤其重要。当然, Linux 完全支持使用 PCI-PCI 桥的使用。

6.5.1 PCI-PCI Bridges: PCI I/O and PCI Memory Windows

PCI-PCI 桥只向下游传递对于 PCI I/O 和 PCI 内存读和写的一个子集。例如在图 6.1 中,只有读和写的地址属于 SCSI 或者以太网设备的时候 PCI-PCI 桥才会把读写的地址从 PCI 总线 0 传递到总线 1 ,其余的都被忽略。这种过滤阻止了不必要的地址信息遍历系统。为了达到这个目的, PCI-PCI 桥必须编程设置它们必须从主总线向次总线通过的 PCI I/O 和 PCI 内存地址空间访问的基础( base )和限制。一旦系统中的 PCI-PCI 桥设置好,只要 Linux 设备驱动程序只是通过这些窗口存取 PCI I/O 和 PCI 内存空间, PCI-PCI 桥是不可见的。这是个重要的特性,使得 Linux 的 PCI 设备驱动程序的作者的日子好过了。但是它也让 Linux 下的 PCI-PCI 桥在一定程度上需要技巧才能配置,我们不久就会看到。

6.5.2 PCI-PCI Bridges: PCI Configuration Cycles and PCI Bus Numbering ( PCI-PCI 桥: PCI 配置 cycle 和 PCI 总线编号)

既然 CPU 的 PCI 初始化代码可以定位不在主 PCI 总线上的设备,必须有一种机制使得桥可以决定是否把配置 cycle 从它的主接口传递到次接口上。一个 cycle 就是它显示在 PCI 总线上的地址。 PCI 规范定义了两种 PCI 地址配置格式:类型 0 和类型 1 ,分别在图 6.3 和图 6.4 中显示。类型 0 的 PCI 配置 cycle 不包含总线号,被这个 PCI 总线上的所有的 PCI 设备解释用于 PCI 地址配置。配置 cycle 的位 32 : 11 看作是设备选择域。设计系统的一个方法是让每一个位选择一个不同的设备。这种情况下为 11 可能选择槽位 0 的 PCI 设备,位 12 选择槽位 1 的 PCI 设备,依此类推。另一种方法是把设备的槽位号直接写到位 31 : 11 中。一个系统使用哪一种机制依赖于系统的 PCI 内存控制器。

类型 1 的 PCI 配置 cycle 包括一个 PCI 总线号,这种配置循环被除了 PCI-PCI 桥之外的所有 PCI 设备忽略。所有看到了类型 1 的 PCI 配置 cycle 的 PCI-PCI 桥都可以把这些信息向它们的下游传送。一个 PCI-PCI 桥是否忽略 PCI 配置循环或者向它的下游传递,依赖于这个桥是如何配置的。每一个 PCI-PCI 桥都有一个主总线接口号和一个次总线接口号。主总线接口离 CPU 最近而次总线接口是离 CPU 最远的。每一个 PCI-PCI 桥都还有一个附属总线编号,这是在第二个总线接口之外可以桥接的最大的 PCI 总线数目。或者说,附属总线编号是 PCI-PCI 桥下游的最大的 PCI 总线编号。当 PCI-PCI 桥看到一个类型 1 的 PCI 配置 cycle 的时候,它做以下事情:

如果指定的总线编号不在桥的次总线编号和总线的附属编号之间就忽略它。

如果指定的总线编号和桥的次总线编号符合就把它转变成为类型 0 的配置命令

如果指定的总线编号大于次要总线编号而小于或等于附属总线编号,就不改变地传递到次要总线接口上。

所以,如果我们希望寻址图 6.9 的拓扑中总线 3 上的设备 1 ,我们必须从 CPU 生成一个类型 1 的配置命令。桥 1 不改变地传递到总线 1 ,桥 2 忽略它但是桥 3 把它转换成一个类型 0 的配置命令,并把它发送到总线 3 ,使设备 1 响应它。

每一个独立的操作系统负责在 PCI 配置阶段分配总线编号,但是不管使用哪一种编码方案,对于系统中所有的 PCI-PCI 桥,以下陈述都必须是正确的:

所有位于一个 PCI-PCI 桥后面的 PCI 总线的编码都必须在次总线编号和附属总线编号之间(包含)

如果违背了这条规则,则 PCI-PCI 桥将无法正确地传递和转换类型 1 的 PCI 配置 cycle ,系统无法成功地找到并初始化系统中的 PCI 设备。为了完成编码方案, Linux 按照特定的顺序配置这些特殊设备。参看 6.6.2 节对于 Linux PCI 桥和总线编码方案的描述以及一个可以工作的例子。

6.6 Linux PCI Initialization ( Linux PCI 初始化过程)

Linux 中 PCI 初始化代码分为三个逻辑部分:

PCI Device Driver 这个伪设备驱动程序从总线 0 开始查找 PCI 系统,定位系统中所有的 PCI 设备和桥。它建立一链接的数据结构的列表,描述系统的拓扑。另外,它还为系统中所有的桥编码。

参见 drivers/pci/pci.c and include/linux/pci.h

PCI BIOS 这个软件层提供了 PCI BIOS ROM 规范中描述的服务。即使 Alpha AXP 没有 BIOS 服务,在 Linux 核心也有提供了相同的功能的等价代码。

参见 arch/*/kernel/bios32.c

PCI Fixup 系统相关的整理代码,整理和系统相关的在 PCI 初始化最后的内存疏松的情况。

参见 arch/*/kernel/bios32.c

6.6.1 Linux Kernel PCI Data Structures ( Linux 核心的 PCI 数据结构)

当 Linux 核心初始化 PCI 系统的时候它建立反映系统真实的 PCI 拓扑结构的数据结构。图 6.5 显示了数据结构之间的关系,它用来描述了图 6.1 中示例的 PCI 系统。

每一个 PCI 设备(包括 PCI-PCI 桥)都用一个 pci_dev 的数据结构描述。每一个 PCI 总线用一个 pci_bus 的数据结构描述。结果是一个 PCI 总线的树型结构,每一个总线上有粘附着一些子 PCI 设备。因为一个 PCI 总线只能通过 PCI-PCI 桥达到(除了主 PCI 总线,总线 0 ),每一个 pci_bus 都包括一个它要通过的 PCI 设备的指针(这个 PCI-PCI 桥)。这个 PCI 设备是这个 PCI 总线的父总线的一个子设备。

图 6.5 中没有显示的还有一个指向系统中所有的 PCI 设备的指针: pci_devices 。系统中所有的 PCI 设备的 pci_dev 的数据结构都排在这个队列中。 Linux 核心使用这个队列快速查找系统中所有的 PCI 设备。

6.6.2 The PCI Device Driver ( PCI 设备驱动程序)

PCI 设备驱动程序完全不是一个真正的设备驱动程序,只是系统初始化的时候操作系统调用的一个函数。 PCI 初始化代码必须扫描系统中所有的 PCI 总线,查找系统中所有的 PCI 设备(包括 PCI-PCI 桥接设备)。它使用 PCI BIOS 代码来查看它当前扫描的 PCI 总线上的每一个可能的槽位是否被占用。如果这个 PCI 槽位占用,它就建立一个描述这个设备的 pci_dev 数据结构,并把它链接到已知 PCI 设备的列表中(由 pci_deivices 指向)。

参见 drivers/pci/pci.c Scan_bus()

PCI 初始化代码从 PCI 总线 0 开始扫描。它试图读出每一个可能的 PCI 槽位中每一个可能的 PCI 设备的 Vendor Identification 和 Device Identification 域。当它找到了占用的槽位它就建立一个 pci_dev 数据结构来描述它。 PCI 初始化代码所建立的所有的 pci_dev 数据结构(包括所有的 PCI-PCI 桥)都链接到一个链接表: pci_devices 。

如果找到的设备是一个 PCI-PCI 桥,则建立一个 pci_bus 的数据结构,并链接到 pci_root 指向的由 pci_bus 和 pci_dev 数据结构组成的树上。 PCI 的初始代码可以判断 PCI 设备是否 PCI-PCI 桥,因为它的分类编码( class code )是 0x060400 。然后 Linux 核心配置它刚刚找到的 PCI-PCI 桥的另一端的 PCI 总线(下游)。如果找到更多的 PCI-PCI 桥,它们都一样被配置。这个过程成为深度( depthwize )算法:系统在宽度搜索之前先在深度展开。看图 6.1 , Linux 会首先配置 PCI 总线 1 和它的以太网和 SCSI 设备,然后配置 PCI 总线 0 上的显示设备。

在 Linux 向下游查找 PCI 总线的时候它必须配置介入的 PCI-PCI 桥的次总线和附属总线编号。这些在下面的 6.6.2 节详细描述:

Configuring PCI-PCI Bridges – Assigning PCI Bus Numbers (配置 PCI-PCI 桥 - 分配 PCI 总线编号)

对于传送通过它们进行的 PCI I/O 、 PCI 内存或者 PCI 配置地址空间的读写, PCI-PCI 桥必须直到以下:

Primary Bus Number 刚好在 PCI-PCI 桥上游的总线编号

Secondary Bus Number 刚好在 PCI-PCI 桥下游的总线编号

Subordinate Bus Number 从这个桥向下可以达到的所有总线中最高的总线编号。

PCI I/O and PCI Memory Windows 从这个 PCI-PCI 桥向下的所有的地址的 PCI I/O 地址空间和 PCI 内存空间的窗口的 base 和 size 。

问题是当你希望配置任何指定的 PCI-PCI 桥的时候你并不知道这个桥的附属总线数目。你不知道是否下游还有其他 PCI-PCI 桥。就算知道,你也不知道它们将会被分配什么编号。答案是使用一个深度递归算法( depthwise recursive algorithm )。在每一个总线上找到任何 PCI-PCI 桥的时候都就给它们分配编号。对于找到的每一个 PCI-PCI 桥,就给它的次总线分配编号,并给它分配临时的附属总线编号 0xFF ,并扫描它的下游所有的 PCI-PCI 桥并分配编号。这看起来相当复杂,但是下面的实际例子能使这个过程更清楚。

PCI-PCI Bridge Numbering : Step 1 参考图 6.6 中的拓扑,扫描找到的第一个桥是桥 1 ( Bridge1 )。桥 1 下游的 PCI 总线编号为 1 ,桥 1 分配一个次总线号 1 和一个临时的附属总线编号 0xFF 。这意味着指定 PCI 总线 1 或更高的所用的类型 1 的 PCI 配置地址会穿过桥 1 到达 PCI 总线 1 。如果它们的总线编号是 1 ,就转换成为类型 0 的配置 cycle ,否则对于其他的总线编号就不变。这也正是 Linux PCI 初始化代码需要做的,这样才能访问并扫描 PCI 总线 1 。

PCI-PCI Bridge Numbering : Step 2 Linux 使用深度算法,所以初始化代码开始扫描 PCI 总线 1 。这是它找到了 PCI-PCI 桥 2 ,桥 2 之外没有其他的 PCI-PCI 桥,所以它的附属总线编号成为 2 ,和它的次接口一样。图 6.7 显示了总线和 PCI-PCI 桥这时是如何编码的。

PCI-PCI Bridge Numbering : Step 3 PCI 初始化代码回来扫描 PCI 总线 1 ,找到了另一个 PCI-PCI 桥 3 。它的主总线接口赋值 1 而它的次总线接口是 3 ,它的附属总线编号是 0xFF 。图 6.8 显示了系统这时是如何配置的。带有总线编号 1 、 2 或 3 的类型 1 的 PCI 配置 cycle 现在可以正确地传送到适当的 PCI 总线。

6.6.3 PCI BIOS Functions ( PCI BIOS 函数)

PCI BIOS 函数是通用的跨平台的一系列标准例程。例如,它们对于 Intel 和 Alpha AXP 系统都是一样的。它们允许 CPU 控制对于所有 PCI 地址空间的访问。只有 Linux 核心和设备驱动程序需要使用它们。

参见 arch/*/kernel/bios32.c

6.6.4 PCI Fixup

Alpha AXP 系统上的 PCI 整理代码比 Intel (基本不做任何事情)要做更多的工作。对于 Intel 系统,启动时候运行的系统 BIOS ,已经完全配置了 PCI 系统。 Linux 不需要做更多的事情,只是映射 PCI 的配置。对于非 Intel 系统,需要做更多的配置:

参见 arch/kernel/bios32.c

对于每一个设备分配 PCI I/O 和 PCI 内存空间

对于系统重的每一个 PCI-PCI 桥必须配置 PCI I/O 和 PCI 内存地址窗口

对于设备产生 Interrupt Line 值,这些控制设备的中断处理

下面描述这些代码如何工作。

Finding Out How Much PCI I/O and PCI Memory Space a Device Needs

(找出一个设备需要多少 PCI I/O 和 PCI 内存空间)

查询找到的每一个 PCI 设备,找出它需要多少 PCI I/O 和内存地址空间。为此,把每一个 Base Address Register 都写成 1 然后读出来。设备会在不关心的地址位返回 1 ,有效地指定了需要的地址空间。

用两个基本的基础地址寄存器( Base Address Register ),第一种指示设备的寄存器以及 PCI I/O 和 PCI 内存空间必须在哪一个地址空间。这通过寄存器的 0 位表示。图 6.10 显示了 PCI 内存和 PCI I/O 的基础地址寄存器的两种形式。

为了找出每一个给定的基础地址寄存器需要多少地址空间,需要向所有的寄存器写并读出来。设备会把不关心的地址位设为 0 ,这样就有效地指明了需要的地址空间。这种设计暗示了使用的所有的地址空间都是 2 的指数,本质上是对齐的。

例如,在你初始化 DECChip 21142 PCI 快速以太网设备的时候,它告诉你在 PCI I/O 或 PCI 内存空间它需要 0x100 字节的地址。初始化代码为它分配空间。在它分配空间之后, 21142 的控制和状态寄存器就可以在这些地址见到。

Allocating PCI I/O and PCI Memory to PCI-PCI Bridges and Devices

(为 PCI-PCI 桥和设备分配 PCI I/O 和 PCI 内存)

象所有的内存一样, PCI I/O 和 PCI 内存空间是有限的,其中有一些相当紧缺。对于非 Intel 系统的 PCI 整理代码(和 Intel 系统的 BIOS 代码)必须有效地为每一个设备分配它需要的内存量。分配给一个设备的 PCI I/O 和 PCI 内存的分配必须自然对齐。例如,如果一个设备请求 PCI I/O 地址 0xB0 ,那么分配的地址就必须是 0xB0 的倍数。另外,分配给任何桥的 PCI I/O 和 PCI 内存地址的基础必须分别对齐 4K 和 1M 的边界。下游的设备给定的地址空间必须位于它所有的上游的 PCI-PCI 桥的内存范围中间。所以有效地分配地址空间是有比较困难的问题。

Linux 使用的算法依赖于用 PCI 设备驱动程序建立的总线 / 设备树所描述的每一个设备,它按照 PCI I/O 内存递增的顺序分配地址空间。又是使用递归算法,遍历 PCI 初始化代码所建立的 pci_bus 和 pci_dev 数据结构。 BIOS 整理代码从 PCI 总线的根( pci_root 所指)开始:

分别把当前的全局 PCI I/O 和内存的基础分别对齐在 4K 和 1M 的边界

对于当前总线上的每一个设备(按照需要的 PCI I/O 内存顺序排列)

- 分配它的 PCI I/O 和 / 或 PCI 内存

- 将全局的 PCI I/O 和内存的基础按照合适的量移动

- 允许设备使用给定的 PCI I/O 和 PCI 内存

分别为当前总线下游的所有总线分配空间,注意这会改变全局的 PCI I/O 和内存基础。

分别把当前的全局 PCI I/O 和内存的基础对齐在 4K 和 1M 的边界,同时指出当前的 PCI-PCI 桥所需要的 PCI I/O 和 PCI 内存的窗口的基础和大小

对于连接在当前总线上的 PCI-PCI 桥,设置它的 PCI-PCI I/O 和 PCI 内存地址和限制。

打开 PCI-PCI 桥上桥接 PCI I/O 和 PCI 内存访问的功能。这意味着如果任何在桥的主 PCI 总线上看到的 PCI I/O 和 PCI 内存地址如果位于它的 PCI I/O 和 PCI 内存地址窗口的话就会被桥接到它的次总线。

以图 6.1 的 PCI 系统作为 PCI 整理代码的例子:

Align the PCI base (初始的) PCI I/O 是 0x4000 , PCI 内存是 0x100000 。这样允许 PCI-ISA 桥把所有低于此的地址都转换到 ISA 地址。

The Video Device 请求 0x200000 的 PCI 内存,因为必须按照要求的大小对齐,所以我们从 PCI 内存 0x200000 开始分配, PCI 内存基础地址移到了 0x400000 ,而 PCI I/O 地址仍旧是 0x4000 。

The PCI-PCI Bridges 我们现在穿过 PCI-PCI 桥,在那里分配内存。注意因为我们不需要对其基础地址因为它们已经正确对齐了。

The Ethernet Device 它在 PCI I/O 和 PCI 内存空间都请求 0xB0 字节。它被分配在 PCI I/O 地址 0x4000 , PCI 内存 0x400000 。 PCI 内存的基础移到了 0x4000B0 , PCI I/O 的基础成为 0x40B0 。

The SCSI Device 它请求 0x1000 的 PCI 内存,所以它在对齐之后分配在 0x401000 。而 PCI I/O 的基础地址还是 0x40B0 , PCI 内存的基础移到了 0x402000 。

The PCI-PCI Bridge’s PCI I/O and Memory Windows 我们现在返回到桥,把它的 PCI I/O 窗口设置成为 0x4000 和 0x40B0 之间,它的 PCI 内存窗口在 0x400000 和 0x402000 之间。这意味着 PCI-PCI 桥会忽略对于显示设备的 PCI 内存访问,如果是对以太网或者 SCSI 设备的访问就可以通过。