如何访问 PCI 设备?

如何访问 PCI 设备?

我有两个与 PCI 总线相关的问题:

设备驱动程序如何访问 PCI 总线上的设备?写入为设备分配的内存区域是否足够(mov memory_space_allocated_for_dev, something如果设备是内存映射的,或者out io_space_allocated for_dev, something如果不是)(硬件是否会将访问内存位置的尝试转换为 PCI 总线上的一系列 PCI 命令等?)。

在所有 PCI 总线上设置好所有设备(即分配内存空间、枚举总线等)后,设备驱动程序不需要知道 PCI 总线是否存在,或者是否使用简单的解码器(如在旧计算机中)进行内存解码(即它们只是从某些内存位置写入和读取以访问设备),这样对吗?

答案1

内存映射 PCI(e) 设备将具有 BAR(基址寄存器),让主机知道应为设备分配多少内存。BIOS(以及之后的操作系统)将为目标设备分配请求的内存空间——而不是内存地址,物理位没有被分配。此外,这通常是物理内存地址,而不是虚拟内存地址。Linux 内核将使用 mmap() 等函数来仲裁对这些设备的访问,这些函数允许将物理内存映射到虚拟内存地址。

例如,假设您有一个 PCIe 设备,可控制 8 个 LED。作为设备创建者,我可以请求一个非常小的 1K 地址空间。BIOS 可能会给我 32 位系统上的物理地址 0xE000_0000 到 0xE000_0400(附注:您现在应该明白为什么 32 位系统和具有大 VRAM 的 GPU 不能很好地协同工作)。

现在,如果我用一个字节写入 0xF0 到内存位置 0xE000_0000,这将打开 LED 7 到 4,并使 3 到 0 熄灭。就是这样 - 这是简单的内存映射 I/O。在 Linux 上添加抽象层,您将利用其出色的 PCI 子系统并编写一个针对给定供应商 ID + 设备 ID 字符串加载的驱动程序。然后,您可以编写一个调用该内核驱动程序的用户空间应用程序,其中 mmap() 调用可以将内存映射到用户空间,从而允许您的用户空间应用程序对某个虚拟内存地址执行字节读取/写入,这些地址将被转换并最终进入其所属的物理内存空间。

x86 当然有 I/O 空间,行为也非常相似,除了内核中的低级之外,outb/outw/outl(及其输入同类)指令将用于从 I/O 空间写入/读取,而不是内存读取/写入指令。同样,用户空间应用程序应该通过 ioctls() 和映射内存段进行通信,因为内核负责安全/访问内存。

对于您的第二个问题,有点与上面的问题混合在一起,但 Linux 的现代 PCIe 驱动程序将依靠 PCI 子系统来完成许多低级内部管理工作。您主要定义您负责的供应商/设备 ID,然后编写一个 .probe() 函数来捕获您的 IRQ 并进行设备设置 - 您的内存交给您。

答案2

已经有一段时间了,但如果我没记错的话,您的内核驱动程序将在 /dev 中设置一个设备,该设备映射到 BIOS 或内核分配给 PCI 设备的内存地址。然后,具有适当权限的用户空间程序将打开、读取/写入/查找和关闭该 /dev/ 文件以与 PCI 设备交互。请参阅《Linux 设备驱动程序》第 12 章:https://lwn.net/Kernel/LDD3/

相关内容