我正在x86_64平台上编写一个玩具内核。我计划将我的内核升级为使用vga和其他设备。到目前为止,我只是用键盘和uart连接我的玩具内核。现在我认为它已经足够成熟,可以开始使用一些基本的设备。
但是,我发现很难在早期启动时找到关于PCI接口的文档。我正在寻找Linux PCI驱动程序的文档,但不是我真正需要的东西。我必须自己走。如果bios将pci空间映射到某个地方,我仍然感到困惑。
有人能给我点启发吗?或者给我点资料。
谢谢

最佳答案

您要做的第一件事是执行PCI枚举。基本上有两种方法来进行PCI枚举,要么基于端口,要么基于内存映射。基于端口的方式是较老的方式,而内存映射的pci配置空间是用pci express引入的。基于端口的方法仍然适用于较新的计算机,也适用于模拟器/虚拟机以实现向后兼容性。因此,我将在接下来的解释中使用这种方法。这也可以工作,因为它有点复杂,如果你了解如何做基于端口的枚举内存映射枚举是非常简单的。我要补充的是,一些不兼容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来读或写一个值,那么enable位基本上应该始终设置为1。
因此,假设您希望读取配置中第一个设备的第一个寄存器,您将执行以下操作:
outl(CONFIG_ADDRESS, 1 << 31);
uint32_t result = inl(CONFIG_DATA)

要确定每个寄存器是什么,可以查看下表:
所以在前面的例子中,第一个寄存器包含设备id和供应商id。
现在您知道了如何从PCI配置空间读取寄存器,您需要知道何时找到了设备。基本上,如果第一个寄存器是0xffffffff,则没有设备连接到该设备编号处的总线。暴力的方法是扫描所有的总线和设备,然后扫描功能,如果你找到一个设备。更复杂的方法是查看第一条总线上第一个设备的头类型,以确定需要检查哪些总线。您可以找到有关如何进行更复杂的枚举的更多信息。
要执行基于内存映射的方法,您需要找到here中指定的mcfg表(也包括在simplerACPI中)。这将为您提供可用于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 ExpressSFI的更多信息。
最后,虽然它是特定于设备的,但是您可以从PCI配置空间获得的最有用的信息通常是基址寄存器。这些通常是设备被内存映射到的物理地址(假设设备是内存映射的),或者是端口地址(假设设备是基于端口的)。当然,中断管脚和线路也很有用。

关于linux - 通过玩具内核访问pci空间,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26808895/

10-11 18:32