// ide_bus.c - Bus-master IDE PCI/DMA initialisation. #include "ide.h" bool IdeDmaInit(void) { // ---- Find PCI IDE controller (class 0x01, subclass 0x01) -------------- uint8_t bus, dev, fn; if (!gHal->Pci.FindClass(0x01, 0x01, &bus, &dev, &fn)) { DbgPrint("IDE DMA: No PCI IDE controller found — using PIO\n"); return false; } // ---- Read BAR4 (Bus Master base, I/O space) --------------------------- // PCI config offset 0x20. Bit 0 must be 1 for I/O BAR. uint32_t bar4 = gHal->Pci.Read32(bus, dev, fn, 0x20); if (!(bar4 & 0x1)) { DbgPrint("IDE DMA: BAR4 is a memory BAR (0x%08x) — unexpected, using PIO\n", bar4); return false; } uint16_t bmBase = (uint16_t)(bar4 & 0xFFFC); if (bmBase == 0) { DbgPrint("IDE DMA: BAR4 is zero (firmware didn't assign it) — using PIO\n"); return false; } DbgPrint("IDE DMA: Bus Master base = 0x%04X (bus %u, dev %u, fn %u)\n", bmBase, bus, dev, fn); // ---- Enable Bus Mastering in PCI Command register (bit 2) ------------ uint32_t pciCmd = gHal->Pci.Read32(bus, dev, fn, 0x04); gHal->Pci.Write32(bus, dev, fn, 0x04, pciCmd | (1u << 2)); // ---- Allocate PRDT page (must be 32-bit physical, 4-byte aligned) ---- PHYSICAL_ADDRESS prdtPhys = MmAllocateContiguous(1); if (!prdtPhys) { DbgPrint("IDE DMA: Failed to allocate PRDT page\n"); return false; } if (prdtPhys >> 32) { // MmAllocateContiguous returned memory above 4 GB — BM IDE can't // address it. This shouldn't happen on normal x86 systems with early // boot allocations but we check defensively. DbgPrint("IDE DMA: PRDT page is above 4 GB (0x%016llX) — using PIO\n", (uint64_t)prdtPhys); return false; } void *prdtVirt = gHal->PhysToVirt(prdtPhys); DbgPrint("IDE DMA: PRDT at phys 0x%08x, virt 0x%016llX\n", (uint32_t)prdtPhys, prdtVirt); // ---- Create synchronisation objects ------------------------------------ SEMAPHORE *sem = NULL; if (!KSUCCESS(KeSemaphoreCreate(0, 0x7FFFFFFF, &sem))) { DbgPrint("IDE DMA: Failed to create IRP semaphore\n"); return false; } EVENT *dmaEv = NULL; if (!KSUCCESS(KeEventCreate(EventAutoReset, 0, &dmaEv))) { DbgPrint("IDE DMA: Failed to create DMA completion event\n"); return false; } // ---- Commit module state ----------------------------------------------- gBmBase = bmBase; gPrdtPhys = (uint32_t)prdtPhys; gPrdtVirt = (PRD *)prdtVirt; gIrpSemaphore = sem; gDmaEvent = dmaEv; KeInitializeDpc(&gDmaDpc, IdeDmaCompletionDpc, (void *)0); IrpQueueInit(&gIrpQueue, sem); // ---- Register IRQ 14 -------------------------------------------------- KeIrqRegister(IDE_IRQ_PRIMARY, IdeIsr); DbgPrint("IDE DMA: IRQ %u registered\n", IDE_IRQ_PRIMARY); // ---- Spawn worker thread ----------------------------------------------- KSTATUS st = KeCreateKernelThread((uintptr_t)IdeWorker, 0, PriorityHigh, (THREAD **)NULL); if (!KSUCCESS(st)) { DbgPrint("IDE DMA: Worker thread creation failed (0x%x)\n", st); // Not fatal for init — the PRDT/IRQ are live but async callers will // never get completions. Better to surface the failure clearly. return false; } DbgPrint("IDE DMA: Init complete\n"); return true; }