为什么手动 pci 地址 space 计算与 lspci 输出不同
Why the manual pci address space calculation is not same as lspci output
我有一个连接到 PCI 总线的以太网控制器。
$ lspci -s 02:01 -v
02:01.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01)
Subsystem: VMware PRO/1000 MT Single Port Adapter
Physical Slot: 33
Flags: bus master, 66MHz, medium devsel, latency 0, IRQ 19
Memory at fd5c0000 (64-bit, non-prefetchable) [size=128K]
Memory at fdff0000 (64-bit, non-prefetchable) [size=64K]
I/O ports at 2000 [size=64]
[virtual] Expansion ROM at fd500000 [disabled] [size=64K]
Capabilities: <access denied>
Kernel driver in use: e1000
Kernel modules: e1000
写了一个示例 linux 内核模块来计算内存映射的大小 I/O space。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#define PCI_INDEX_PORT 0xcf8
#define PCI_DATA_PORT 0xcfc
unsigned short read_pci_config_short(unsigned char bus, unsigned char slot, unsigned char func,
unsigned char offset)
{
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
return inw(0xcfc + (offset&2));
}
unsigned int read_pci_config(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset)
{
unsigned int v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inl(0xcfc);
return v;
}
void write_pci_config(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset,
unsigned int val)
{
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
outl(val, 0xcfc);
}
int __init hello_init(void)
{
u64 address;
//02:01.0 Ethernet controller
unsigned int bar_register = read_pci_config(2, 1, 0, 0x10);
pr_info("bar register:%02x\n", bar_register);
if (bar_register & 0x01) {
pr_info("I/O Mapped I/O\n");
address = (bar_register&0xfffffffc);
}
else {
pr_info("Memory Mapped I/O\n");
if (((bar_register >> 1) & 0x03) == 0x00) {
pr_info("32-BIT Address space\n");
address = (bar_register & 0xFFFFFFF0U);
}
else {
unsigned long int bar_register1 = read_pci_config(2, 1, 0, 0x14);
unsigned int size, size_bar_register1, size_bar_register2;
pr_info("64-bit address space\n");
pr_info("bar register2:%02lx\n", bar_register1);
address = (bar_register & 0xFFFFFFF0U) +
((bar_register1 & 0xFFFFFFFFU) << 32) ;
//write all ones
write_pci_config(2, 1, 0, 0x10, 0xfffffffffLU);
write_pci_config(2, 1, 0, 0x14, 0xfffffffffLU);
//read it
size_bar_register1 = read_pci_config(2, 1, 0, 0x10);
size_bar_register2 = read_pci_config(2, 1, 0, 0x14);
//restore the original value
write_pci_config(2, 1, 0, 0x10, bar_register);
write_pci_config(2, 1, 0, 0x14, bar_register1);
pr_info("size_bar_register1:%02x\n", size_bar_register1);
pr_info("size_bar_register2:%02x\n", size_bar_register2);
//Masking
pr_info("size_bar_register1:%02x\n", ~size_bar_register1 + 1);
pr_info("size_bar_register2:%02x\n", ~size_bar_register2 + 1);
}
}
pr_info("BAR Address Register:%02llx\n", address);
return 0;
}
void __exit hello_exit(void)
{
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
我得到 0x1fffc 作为输出而不是 0x20000。上面的代码有什么错误。
$ [29956.534327] bar register:fd5c0004
[29956.534328] Memory Mapped I/O
[29956.534339] 64-bit address space
[29956.534340] bar register2:00
[29956.536082] size_bar_register1:fffe0004
[29956.536083] size_bar_register2:ffffffff
[29956.536083] size_bar_register1:1fffc
[29956.536083] size_bar_register2:01
[29956.536083] BAR Address Register:fd5c0000
如果是内存映射IO区;在执行 ~size_bar_register1 + 1
部分之前,您需要清除最低的 4 位(用于可预取或不可预取标志、地址大小、类型等)。
例如:
size_bar_register1 = read_pci_config(2, 1, 0, 0x10); // 0xFFFE0004
size_bar_register1 &= ~15; // 0xFFFE0000
size_bar_register1 = ~size_bar_register2 + 1; // 0x00020000 = 128 KiB
我有一个连接到 PCI 总线的以太网控制器。
$ lspci -s 02:01 -v
02:01.0 Ethernet controller: Intel Corporation 82545EM Gigabit Ethernet Controller (Copper) (rev 01)
Subsystem: VMware PRO/1000 MT Single Port Adapter
Physical Slot: 33
Flags: bus master, 66MHz, medium devsel, latency 0, IRQ 19
Memory at fd5c0000 (64-bit, non-prefetchable) [size=128K]
Memory at fdff0000 (64-bit, non-prefetchable) [size=64K]
I/O ports at 2000 [size=64]
[virtual] Expansion ROM at fd500000 [disabled] [size=64K]
Capabilities: <access denied>
Kernel driver in use: e1000
Kernel modules: e1000
写了一个示例 linux 内核模块来计算内存映射的大小 I/O space。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#define PCI_INDEX_PORT 0xcf8
#define PCI_DATA_PORT 0xcfc
unsigned short read_pci_config_short(unsigned char bus, unsigned char slot, unsigned char func,
unsigned char offset)
{
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
return inw(0xcfc + (offset&2));
}
unsigned int read_pci_config(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset)
{
unsigned int v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inl(0xcfc);
return v;
}
void write_pci_config(unsigned char bus, unsigned char slot, unsigned char func, unsigned char offset,
unsigned int val)
{
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
outl(val, 0xcfc);
}
int __init hello_init(void)
{
u64 address;
//02:01.0 Ethernet controller
unsigned int bar_register = read_pci_config(2, 1, 0, 0x10);
pr_info("bar register:%02x\n", bar_register);
if (bar_register & 0x01) {
pr_info("I/O Mapped I/O\n");
address = (bar_register&0xfffffffc);
}
else {
pr_info("Memory Mapped I/O\n");
if (((bar_register >> 1) & 0x03) == 0x00) {
pr_info("32-BIT Address space\n");
address = (bar_register & 0xFFFFFFF0U);
}
else {
unsigned long int bar_register1 = read_pci_config(2, 1, 0, 0x14);
unsigned int size, size_bar_register1, size_bar_register2;
pr_info("64-bit address space\n");
pr_info("bar register2:%02lx\n", bar_register1);
address = (bar_register & 0xFFFFFFF0U) +
((bar_register1 & 0xFFFFFFFFU) << 32) ;
//write all ones
write_pci_config(2, 1, 0, 0x10, 0xfffffffffLU);
write_pci_config(2, 1, 0, 0x14, 0xfffffffffLU);
//read it
size_bar_register1 = read_pci_config(2, 1, 0, 0x10);
size_bar_register2 = read_pci_config(2, 1, 0, 0x14);
//restore the original value
write_pci_config(2, 1, 0, 0x10, bar_register);
write_pci_config(2, 1, 0, 0x14, bar_register1);
pr_info("size_bar_register1:%02x\n", size_bar_register1);
pr_info("size_bar_register2:%02x\n", size_bar_register2);
//Masking
pr_info("size_bar_register1:%02x\n", ~size_bar_register1 + 1);
pr_info("size_bar_register2:%02x\n", ~size_bar_register2 + 1);
}
}
pr_info("BAR Address Register:%02llx\n", address);
return 0;
}
void __exit hello_exit(void)
{
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
我得到 0x1fffc 作为输出而不是 0x20000。上面的代码有什么错误。
$ [29956.534327] bar register:fd5c0004
[29956.534328] Memory Mapped I/O
[29956.534339] 64-bit address space
[29956.534340] bar register2:00
[29956.536082] size_bar_register1:fffe0004
[29956.536083] size_bar_register2:ffffffff
[29956.536083] size_bar_register1:1fffc
[29956.536083] size_bar_register2:01
[29956.536083] BAR Address Register:fd5c0000
如果是内存映射IO区;在执行 ~size_bar_register1 + 1
部分之前,您需要清除最低的 4 位(用于可预取或不可预取标志、地址大小、类型等)。
例如:
size_bar_register1 = read_pci_config(2, 1, 0, 0x10); // 0xFFFE0004
size_bar_register1 &= ~15; // 0xFFFE0000
size_bar_register1 = ~size_bar_register2 + 1; // 0x00020000 = 128 KiB