add KePanic instead of splattering _disable __halt everywhere, implement a DMA + interrupts path to the IDE driver, move the dynamic arrays from kmain.c to ke_utils.h, remove weird unicode chars

This commit is contained in:
maksw committed 2026-05-03 22:45:31 +02:00
1 parent 83d48e09bb
commit 4b57738fe3
15 files changed
+673 -219

No files matched your search

+19 -19
View File
@@ -20,7 +20,7 @@
// returns STATUS_PENDING immediately. Multiple callers can have outstanding
// requests against different drives concurrently without stalling each other.
//
// NCQ extension (future work — see "NCQ TODO" comments):
// NCQ extension (future work - see "NCQ TODO" comments):
// To saturate modern SSDs, replace the serialised single-slot model with
// Native Command Queuing:
// • Allocate per-slot bounce buffers (NUM_NCQ_SLOTS × SLOT_BOUNCE_BYTES).
@@ -381,7 +381,7 @@ typedef struct {
volatile uint32_t lastIs;
// -----------------------------------------------------------------------
// IRP queue — async dispatch
// IRP queue - async dispatch
// -----------------------------------------------------------------------
// AhciWorker blocks here waiting for work. AhciSubmitIrp enqueues an IRP
// and signals the semaphore to wake the worker.
@@ -413,7 +413,7 @@ static KSTATUS ahci_map_mmio(uint64_t physBase) {
uintptr_t virt = (uintptr_t)g_hal->PhysToVirt(phys);
uint64_t flags = PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE;
KSTATUS st = MmMapPage(virt, phys, flags);
// Ignore STATUS_ALREADY_EXISTS — a prior mapping with different flags
// Ignore STATUS_ALREADY_EXISTS - a prior mapping with different flags
// is possible for the first page if the bootloader was overly generous.
// Use MmSetPageProtect to force NOCACHE in that case.
if (st == STATUS_ALREADY_EXISTS) {
@@ -535,7 +535,7 @@ static KSTATUS port_init_memory(AHCI_DRIVE *drv) {
}
// ---------------------------------------------------------------------------
// Issue a command — polling fallback (used during early init / IDENTIFY only,
// Issue a command - polling fallback (used during early init / IDENTIFY only,
// before the scheduler and interrupt handler are live).
// Returns STATUS_SUCCESS, STATUS_DEVICE_ERROR, or STATUS_TIMEOUT.
// ---------------------------------------------------------------------------
@@ -580,7 +580,7 @@ static KSTATUS port_issue_cmd_poll(AHCI_DRIVE *drv) {
}
// ---------------------------------------------------------------------------
// ISR — called from KeIrqDispatch on the AHCI PCI IRQ line.
// ISR - called from KeIrqDispatch on the AHCI PCI IRQ line.
// Walks all registered drives, checks if their port raised an interrupt,
// captures IS, clears it, and signals the per-drive completion event.
// ---------------------------------------------------------------------------
@@ -597,10 +597,10 @@ static void AhciIsr(void) {
uint32_t portBit = (1u << drv->portNo);
if (!(pending & portBit)) continue;
// Capture and clear port IS before signalling — the waiter reads
// Capture and clear port IS before signalling - the waiter reads
// lastIs to check for errors without racing the next command.
uint32_t portIs = drv->port->is;
drv->port->is = portIs; // W1C — write back to clear
drv->port->is = portIs; // W1C - write back to clear
drv->lastIs = portIs;
// Clear the HBA-level bit.
@@ -612,7 +612,7 @@ static void AhciIsr(void) {
}
// ---------------------------------------------------------------------------
// Issue a command — interrupt-driven (runtime path).
// Issue a command - interrupt-driven (runtime path).
// Caller has already built slot 0 via build_rw_cmd / port_identify.
// Blocks the current thread until the ISR signals completionEvent.
// ---------------------------------------------------------------------------
@@ -638,7 +638,7 @@ static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) {
port->ci = (1u << 0);
KeWaitForEvent(drv->completionEvent);
// The ISR has cleared IS — check for errors in the snapshot.
// The ISR has cleared IS - check for errors in the snapshot.
uint32_t is = drv->lastIs;
if (is & (PORT_IS_TFES | PORT_IS_HBFS | PORT_IS_HBDS | PORT_IS_IFS)) {
DbgPrint("AHCI: error IS=0x%x TFD=0x%x SERR=0x%x\n",
@@ -808,7 +808,7 @@ static KSTATUS do_write(AHCI_DRIVE *drv, uint64_t lba,
}
// ---------------------------------------------------------------------------
// HAL_DISK callbacks — context pointer replaces trampolines.
// HAL_DISK callbacks - context pointer replaces trampolines.
// Ctx is the AHCI_DRIVE* stored in DISK_DRIVER.Ctx; the dispatch layer in
// hal_disk.c passes it as the first argument to every call.
// ---------------------------------------------------------------------------
@@ -825,7 +825,7 @@ static KSTATUS AhciWrite(void *ctx, uint64_t lba, uint32_t count, const void *bu
// Async IRP dispatch
// ---------------------------------------------------------------------------
// AhciSubmitIrp — called by IoSubmitIrp().
// AhciSubmitIrp - called by IoSubmitIrp().
// Enqueues the IRP and wakes the per-drive worker thread.
// Returns STATUS_PENDING immediately; the caller waits on irp->CompletionEvent
// or irp->CompletionFn.
@@ -836,7 +836,7 @@ static KSTATUS AhciSubmitIrp(void *ctx, IRP *irp) {
return STATUS_PENDING;
}
// AhciWorker — kernel thread, one per drive.
// AhciWorker - kernel thread, one per drive.
// Dequeues IRPs and executes them one at a time using the existing
// single-slot interrupt-driven path. Completion fires the IRP's event
// and/or callback so the original caller can observe the result without
@@ -848,7 +848,7 @@ static KSTATUS AhciSubmitIrp(void *ctx, IRP *irp) {
// 3. Grab a free bit in drv->slotMask (under the slot spinlock).
// 4. Copy write data into slotBounce[slot], build the NCQ FIS (FPDMA),
// record slotIrp[slot] = irp, and issue via port->sact + port->ci.
// 5. Return immediately — the ISR drives all completions.
// 5. Return immediately - the ISR drives all completions.
static void AhciWorker(uintptr_t arg) {
AHCI_DRIVE *drv = (AHCI_DRIVE *)arg;
@@ -857,7 +857,7 @@ static void AhciWorker(uintptr_t arg) {
KeWaitSemaphore(drv->irpQueue.Semaphore);
IRP *irp = IrpQueueDequeue(&drv->irpQueue);
if (!irp) continue; // spurious wakeup — should not happen
if (!irp) KePanic("AhciWorker: semaphore fired but queue is empty - IRP accounting bug\n");
bool write = !!(irp->Flags & IRP_FLAG_WRITE);
KSTATUS st;
@@ -980,11 +980,11 @@ static KSTATUS port_comreset(AHCI_DRIVE *drv) {
}
// PORT_IE bits we care about:
// DHRS (bit 0) - D2H Register FIS received — normal completion
// DHRS (bit 0) - D2H Register FIS received - normal completion
// PSS (bit 1) - PIO Setup FIS received
// DSS (bit 2) - DMA Setup FIS received
// SDBS (bit 3) - Set Device Bits FIS received
// TFES (bit 30) - Task File Error Status — error
// TFES (bit 30) - Task File Error Status - error
// HBFS (bit 29) - Host Bus Fatal error
// HBDS (bit 28) - Host Bus Data error
// IFS (bit 27) - Interface Fatal error
@@ -1019,7 +1019,7 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
// -----------------------------------------------------------------------
g_irq = hal->Pci.GetIrqLine(pciBus, pciDev, pciFn);
if (g_irq == 0xFF) {
DbgPrint("AHCI: no PCI IRQ wired — cannot use interrupt-driven mode\n");
DbgPrint("AHCI: no PCI IRQ wired - cannot use interrupt-driven mode\n");
return STATUS_NOT_SUPPORTED;
}
DbgPrint("AHCI: PCI IRQ line = %u\n", (unsigned)g_irq);
@@ -1082,7 +1082,7 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
continue;
}
// Phase 3: COMRESET — forces device to re-send D2H FIS for valid SIG
// Phase 3: COMRESET - forces device to re-send D2H FIS for valid SIG
st = port_comreset(drv);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: port %u: COMRESET failed\n", portno);
@@ -1113,7 +1113,7 @@ KSTATUS AhciInitialize(const HAL_API *hal) {
}
IrpQueueInit(&drv->irpQueue, irpSem);
// Phase 6: IDENTIFY (polled — scheduler not yet running)
// Phase 6: IDENTIFY (polled - scheduler not yet running)
st = port_identify(drv);
if (!KSUCCESS(st)) {
DbgPrint("AHCI: port %u: IDENTIFY failed (0x%x)\n", portno, st);