On virtualbox, I find the PCI capability structure for MSI, with a control word of 0x0170. This seems like an incorrect value, as the PCI spec says the following about MSI's control word:
- 15::09 reserved (always 0 on read)
- 8 Per-vector masking capable (RO)
- 7 64 bit address capable (RO)
- 6::4 Multiple message enable - indicates 2**n allocated vectors (RW, 0 on reset, 110 & 111 are reserved values)
- 3::1 Multiple message capable - indicates 2**n enabled vectors (R0)
- 0 MSI enable (RW, 0 after reset)
- current control value don't at all match post-reset (6::4 are 111 not 000) - this one actually seems conceptually fine, it could be set that way by UEFI firmware before my bootloader
- 111 in 6::4 is a reserved value and doesn't make sense
- even if 111 (which should signify 2**7 interrupts) were valid, 3::1 say the device only supports 2**0 (1) interrupt, so it's still invalid.
Does anyone have any ideas about what the mistake I'm making is?
You can see my PCI device class here, but here are the relevant bits:
Code: Select all
struct pci_cap_msi
{
pci_cap::type id;
uint8_t next;
uint16_t control;
} __attribute__ ((packed));
struct pci_cap_msi32
{
pci_cap::type id;
uint8_t next;
uint16_t control;
uint32_t address;
uint16_t data;
uint16_t reserved;
uint32_t mask;
uint32_t pending;
} __attribute__ ((packed));
struct pci_cap_msi64
{
pci_cap::type id;
uint8_t next;
uint16_t control;
uint64_t address;
uint16_t data;
uint16_t reserved;
uint32_t mask;
uint32_t pending;
} __attribute__ ((packed));
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
m_base(group.base_for(bus, device, func)),
m_msi(nullptr),
m_bus_addr(bus_addr(bus, device, func)),
m_irq(isr::isrIgnoreF)
{
// snipped saving out the vendor/device/class/etc fields into class members
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
*command |= 0x400; // Mask old INTx style interrupts
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %d.%d id %04x:%04x",
bus, device, func, m_class, m_subclass, m_vendor, m_device);
// m_base is a uint32_t* pointing at the start of the PCIe configuration space, so [13] is offset 0x34
// which is the capabilities pointer byte
uint8_t next = m_base[13] & 0xff;
while (next) {
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
next = cap->next;
log::debug(logs::device, " - found PCI cap type %02x", cap->id);
if (cap->id == pci_cap::type::msi) {
m_msi = cap;
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
mcap->control |= ~0x1; // Mask interrupts
log::debug(logs::device, " - MSI control %04x", mcap->control);
}
}
}
void
pci_device::write_msi_regs(addr_t address, uint16_t data)
{
kassert(m_msi, "Tried to write MSI for a device without that cap");
if (m_msi->id == pci_cap::type::msi) {
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
if (mcap->control & 0x0080) {
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
mcap64->address = address;
mcap64->data = data;
if (mcap64->control & 0x0100)
log::debug(logs::device, " - MSI mask %08x pending %08x", mcap64->mask, mcap64->pending);
} else {
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
mcap32->address = address;
mcap32->data = data;
if (mcap32->control & 0x0100)
log::debug(logs::device, " - MSI mask %08x pending %08x", mcap32->mask, mcap32->pending);
}
uint16_t control = mcap->control;
control &= 0xff8f; // We're allocating one vector, clear 6::4
control |= 0x0001; // Enable MSI
mcap->control = control;
} else {
kassert(0, "MIS-X is NYI");
}
}
Code: Select all
dev debug: Found MCFG entry: base dc000000 group 0 bus 0-63
dev debug: Probing PCI group at base ffffff80dc000000
dev info: Found PCIe device at 00:02:0 of type 3.0 id 80ee:beef
dev info: Found PCIe device at 00:03:0 of type 2.0 id 1022:2000
dev info: Found PCIe device at 00:04:0 of type 8.128 id 80ee:cafe
dev info: Found PCIe device at 00:07:0 of type 6.128 id 8086:7113
dev info: Found PCIe device at 00:31:0 of type 6.1 id 8086:27b9
dev info: Found PCIe device at 00:31:2 of type 1.6 id 8086:2829
dev debug: - found PCI cap type 05
dev debug: - MSI control 0170
dev debug: - found PCI cap type 01
dev debug: - found PCI cap type 12
dev debug: Found table BGRT
driv info: AHCI registering device 0:31:2:
driv debug: 1 ports
driv debug: 32 command slots
driv info: Found device type SATA at port 0
mem debug: Got request to offset map 4 pages
driv debug: Rebasing address for AHCI port 0 to ffffff8000050000 [4]
dev debug: Allocating IRQ 10 to AHCI Device.
dev debug: - MSI mask 00000000 pending 01000000
driv debug: Created command, slot 0, 1 PRD entries.
driv debug: Reading 1 sectors, starting from 1 (0x200)