为什么 read_pci_config_16 函数在调用 inb/inw 时按位执行
Why is the read_pci_config_16 function performing bitwise and when calling inb/inw
要读取 PCI 配置 space,我需要将 BDF 和偏移量写入 0xCF8h,然后读取数据寄存器 0xCFCh
我正在研究 linux 内核源代码,其中使用了以下逻辑。
在文件 arch/x86/pci/early.c
u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset)
{
u32 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inl(0xcfc);
return v;
}
u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset)
{
u8 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inb(0xcfc + (offset&3));
return v;
}
u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset)
{
u16 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inw(0xcfc + (offset&2));
return v;
}
为什么在read_pci_config_8和read_pci_config_16
的情况下进行按位与运算
v = inw(0xcfc + (offset&2));
该代码被误导了。
您应该只从 IO 端口 0xCFC 读取和 32 位双字(芯片组可能会忽略任何不是从该 IO 端口读取或写入的 32 位 IO 端口);它们是 IO 端口而不是 RAM - 例如IO口0xCFD(或"IO port 0xCFC+1")是一个完全无关的IO口,与IO口0xCFC完全没有关系。
有关参考(以及显示如何正确阅读单词的示例代码),请参阅:https://wiki.osdev.org/PCI#Configuration_Space_Access_Mechanism_.231
注意:如果你仔细阅读了PCI本地总线spec/s;从技术上讲,芯片组还应该支持来自 IO 端口 0xCFC 和 IO 端口 0xCFD、0xCFE 和 0xCFF 的 reads/writes 字节和字;也许有一天有人会设法实现不需要很多页勘误表的芯片,并且假设芯片组实际上符合每个相关规范的每个微小细节的风险将低到足以证明"near zero benefits"的合理性Linux'方法。
将 I/O 端口 CFC 视为 32 位“window”,进入由端口 CF8 中的值寻址的 32 位 PCI 配置寄存器。 PCI 允许在此 window 中读取 1、2 或 4 个字节。如果寄存器偏移量的低两位不为0,则表示要读取的一个或多个字节不在window的基址,而是需要使用window内的偏移量来访问所需的字节。您询问的 AND 操作将读取的端口地址调整为 select 所需的字节。由于 16 位读取必须对齐到 2 字节的倍数,因此调整必须是 0 或 2。
要读取 PCI 配置 space,我需要将 BDF 和偏移量写入 0xCF8h,然后读取数据寄存器 0xCFCh
我正在研究 linux 内核源代码,其中使用了以下逻辑。
在文件 arch/x86/pci/early.c
u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset)
{
u32 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inl(0xcfc);
return v;
}
u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset)
{
u8 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inb(0xcfc + (offset&3));
return v;
}
u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset)
{
u16 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inw(0xcfc + (offset&2));
return v;
}
为什么在read_pci_config_8和read_pci_config_16
的情况下进行按位与运算v = inw(0xcfc + (offset&2));
该代码被误导了。
您应该只从 IO 端口 0xCFC 读取和 32 位双字(芯片组可能会忽略任何不是从该 IO 端口读取或写入的 32 位 IO 端口);它们是 IO 端口而不是 RAM - 例如IO口0xCFD(或"IO port 0xCFC+1")是一个完全无关的IO口,与IO口0xCFC完全没有关系。
有关参考(以及显示如何正确阅读单词的示例代码),请参阅:https://wiki.osdev.org/PCI#Configuration_Space_Access_Mechanism_.231
注意:如果你仔细阅读了PCI本地总线spec/s;从技术上讲,芯片组还应该支持来自 IO 端口 0xCFC 和 IO 端口 0xCFD、0xCFE 和 0xCFF 的 reads/writes 字节和字;也许有一天有人会设法实现不需要很多页勘误表的芯片,并且假设芯片组实际上符合每个相关规范的每个微小细节的风险将低到足以证明"near zero benefits"的合理性Linux'方法。
将 I/O 端口 CFC 视为 32 位“window”,进入由端口 CF8 中的值寻址的 32 位 PCI 配置寄存器。 PCI 允许在此 window 中读取 1、2 或 4 个字节。如果寄存器偏移量的低两位不为0,则表示要读取的一个或多个字节不在window的基址,而是需要使用window内的偏移量来访问所需的字节。您询问的 AND 操作将读取的端口地址调整为 select 所需的字节。由于 16 位读取必须对齐到 2 字节的倍数,因此调整必须是 0 或 2。