我正在x86_64平台上编写一个玩具内核。我计划提升我的内核以使用VGA和其他设备。直到这一点,我正在使用键盘和UART连接我的玩具内核。现在我认为它已经足够成熟,可以开始使用一些基本设备。
但是,我发现很难在早期启动时找到有关PCI接口的文档。我正在寻找有关Linux PCI驱动程序的文档,但不是我真正需要的东西。我必须自己走路。如果BIOS将PCI空间映射到某个地方,我仍然感到困惑。
有人可以对此有所了解或指出我的一些文件。
由于
答案 0 :(得分:0)
您要做的第一件事是执行PCI枚举。基本上有两种方法可以进行PCI枚举,基于端口或基于内存映射。基于端口的是旧方式,PCI Express引入了内存映射PCI配置空间。基于端口的方法仍适用于较新的计算机,也可与仿真器/虚拟机配合使用,以实现向后兼容。因此,我将在其余的解释中使用这种方法。这也有点复杂,如果您了解如何进行基于端口的枚举,则内存映射枚举非常简单。我应该补充说,一些非PC兼容的x86主板不支持基于端口的PCI枚举。
要通过端口访问PCI配置空间(以便执行枚举),您需要了解两个特定端口:
CONFIG_ADDRESS
- 端口0xCF8
CONFIG_DATA
- 端口0xCFC
基本上,您所做的是为CONFIG_ADDRESS
写一个地址,并使用CONFIG_DATA
读取或写入该地址的值。写入CONFIG_ADDRESS
的地址格式为:
31 30 - 24 23 - 16 15 - 11 10 - 8 7 - 2 1 - 0
Enable Bit Reserved Bus Number Device Number Function Number Register Number 00
假设您要使用CONFIG_DATA
读取或写入值,则启用位基本上应始终设置为1。
所以说你希望阅读配置中第一个设备的第一个寄存器,你会做类似的事情:
outl(CONFIG_ADDRESS, 1 << 31);
uint32_t result = inl(CONFIG_DATA)
要确定每个寄存器的内容,您可以查看此表:
因此,在前面的示例中,第一个寄存器包含设备ID和供应商ID。
现在您知道如何从PCI配置空间中读取您在找到设备时需要知道的寄存器。基本上,如果第一个寄存器是0xFFFFFFFF,那么没有设备连接到该设备号的总线。蛮力方式是扫描所有总线和设备,然后在找到设备时扫描功能。更复杂的方法将查看第一条总线上第一个设备的标头类型,以确定需要检查哪些总线。您可以找到有关如何执行更复杂枚举的更多信息here。
要执行基于内存映射的方法,您需要找到ACPI中指定的MCFG表(也包含在更简单的SFI中)。这将为您提供可用于PCI枚举的物理地址。所有内存地址都是该地址的偏移量。另外一个关键的区别是配置空间已经扩展,因此地址位字段与基于端口的枚举位字段不匹配。 PCI Express的地址使用以下格式:
31 - 28 27 - 20 19 - 15 14 - 12 11 - 8 7 - 2 1 - 0
ZEROS bus number device number function number extended register number register number offset
这是从MCFG表获得的物理寻址的偏移量。您可以找到有关PCI express here的更多信息。
最后,虽然它是特定于设备的,但您可以从PCI配置空间获得的最有用信息通常是基址寄存器。这些通常是设备内存映射的物理地址,假设它是内存映射的,或者是假设它是基于端口的端口地址。当然,中断引脚和线也很有用。