PCI 配置 space 寄存器 - 写入值

PCI configuration space registers - write values

我正在为自制操作系统开发网络驱动程序 (RTL8139),但在将值写入 PCI 配置 space 寄存器时遇到问题。

我想更改中断线(偏移量 0x3c)的值以获取另一个 IRQ 号并启用命令寄存器(偏移量 0x04)的总线主控(设置位 2)。
当我读回这些值时,我发现我的值被正确写入。但是它没有使用新的 IRQ 号(在我的例子中是 6),而是使用旧值 (11)。
DMA 的总线主控器也不工作(我想发送的数据包具有正确的大小(由 IO 端口设置)但它们没有内容(所有值仅为 0,收发缓冲区在物理内存上并且具有非零值)。 在我仔细检查物理地址后,这总是按预期与我的代码一起工作。我需要这个来让网络控制器访问我的物理内存,其中 receive/transceive 的缓冲区是. (Bus mastering needed for RTL8139)

我是否必须执行其他操作来确认我对 PCI 设备所做的更改?

作为模拟器,我使用 qemu。

为了reading/writing我写了以下函数:

uint16_t pci_read_word(uint16_t bus, uint16_t slot, uint16_t func, uint16_t offset)
{
 uint64_t address;
 uint64_t lbus = (uint64_t)bus;
 uint64_t lslot = (uint64_t)slot;
 uint64_t lfunc = (uint64_t)func;
 uint16_t tmp = 0;
 address = (uint64_t)((lbus << 16) | (lslot << 11) |
                   (lfunc << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
 outportl (0xCF8, address);
 tmp = (uint16_t)((inportl (0xCFC) >> ((offset & 2) * 8)) & 0xffff);
 return (tmp);
}

uint16_t pci_write_word(uint16_t bus, uint16_t slot, uint16_t func, uint16_t offset, uint16_t data)
{
 uint64_t address;
 uint64_t lbus = (uint64_t)bus;
 uint64_t lslot = (uint64_t)slot;
 uint64_t lfunc = (uint64_t)func;
 uint32_t tmp = 0;
 address = (uint64_t)((lbus << 16) | (lslot << 11) |
                   (lfunc << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
 outportl (0xCF8, address);
 tmp = (inportl (0xCFC));
 tmp &= ~(0xFFFF << ((offset & 0x2)*8)); // reset the word at the offset
 tmp |= data << ((offset & 0x2)*8); // write the data at the offset
 outportl (0xCF8, address); // set address again just to be sure
 outportl(0xCFC,tmp); // write data
 return pci_read_word(bus,slot,func,offset); // read back data;
}

我希望有人能帮助我。

"interrupt line" 字段(在 PCI 配置中的偏移量 0x03C space)实际上什么都不做。

全文

PCI 卡在 PCI 插槽上最多可以使用 4 个 "PCI IRQs";并按顺序使用它们(所以如果你有十个 PCI 卡都有一个 IRQ,那么它们都会使用插槽中的第一个 PCI IRQ)。

有一个棘手的 "barber pole" 安排将 "PCI IRQ at the slot" 连接到 "PCI IRQ at the host controller",旨在减少 IRQ 共享。如果你有十个 PCI 卡都有一个 IRQ,那么它们都会使用插槽中的第一个 PCI IRQ,但是每个 PCI 插槽中的第一个 IRQ 将连接到主机控制器中的不同 PCI IRQ。所有这些都是硬连线的,无法通过软件更改。

使事情更加复杂;为了将 PCI IRQ(来自 PCI 主机控制器)连接到传统的 PIC 芯片,添加了一个特殊的 "PCI IRQ router"。理论上 "PCI IRQ router" 的配置可以通过软件更改(如果您可以找到描述 table 的文档,该文档描述了 "PCI IRQ router" 的位置、功能和限制)。

如果没有固件的帮助,OS 不可能确定 PCI 设备实际使用的是哪个 PIC 芯片输入。出于这个原因,固件会在引导期间计算出来,然后将 "PIC chip input number" 存储在某个地方以供 OS 查找。这就是 "interrupt line" 寄存器 - 它只是一个 8 位寄存器,可以存储任何你喜欢的东西(BIOS/firmware 用来存储 "PIC chip input number")。