// hal_pci.c - PCI configuration space via Mechanism 1 port I/O #include #include #include #include #define PCI_CONFIG_ADDR 0xCF8 #define PCI_CONFIG_DATA 0xCFC #define PCI_REG_COMMAND_STATUS 0x04u #define PCI_REG_CAP_PTR 0x34u #define PCI_STATUS_CAP_LIST (1u << 20) #define PCI_COMMAND_INTX_DISABLE (1u << 10) #define PCI_CAP_ID_MSI 0x05u #define PCI_CAP_ID_MSIX 0x11u #define PCI_MSI_CTRL_ENABLE (1u << 0) #define PCI_MSI_CTRL_MME_MASK (7u << 4) #define PCI_MSI_CTRL_64BIT (1u << 7) #define PCI_MSI_CTRL_PVM (1u << 8) #define PCI_MSI_ADDRESS_BASE 0xFEE00000u #define PCI_IRQ_VECTOR_BASE 0x20u static uint32_t MakeAddr(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg) { return 0x80000000UL | ((uint32_t)bus << 16) | ((uint32_t)(dev & 0x1F) << 11) | ((uint32_t)(fn & 0x07) << 8) | (reg & 0xFC); } static uint32_t PciRead32(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg) { __outdword(PCI_CONFIG_ADDR, MakeAddr(bus, dev, fn, reg)); return __indword(PCI_CONFIG_DATA); } static void PciWrite32(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg, uint32_t val) { __outdword(PCI_CONFIG_ADDR, MakeAddr(bus, dev, fn, reg)); __outdword(PCI_CONFIG_DATA, val); } static uint8_t PciRead8(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg) { uint32_t v = PciRead32(bus, dev, fn, reg); return (uint8_t)(v >> ((reg & 3u) * 8u)); } static uint16_t PciRead16(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg) { uint32_t v = PciRead32(bus, dev, fn, reg); return (uint16_t)(v >> ((reg & 2u) * 8u)); } static void PciWrite16(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg, uint16_t value) { uint8_t aligned = reg & ~3u; uint32_t shift = (reg & 2u) * 8u; uint32_t old = PciRead32(bus, dev, fn, aligned); old &= ~(0xFFFFu << shift); old |= ((uint32_t)value << shift); PciWrite32(bus, dev, fn, aligned, old); } static bool PciFindDevice(uint16_t vendor, uint16_t device, uint8_t* outBus, uint8_t* outDev, uint8_t* outFn) { for (int bus = 0; bus < 256; bus++) for (int dev = 0; dev < 32; dev++) for (int fn = 0; fn < 8; fn++) { uint32_t id = PciRead32((uint8_t)bus, (uint8_t)dev, (uint8_t)fn, 0); if ((id & 0xFFFF) == vendor && (id >> 16) == device) { *outBus = (uint8_t)bus; *outDev = (uint8_t)dev; *outFn = (uint8_t)fn; return true; } } return false; } static bool PciFindClass(uint8_t classCode, uint8_t subclass, uint8_t* outBus, uint8_t* outDev, uint8_t* outFn) { for (int bus = 0; bus < 256; bus++) for (int dev = 0; dev < 32; dev++) for (int fn = 0; fn < 8; fn++) { uint32_t id = PciRead32((uint8_t)bus,(uint8_t)dev,(uint8_t)fn,0); if ((id & 0xFFFF) == 0xFFFF) continue; // no device uint32_t cc = PciRead32((uint8_t)bus,(uint8_t)dev,(uint8_t)fn,8); if (((cc >> 24) & 0xFF) == classCode && ((cc >> 16) & 0xFF) == subclass) { *outBus = (uint8_t)bus; *outDev = (uint8_t)dev; *outFn = (uint8_t)fn; return true; } } return false; } static uint8_t PciGetIrqLine(uint8_t bus, uint8_t dev, uint8_t fn) { // PCI config register 0x3C: byte 0 = IRQ line, byte 1 = IRQ pin uint32_t val = PciRead32(bus, dev, fn, 0x3C); return (uint8_t)(val & 0xFF); // 0xFF = not connected } static uint8_t PciFindCapability(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t capId) { uint32_t status = PciRead32(bus, dev, fn, PCI_REG_COMMAND_STATUS); if ((status & PCI_STATUS_CAP_LIST) == 0) return 0; uint8_t cap = PciRead8(bus, dev, fn, PCI_REG_CAP_PTR) & 0xFCu; for (uint32_t guard = 0; cap && guard < 48u; guard++) { uint32_t hdr = PciRead32(bus, dev, fn, cap); uint8_t id = (uint8_t)(hdr & 0xFFu); uint8_t next = (uint8_t)((hdr >> 8) & 0xFCu); if (id == capId) return cap; cap = next; } return 0; } static KSTATUS PciEnableMsi(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t irqSlot) { if (irqSlot >= 24u) return STATUS_INVALID_PARAMETER; uint8_t cap = PciFindCapability(bus, dev, fn, PCI_CAP_ID_MSI); if (!cap) { HalDbgPrint("PCI %u:%u.%u: MSI capability not present\n", bus, dev, fn); return STATUS_NOT_SUPPORTED; } uint32_t bspLapic = HalGetBspLapicId(); if (!HalApicUsingX2Apic() && bspLapic >= 256u) return STATUS_NOT_SUPPORTED; if (HalApicUsingX2Apic() && bspLapic >= 256u) { // Extended x2APIC MSI destinations need interrupt-remapping awareness. // Keep the first MSI implementation xAPIC-compatible and explicit. HalDbgPrint("PCI %u:%u.%u: MSI x2APIC destination %u not supported yet\n", bus, dev, fn, bspLapic); return STATUS_NOT_SUPPORTED; } uint16_t control = PciRead16(bus, dev, fn, (uint8_t)(cap + 2u)); bool is64 = (control & PCI_MSI_CTRL_64BIT) != 0; bool pvm = (control & PCI_MSI_CTRL_PVM) != 0; uint8_t vector = (uint8_t)(PCI_IRQ_VECTOR_BASE + irqSlot); uint32_t address = PCI_MSI_ADDRESS_BASE | ((bspLapic & 0xFFu) << 12); uint16_t data = vector; PciWrite32(bus, dev, fn, (uint8_t)(cap + 4u), address); if (is64) { PciWrite32(bus, dev, fn, (uint8_t)(cap + 8u), 0); PciWrite16(bus, dev, fn, (uint8_t)(cap + 12u), data); if (pvm) PciWrite32(bus, dev, fn, (uint8_t)(cap + 16u), 0); } else { PciWrite16(bus, dev, fn, (uint8_t)(cap + 8u), data); if (pvm) PciWrite32(bus, dev, fn, (uint8_t)(cap + 12u), 0); } control &= (uint16_t)~PCI_MSI_CTRL_MME_MASK; // enable exactly one message control |= PCI_MSI_CTRL_ENABLE; PciWrite16(bus, dev, fn, (uint8_t)(cap + 2u), control); uint32_t cmd = PciRead32(bus, dev, fn, PCI_REG_COMMAND_STATUS); cmd |= PCI_COMMAND_INTX_DISABLE; PciWrite32(bus, dev, fn, PCI_REG_COMMAND_STATUS, cmd); HalDbgPrint("PCI %u:%u.%u: MSI enabled cap=0x%02x vector=0x%02x irqslot=%u dest=%u %s\n", bus, dev, fn, cap, vector, irqSlot, bspLapic, is64 ? "64-bit" : "32-bit"); return STATUS_SUCCESS; } void HalPciInitialize(HAL_PCI* out) { out->Read32 = PciRead32; out->Write32 = PciWrite32; out->FindDevice = PciFindDevice; out->FindClass = PciFindClass; out->GetIrqLine = PciGetIrqLine; out->EnableMsi = PciEnableMsi; }