88 lines
3.4 KiB
C
88 lines
3.4 KiB
C
// 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;
|
|
}
|