Files
nt-lite/code/kernel/hal/hal_pci.c
T

179 lines
6.6 KiB
C

// hal_pci.c - PCI configuration space via Mechanism 1 port I/O
#include <stdint.h>
#include <ntlite/ddi/hal_api.h>
#include <kernel/hal/hal_apic.h>
#include <kernel/hal/hal_debug.h>
#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;
}