// pci.c -- PCI configuration space access for DOS/DJGPP // // Implements PCI mechanism 1 (CONFIG_ADDRESS at 0xCF8, CONFIG_DATA // at 0xCFC). This is the standard PCI configuration access method // supported by all PCI-capable chipsets. // // How mechanism 1 works: // 1. Write a 32-bit address to port 0xCF8 with bit 31 set (enable), // bus/dev/func/register fields encoded in bits 23:0 // 2. Read or write the 32-bit data at port 0xCFC // 3. For sub-dword access (8/16-bit), read the full dword and // mask/shift, or write with a read-modify-write // // Detection: write 0x80000000 to 0xCF8 and read back. If the value // matches, mechanism 1 is present. This works because bit 31 is the // enable bit -- on non-PCI systems, port 0xCF8 is either absent // (reads back 0xFF) or belongs to a different device. #include "pci.h" #include // PCI configuration mechanism 1 I/O ports #define PCI_CONFIG_ADDR 0x0CF8 #define PCI_CONFIG_DATA 0x0CFC // ============================================================ // Prototypes // ============================================================ uint32_t pciBuildAddress(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); bool pciDetect(void); int32_t pciEnumerate(PciEnumCallbackT cb, void *userData); bool pciFindDevice(uint16_t vendorId, uint16_t deviceId, PciDeviceT *dev); bool pciFindDeviceList(const uint16_t *idPairs, PciDeviceT *dev, int32_t *matchIdx); uint8_t pciRead8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); uint16_t pciRead16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); uint32_t pciRead32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); void pciWrite8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint8_t val); void pciWrite16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint16_t val); void pciWrite32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint32_t val); // ============================================================ // pciBuildAddress // ============================================================ // // Constructs a PCI configuration space address for mechanism 1. // Format: [31]=enable, [23:16]=bus, [15:11]=device, [10:8]=function, // [7:2]=register (dword-aligned), [1:0]=0 uint32_t pciBuildAddress(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) { return 0x80000000 | ((uint32_t)bus << 16) | ((uint32_t)dev << 11) | ((uint32_t)func << 8) | ((uint32_t)reg & 0xFC); } // ============================================================ // pciDetect // ============================================================ // // Checks for PCI mechanism 1 by writing the enable bit to the // CONFIG_ADDRESS port and reading it back. Saves and restores // the original port value to avoid disturbing any in-progress // PCI transaction. bool pciDetect(void) { uint32_t saved = inportl(PCI_CONFIG_ADDR); outportl(PCI_CONFIG_ADDR, 0x80000000); uint32_t readBack = inportl(PCI_CONFIG_ADDR); outportl(PCI_CONFIG_ADDR, saved); return (readBack == 0x80000000); } // ============================================================ // pciEnumerate // ============================================================ // // Scans all bus/device/function combinations for present devices. // A device is present if its vendor ID is not 0xFFFF. Multi-function // devices are detected by checking bit 7 of the header type register // on function 0; single-function devices only probe function 0. int32_t pciEnumerate(PciEnumCallbackT cb, void *userData) { int32_t count = 0; for (int32_t bus = 0; bus < PCI_MAX_BUS; bus++) { for (int32_t dev = 0; dev < PCI_MAX_DEV; dev++) { uint16_t vendor0 = pciRead16(bus, dev, 0, PCI_VENDOR_ID); if (vendor0 == 0xFFFF) { continue; } // Check if multi-function device uint8_t headerType = pciRead8(bus, dev, 0, PCI_HEADER_TYPE); int32_t maxFunc = (headerType & 0x80) ? PCI_MAX_FUNC : 1; for (int32_t func = 0; func < maxFunc; func++) { uint16_t vendorId = pciRead16(bus, dev, func, PCI_VENDOR_ID); if (vendorId == 0xFFFF) { continue; } PciDeviceT device; device.bus = bus; device.dev = dev; device.func = func; device.vendorId = vendorId; device.deviceId = pciRead16(bus, dev, func, PCI_DEVICE_ID); device.revision = pciRead8(bus, dev, func, PCI_REVISION_ID); device.baseClass = pciRead8(bus, dev, func, PCI_BASE_CLASS); device.subClass = pciRead8(bus, dev, func, PCI_SUBCLASS); for (int32_t i = 0; i < 6; i++) { device.bar[i] = pciRead32(bus, dev, func, PCI_BAR0 + i * 4); } count++; if (cb && cb(&device, userData)) { return count; } } } } return count; } // ============================================================ // pciFindDevice // ============================================================ bool pciFindDevice(uint16_t vendorId, uint16_t deviceId, PciDeviceT *dev) { for (int32_t bus = 0; bus < PCI_MAX_BUS; bus++) { for (int32_t d = 0; d < PCI_MAX_DEV; d++) { uint16_t vendor0 = pciRead16(bus, d, 0, PCI_VENDOR_ID); if (vendor0 == 0xFFFF) { continue; } uint8_t headerType = pciRead8(bus, d, 0, PCI_HEADER_TYPE); int32_t maxFunc = (headerType & 0x80) ? PCI_MAX_FUNC : 1; for (int32_t func = 0; func < maxFunc; func++) { uint16_t vid = pciRead16(bus, d, func, PCI_VENDOR_ID); uint16_t did = pciRead16(bus, d, func, PCI_DEVICE_ID); if (vid == vendorId && did == deviceId) { dev->bus = bus; dev->dev = d; dev->func = func; dev->vendorId = vid; dev->deviceId = did; dev->revision = pciRead8(bus, d, func, PCI_REVISION_ID); dev->baseClass = pciRead8(bus, d, func, PCI_BASE_CLASS); dev->subClass = pciRead8(bus, d, func, PCI_SUBCLASS); for (int32_t i = 0; i < 6; i++) { dev->bar[i] = pciRead32(bus, d, func, PCI_BAR0 + i * 4); } return true; } } } } return false; } // ============================================================ // pciFindDeviceList // ============================================================ // // Searches for the first PCI device matching any vendor/device pair // in the given list. The list is an array of uint16_t pairs: // { vendor1, device1, vendor2, device2, ..., 0, 0 } // On match, fills dev and sets matchIdx to the pair index (0-based). bool pciFindDeviceList(const uint16_t *idPairs, PciDeviceT *dev, int32_t *matchIdx) { for (int32_t bus = 0; bus < PCI_MAX_BUS; bus++) { for (int32_t d = 0; d < PCI_MAX_DEV; d++) { uint16_t vendor0 = pciRead16(bus, d, 0, PCI_VENDOR_ID); if (vendor0 == 0xFFFF) { continue; } uint8_t headerType = pciRead8(bus, d, 0, PCI_HEADER_TYPE); int32_t maxFunc = (headerType & 0x80) ? PCI_MAX_FUNC : 1; for (int32_t func = 0; func < maxFunc; func++) { uint16_t vid = pciRead16(bus, d, func, PCI_VENDOR_ID); uint16_t did = pciRead16(bus, d, func, PCI_DEVICE_ID); if (vid == 0xFFFF) { continue; } for (int32_t idx = 0; idPairs[idx * 2] != 0; idx++) { if (vid == idPairs[idx * 2] && did == idPairs[idx * 2 + 1]) { dev->bus = bus; dev->dev = d; dev->func = func; dev->vendorId = vid; dev->deviceId = did; dev->revision = pciRead8(bus, d, func, PCI_REVISION_ID); dev->baseClass = pciRead8(bus, d, func, PCI_BASE_CLASS); dev->subClass = pciRead8(bus, d, func, PCI_SUBCLASS); for (int32_t i = 0; i < 6; i++) { dev->bar[i] = pciRead32(bus, d, func, PCI_BAR0 + i * 4); } if (matchIdx) { *matchIdx = idx; } return true; } } } } } return false; } // ============================================================ // pciRead8 // ============================================================ uint8_t pciRead8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) { outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg)); uint32_t dword = inportl(PCI_CONFIG_DATA); return (dword >> ((reg & 3) * 8)) & 0xFF; } // ============================================================ // pciRead16 // ============================================================ uint16_t pciRead16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) { outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg)); uint32_t dword = inportl(PCI_CONFIG_DATA); return (dword >> ((reg & 2) * 8)) & 0xFFFF; } // ============================================================ // pciRead32 // ============================================================ uint32_t pciRead32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) { outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg)); return inportl(PCI_CONFIG_DATA); } // ============================================================ // pciWrite8 // ============================================================ void pciWrite8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint8_t val) { outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg)); uint32_t dword = inportl(PCI_CONFIG_DATA); int32_t shift = (reg & 3) * 8; dword = (dword & ~(0xFF << shift)) | ((uint32_t)val << shift); outportl(PCI_CONFIG_DATA, dword); } // ============================================================ // pciWrite16 // ============================================================ void pciWrite16(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint16_t val) { outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg)); uint32_t dword = inportl(PCI_CONFIG_DATA); int32_t shift = (reg & 2) * 8; dword = (dword & ~(0xFFFF << shift)) | ((uint32_t)val << shift); outportl(PCI_CONFIG_DATA, dword); } // ============================================================ // pciWrite32 // ============================================================ void pciWrite32(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint32_t val) { outportl(PCI_CONFIG_ADDR, pciBuildAddress(bus, dev, func, reg)); outportl(PCI_CONFIG_DATA, val); }