Enterprise Just Builder

Solution for Enterprise Software Architecture

PCI

为 QEMU 虚拟设备编写 Linux PCI 设备驱动

本文将以 QEMU 的 ivshmem 虚拟设备作为硬件平台,逐步演示如何为其编写 Linux PCI 设备驱动。该驱动通过的测试环境为 QEMU 版本V1.0,主机操作系统 Ubuntu 12.04,客户机(VM)操作系统 Linux v3.x,此外它在 QEMU v1.0.1, v2.2.0 和 v2.7.0-rc4 中也表现良好。例子中所引用的头文件和其他源文件如 drivers/pci/pci.c 是以 Linux 内核源文件的根目录为搜索路径的。如 即意指 include/linux/pci.h。

背景
要特别强调的是虚拟平台不能替代实际的物理硬件。但在演示、培训、实验等场景,虚拟平台就特别适合:配置虚拟硬件则相当便捷,且可提供一个一致的可供使用的硬件平台。而且添加、扩展或者移除虚拟平台组件也十分方便。 1

本文选用的是 InterVM SHared MEMory (ivshmem) 这个 QEMU PCI 设备。选择该设备的原因有很多, 包括设备构造简单,可以在主机上使用代码和它进行交互和测试。主机 SHM 共享内存被映射为客户机的 MMIO 区域,通过模拟的物理设备(ivshmem 设备)的 MMIO 访问,客户机操作系统就可以访问主机上的 POSIX SHM 共享内存。此外 ivshmem 框架还允许其他启用 ivshmem 的客户机 (或主机上的程序) 通过 eventfd (2) 机制将 irq 发送给虚拟机。如图1-1 所示。

由于 ivshmem 不是默认启用的 QEMU PC 字符设备, 因此需要在 QEMU 启动命令行中指定相应的设备选项。要查看所支持的设备的列表, 请运行 (以 X64 为例):

$ qemu-system-x86_64 -device ?

注: 编写 PCI 设备驱动所用的 Linux API 通常可以支持 PCI 族,包括 PCI, PCI-X 和 PCI-E。ivshmem 设备模拟的是 PCI 设备。

>>> 阅读全文

 

, , ,

X86/x64 架构的系统地址表初始化(2): 基于 PCIe 的系统

如果读者对 PCI 总线协议还不太了解,建议您先阅读上一篇文章:《X86/x64 架构的系统地址表初始化(1): 基于 PCI 的系统》,以获得足够的背景知识方便理解本文。1

我们在第一篇文章中讨论了基于 PCI 的 X86/X64 系统的系统地址表的初始化。本文我们将关注更现代的系统,即基于 PCI Express 的 X86/X64 系统。后文 PCI Express 统一简称为 PCIe,以和 PCI Express 规格书保持一致。

我们将要研究基于 PCIe 的 X86/X64 系统的系统地址表初始化过程。和上一篇文章一样,重点在于理解 PCIe 总线协议的地址映射机制。要理解在 PCIe 系统上如何访问 PCI 扩展 ROM, 了解地址映射十分重要。

在物理层上 PCIe 完全不同于 PCI。但在逻辑层上,PCIe 只是 PCI 的扩展。事实上,在 PCIe 平台上可以引导专为 PCI 总线设计的操作系统,只要该 OS 支持 PCI 总线规范,就不会有问题。PCIe 是 PCI 扩展也意味着要理解 PCIe 协议,就要先熟悉 PCI 总线协议。这就是我建议您先阅读第一篇文章的原因。

约定
本文使用以下约定:

>>> 阅读全文

 

, , , , , ,

X86/X64 架构的系统地址表初始化 (1):PCI 总线系统

去年我在《信息安全杂志》上发表了“恶意的 PCI 扩展 ROM”一文,文中并未对 PCI 扩展 ROM 1的地址映射展开讨论,这篇文章2在此作出补充。系统程序员常对设备内存(如 PCI 设备内存)是如何映射到系统地址空间感到疑惑不已。本文详细论述了系统地址表的初始化过程,尤其是那些控制着设备内存映射到系统地址表的 PCI 芯片寄存器的初始化。注意:只有带内存的 PCI 设备才有地址映射的要求,比如显卡、带有板载缓冲区或支持 PCI 扩展 ROM 的网卡等。

由于 X86/X64 架构的总线协议必须保持向后兼容,因此其系统地址表十分复杂。系统所使用的总线协议规定了总线设备的内存到系统地址表的地址映射关系。因此,要了解系统地址表的初始化,就必须了解特定的总线协议的地址映射机制。本文重点关注基于 PCI 总线协议的系统。以当今的的标准来衡量,PCI 总线协议略显老旧了。但是,弄清楚它在软件/固件的最底层中所起到的作用仍非常重要。不了解 PCI 总线协议,就无法理解后续的 PCI Express (PCIe) 总线协议。而 PCIe 现在基本成为 X86/X64 系统的主流总线协议。 本系列文章的第 2 部分将讨论基于 PCIe 的系统。

约定
本文常常可见“内存”或“存储器”这个词,且含义不尽相同。为了避免困惑,本文约定如下:

  • “主存储器” 或者 “主存” 指的是安装在主板上的 RAM 模块。
  • “内存控制器” 指的是 CPU 或者芯片组中用以控制和存取 RAM 模块的部分
  • “可刷新存储器” 指的是主板上存储 BIOS/UEFI 的芯片或者存储 PCI 扩展 ROM 内容的芯片。
  • “内存范围” 或 “内存地址范围” 指的是设备在 CPU 内存空间所占用的范围,即从基地址(起始地址)到结束地址的范围(基地址+内存大小)
  • “内存空间”指的是 CPU 所能访问的内存地址集,即 CPU 所能寻址的内存。这里的内存包括 RAM、ROM 以及其它 CPU 所能寻址的存储器。
  • “PCI 扩展 ROM” 除非另有定义,否则指的是 PCI 设备上的 ROM 芯片。

启动过程概述
本节将详细解释系统启动过程,这有助于后续了解系统地址表及其它与总线协议相关的内容。在涉及这些内容之前,你需要对启动过程有清晰的认识。

X86/X64 的启动从执行平台固件(BIOS/UEFI)开始。执行平台固定比启动操作系统要早,甚至是在 “启动加载器” 加载并执行操作系统之前。平台固件的执行过程可以简单概括如下:

>>> 阅读全文

 

, ,

Linux 设备驱动(一) PCI 设备枚举

本文将讨论 Linux 内核如何初始化系统的 PCI 总线和 PCI 设备。所谓 PCI — Peripheral Component Interconnect 即外部设备互联,PCI 标准是描述如何将系统中的设备以一种结构化、可控制的方式连接在一起的规范,包括系统外部设备的连接方式以及符合标准的设备组件应具备的行为规范。PCI 总线作为处理器系统的局部总线,主要目的是为了连接外部设备。较之前的存在其他总线如 ISA、EISA 和 MCA 总线相比,PCI 具有许多突出的优点:

  • PCI 总线空间与处理器空间隔离。PCI 设备具有独立的地址空间,该空间与存储器地址空间通过 HOST 主桥隔离。
  • 可扩展性。在 PCI 总线中,HOST 主桥可以通过 PCI 桥(PCI Bridge)扩展出一系列 PCI 总线,形成以 HOST 主桥为根节点的 PCI 总线树。该总线树上最多能挂接 256 个 PCI 设备(包括 PCI 桥)。
  • 动态配置机制。每个 PCI 设备都有独立的配置空间,包含设备在 PCI 总线中使用的基地址。基地址由系统软件动态配置并保证唯一性,从而解决了地址冲突的问题,实现了“即插即用” 的功能。PCI 桥的配置空间中则含有其 PCI 子树所能使用的地址范围。
  • 总线带宽。与之前的局部总线相比,PCI 总线极大提高了数据传送带宽,如 32位/33MHz 的 PCI 总线可以提供 132MB/s 的峰值带宽,而 64位/66MHz 的 PCI 总线可提供的峰值带宽为 532MB/s。
  • 共享总线的仲裁机制。PCI 设备通过仲裁获得 PCI 总线的使用权后才能进行数据传送;在 PCI 总线上进行数据传送,并不需要处理器进行干预。
  • 中断机制。PCI 总线上的设备可以通过四根中断请求信号线 INTA~D# 向处理器提交中断请求。PCI 总线上的设备可以共享这些中断请求信号。

一、PCI 总线组成结构
PCI 总线作为处理器系统的局部总线,是处理器系统的一个组成部件。讲述 PCI 总线的组成结构不能离开处理器系统这个大环境。在一个处理器系统中,与 PCI 总线相关的模块如图1‑1所示。

在一个处理器系统中,与 PCI 总线相关的模块包括:HOST 主桥、PCI 总线、PCI 桥接器和 PCI 设备。对于一些简单的系统,可能不含有 PCI 桥接器,这时所有设备都连接在 HOST 主桥引出的 PCI 总线上;而有些处理器系统中可能有多个 HOST 主桥,比如图1‑1中的处理器系统中就含有 HOST 主桥 X 和 HOST 主桥 Y。

PCI 总线由 HOST 主桥和 PCI 桥接器引出,HOST 主桥与主内存控制器在同一级总线上,PCI 设备可以方便地通过 HOST 主桥访问主内存,即进行 DMA 操作。

值得注意的是,PCI 设备的 DMA 操作需要与处理器系统的 Cache 进行一致性操作,当 PCI 设备通过 HOST 主桥访问主内存时,Cache 一致性模块将进行地址监听,并根据监听结果改变 Cache 的状态。

>>> 阅读全文

 

, ,

FreeBSD 的 ACPI 实现

几乎所有现代计算机系统的硬件都允许对电源使用进行管理、对系统温度进行监测,并将之维持在适当水平。由此一定水平的电源消耗就能带来最佳的性能。这对于使用电池的移动平台尤为重要,因为不必要的电源消耗会减少电池续航时间,在结束工作前,你就不得不重新充电。节电对桌面系统也同样重要,在系统空闲时关闭显示器、磁盘驱动器等设备显著可以降低能耗。1

不幸的是,在传统 PC 中, 用于电源及散热配置管理的大多数软件都与 BIOS 紧密相关。而且,除非受管的设备所连接的是即插即用总线(如 PCI),否则操作系统就很难便捷地检测、配置或管理设备。例如 ISA PnP 机制适用于枚举外置 ISA 卡设备而不是板载设备。而 PnP BIOS 则适用于枚举板载设备,但也很难扩展成通用方法。另外 PnP BIOS 是 16 位的,所以操作系统必须通过 16 位虚拟环境才能调用 PnP BIOS 的函数。

在引入高级配置和电源管理接口 (ACPI — Advanced Configuration and Power Management Interface) 前, 高级电源管理 (APM) BIOS 被广泛用于电源管理。在 APM 中,大部分的电源管理控制逻辑都驻留在 APM BIOS 代码中。支持 APM 的操作系统通过固定的 BIOS API 与 APM BIOS 进行通信,这些 API 提供了 BIOS 功能的基本访问。支持 APM 的操作系统必须定期对 APM 进行轮询以处理 APM 相关事件。APM BIOS 还可使用特殊系统管理中断,该中断对操作系统是不可见的。APM 提供了四种状态:运行、暂停、休眠和软关闭。

有三大因素制约着 APM 的使用。首先,除非供应商提供特殊的程序,否则很多 APM 功能都要在操作系统加载前通过供应商专有的 BIOS 菜单进行配置,比如设置关闭显示器前的系统空闲时间。此外,实施 APM 电源管理配置的具体策略是由 BIOS 供应商指定的。例如,某 APM BIOS 的策略是在关闭显示器的同时也降低 CPU 时钟频率或关闭网卡等设备,如果不修改 BIOS ,你就无法修改这一策略。

其次,APM 是 BIOS 级别代码,运行在操作系统的范围外。这使得开发和调试 APM 代码极其困难。而用户也只能通过烧写 ROM 的方式来解决 APM 的错误。重刷 BIOS 相当危险,因为一旦发生错误,系统就再无法使用了。

>>> 阅读全文

 

, , , , ,