diff --git a/OVMF/OVMF_VARS_4M.fd b/OVMF/OVMF_VARS_4M.fd index f38688d..386e81a 100644 Binary files a/OVMF/OVMF_VARS_4M.fd and b/OVMF/OVMF_VARS_4M.fd differ diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index 9992ad6..c075991 100644 Binary files a/OVMF/OVMF_VARS_4M_d.fd and b/OVMF/OVMF_VARS_4M_d.fd differ diff --git a/build.bat b/build.bat index 54c3661..fcad067 100644 --- a/build.bat +++ b/build.bat @@ -57,7 +57,7 @@ echo Linking hal.dll... link %LFLAGS% /DLL /OUT:"bin\hal.dll" ^ /ENTRY:HalInitialize /IGNORE:4216 ^ /EXPORT:HalInitialize /EXPORT:HalRegisterScheduler /EXPORT:HalGetApi ^ - /EXPORT:IoRegisterDisk ^ + /EXPORT:IoRegisterDisk /EXPORT:HalDiskDispatch ^ obj\printf.obj obj\libc.obj obj\hal\*.obj || exit /b 1 :: ---- Compile kernel ---- @@ -72,7 +72,9 @@ link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:KernelMain /OUT:"bin\kernel.exe" ^ /PDB:"bin\kernel.pdb" /EXPORT:MmAllocatePool /EXPORT:MmFreePool /EXPORT:IoRegisterFilesystem ^ /EXPORT:KeSignalEvent /EXPORT:MmAllocateContiguous /EXPORT:ObDereferenceObject /EXPORT:KeEventCreate ^ /EXPORT:KeWaitForEvent /EXPORT:KeClearEvent /EXPORT:MmGetPhysicalAddress /EXPORT:DbgPrint /EXPORT:KeIrqDispatch ^ - /EXPORT:MmMapPage /EXPORT:MmSetPageProtect ^ + /EXPORT:MmMapPage /EXPORT:MmSetPageProtect /EXPORT:KeYield /EXPORT:KeIrqRegister ^ + /EXPORT:SemaphoreCreate /EXPORT:KeWaitSemaphore /EXPORT:KeSignalSemaphore /EXPORT:KeCreateKernelThread ^ + /EXPORT:IrpQueueInit /EXPORT:IrpQueueEnqueue /EXPORT:IrpQueueDequeue ^ bin\hal.lib obj\ini_parser.obj obj\printf.obj obj\libc.obj obj\kernel\*.obj || exit /b 1 :: ---- Compile syscall.dll ---- @@ -137,7 +139,7 @@ echo Assembling ESP... mkdir "%DEST%\drivers" 2>nul mkdir "%DEST%\EFI\BOOT" 2>nul copy /y "bin\bootx64.efi" "%DEST%\EFI\BOOT\" -copy /y "bin\kernel.exe" "%DEST%\" +copy /y "bin\kernel.exe" "%DEST%\colonel.exe" copy /y "bin\hal.dll" "%DEST%\" copy /y "bin\syscall.dll" "%DEST%\" copy /y "bin\win32.dll" "%DEST%\" diff --git a/code/boot/main.c b/code/boot/main.c index d1d9fe4..84d535c 100644 --- a/code/boot/main.c +++ b/code/boot/main.c @@ -15,12 +15,13 @@ #include #include +#include #include -int _fltused = 0; +#include +#include +#include -#include "../kernel/boot_info.h" -#include "../kernel/memory_map.h" -#include "ini_parser.h" +int _fltused = 0; #define KERNEL_VIRT_BASE 0xFFFF800000000000ULL #define PHYS_MAP_BASE 0xFFFF880000000000ULL @@ -557,7 +558,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable } MODULE_LOAD_INFO; MODULE_LOAD_INFO core[] = { - { L"kernel.exe", "kernel.exe", NULL, KERNEL_VIRT_BASE }, + { L"colonel.exe", "kernel.exe", NULL, KERNEL_VIRT_BASE }, { L"hal.dll", "hal.dll", NULL, 0 /* Calculated dynamically */ }, }; @@ -567,7 +568,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable UINT8* rawBuf = LoadFile(core[i].path, &rawSize); if (!rawBuf) { Print(L"MISSING: %s\n", core[i].path); - while(1); + while(true); return EFI_NOT_FOUND; } @@ -576,7 +577,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable EFI_STATUS status = LoadPeImage(rawBuf, virtCursor, &physBase); if (EFI_ERROR(status)) { Print(L"PE Load Error: %r\n", status); - while(1); + while(true); return status; } @@ -609,7 +610,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable if (EFI_ERROR(ResolveImports(core[0].physBase, core[1].physBase, core[1].virtBase))) { Print(L"Failed to resolve imports. System halted.\n"); - while(1); + while(true); } Print(L"5\n"); diff --git a/code/drivers/ahci/ahci.c b/code/drivers/ahci/ahci.c index 8526701..b618218 100644 --- a/code/drivers/ahci/ahci.c +++ b/code/drivers/ahci/ahci.c @@ -1,4 +1,4 @@ -// ahci.c - AHCI driver, polling mode. +// ahci.c - AHCI driver, interrupt-driven with per-drive async IRP queue. // Supports SATA drives only (no ATAPI). Registers each found drive with the // HAL disk subsystem via IoRegisterDisk(). // @@ -7,17 +7,51 @@ // hal->PhysToVirt(phys) converts to kernel virtual address. // DMA buffers are bounce-buffered (allocated via MmAllocateContiguous) to // avoid needing a VirtToPhys conversion for arbitrary kernel pointers. +// +// Async I/O model (IRP path): +// Each drive has a dedicated kernel worker thread (AhciWorker) that blocks +// on a per-drive counting semaphore. Callers submit IRPs via AhciSubmitIrp, +// which enqueues the IRP and signals the semaphore. The worker dequeues +// one IRP at a time, runs the synchronous hardware transfer (blocking the +// worker, NOT the submitting thread), then writes irp->Status and fires +// irp->CompletionEvent / irp->CompletionFn. +// +// This gives true async I/O from the caller's perspective: IoSubmitIrp() +// 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): +// 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). +// • Use ATA_CMD_READ_FPDMA_QUEUED / WRITE_FPDMA_QUEUED. +// • Encode the slot tag in FIS.countl bits 7:3. +// • Issue via port->sact (NCQ active) rather than port->ci. +// • In AhciIsr: completed slots = prev_sact ^ port->sact; look up +// slot_irp[slot] and fire each completion from within the ISR. +// • The worker becomes a pure dispatcher: grab a free slot, build the +// command, issue it, return immediately. No per-slot KeWaitForEvent. +// +// Interrupt model (unchanged): +// GHC.IE and per-port PORT_IE are enabled after each port is initialised. +// AhciIsr fires on the PCI IRQ line and signals the per-drive completionEvent. +// port_issue_cmd blocks the worker thread on KeWaitForEvent instead of +// spinning on CI. port_issue_cmd_poll is kept for early init (IDENTIFY) +// before the scheduler is live. +#include +#include +#include #include #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include -// --------------------------------------------------------------------------- -// FIS types // --------------------------------------------------------------------------- typedef enum { FIS_TYPE_REG_H2D = 0x27, @@ -322,11 +356,14 @@ typedef volatile struct { // --------------------------------------------------------------------------- static const HAL_API *g_hal; +static HBA_MEM *g_hba; // HBA MMIO base (set during init) +static uint8_t g_irq; // PCI IRQ line for this HBA // Per-drive context typedef struct { HBA_PORT *port; // MMIO port registers (virtual) uint64_t sectorCount; // Total LBA sectors on device + uint8_t portNo; // Physical port index (for ISR dispatch) // Structures block (command list + FIS + cmd tables) uint64_t structPhys; // Physical base @@ -335,6 +372,28 @@ typedef struct { // Bounce buffer for DMA data uint64_t bouncePhys; void *bounceVirt; + + // Signaled by the ISR when the port raises an interrupt. + // Auto-reset: one Wait consumes one completion. + EVENT *completionEvent; + + // Last IS snapshot captured by the ISR (for error checking post-wait). + volatile uint32_t lastIs; + + // ----------------------------------------------------------------------- + // IRP queue — async dispatch + // ----------------------------------------------------------------------- + // AhciWorker blocks here waiting for work. AhciSubmitIrp enqueues an IRP + // and signals the semaphore to wake the worker. + IRP_QUEUE irpQueue; + THREAD *workerThread; + + // NCQ TODO: for multi-slot NCQ, add: + // uint32_t slotMask; // bitmask of in-flight slots + // IRP *slotIrp[NUM_CMD_SLOTS]; // slot → pending IRP + // uint64_t slotBouncePhys[NUM_CMD_SLOTS]; + // void *slotBounceVirt[NUM_CMD_SLOTS]; + // volatile uint32_t prevSact; // previous port->sact snapshot } AHCI_DRIVE; static AHCI_DRIVE g_drives[MAX_AHCI_DRIVES]; @@ -476,11 +535,12 @@ static KSTATUS port_init_memory(AHCI_DRIVE *drv) { } // --------------------------------------------------------------------------- -// Issue a command (polling, always uses slot 0) -// Returns STATUS_SUCCESS or STATUS_DEVICE_ERROR / STATUS_TIMEOUT. +// 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. // --------------------------------------------------------------------------- -static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) { +static KSTATUS port_issue_cmd_poll(AHCI_DRIVE *drv) { HBA_PORT *port = drv->port; // Clear pending interrupts before issuing @@ -499,19 +559,17 @@ static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) { port->ci = (1u << 0); // Poll until slot 0 clears - for (;;) { + while (true) { if (!(port->ci & (1u << 0))) break; if (port->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", port->is, port->tfd, port->serr); - // Reset error bits port->serr = (uint32_t)-1; port->is = (uint32_t)-1; return STATUS_DEVICE_ERROR; } } - // One final error check after CI cleared if (port->is & PORT_IS_TFES) { DbgPrint("AHCI: TFES after completion TFD=0x%x\n", port->tfd); port->is = (uint32_t)-1; @@ -521,6 +579,77 @@ static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) { return STATUS_SUCCESS; } +// --------------------------------------------------------------------------- +// 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. +// --------------------------------------------------------------------------- + +static void AhciIsr(void) { + if (!g_hba) return; + + // GHC.IS tells us which ports are pending. + uint32_t pending = g_hba->is; + if (!pending) return; + + for (int i = 0; i < g_driveCount; i++) { + AHCI_DRIVE *drv = &g_drives[i]; + uint32_t portBit = (1u << drv->portNo); + if (!(pending & portBit)) continue; + + // 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->lastIs = portIs; + + // Clear the HBA-level bit. + g_hba->is = portBit; + + if (drv->completionEvent) + KeSignalEvent(drv->completionEvent); + } +} + +// --------------------------------------------------------------------------- +// 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. +// --------------------------------------------------------------------------- + +static KSTATUS port_issue_cmd(AHCI_DRIVE *drv) { + HBA_PORT *port = drv->port; + + // Clear stale IS and snapshot before issuing so we don't mis-read a + // leftover value from a prior command. + port->is = (uint32_t)-1; + drv->lastIs = 0; + + // Wait for port idle + int spin = 0; + while ((port->tfd & (ATA_SR_BUSY | ATA_SR_DRQ)) && spin < POLL_TIMEOUT) + spin++; + if (spin == POLL_TIMEOUT) { + DbgPrint("AHCI: port hung (pre-issue)\n"); + return STATUS_TIMEOUT; + } + + // Issue slot 0, then block on the event. + port->ci = (1u << 0); + KeWaitForEvent(drv->completionEvent); + + // 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", + is, port->tfd, port->serr); + port->serr = (uint32_t)-1; + return STATUS_DEVICE_ERROR; + } + + return STATUS_SUCCESS; +} + // --------------------------------------------------------------------------- // Build a standard DMA read or write command in slot 0 // --------------------------------------------------------------------------- @@ -606,7 +735,8 @@ static KSTATUS port_identify(AHCI_DRIVE *drv) { fis->command = ATA_CMD_IDENTIFY; fis->device = 0; - KSTATUS st = port_issue_cmd(drv); + // Poll: IDENTIFY runs before the scheduler and IRQ handler are live. + KSTATUS st = port_issue_cmd_poll(drv); if (!KSUCCESS(st)) return st; // Parse sector count from IDENTIFY data @@ -677,31 +807,71 @@ static KSTATUS do_write(AHCI_DRIVE *drv, uint64_t lba, return STATUS_SUCCESS; } -// Macro-generate per-slot trampoline pairs (read + write) -#define AHCI_TRAMPOLINE(N) \ - static KSTATUS AhciRead##N(uint64_t lba, uint32_t n, void *b) \ - { return do_read (&g_drives[N], lba, n, b); } \ - static KSTATUS AhciWrite##N(uint64_t lba, uint32_t n, const void *b) \ - { return do_write(&g_drives[N], lba, n, b); } +// --------------------------------------------------------------------------- +// 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. +// --------------------------------------------------------------------------- -AHCI_TRAMPOLINE(0) -AHCI_TRAMPOLINE(1) -AHCI_TRAMPOLINE(2) -AHCI_TRAMPOLINE(3) -AHCI_TRAMPOLINE(4) -AHCI_TRAMPOLINE(5) -AHCI_TRAMPOLINE(6) -AHCI_TRAMPOLINE(7) +static KSTATUS AhciRead(void *ctx, uint64_t lba, uint32_t count, void *buf) { + return do_read((AHCI_DRIVE *)ctx, lba, count, buf); +} -typedef KSTATUS (*RW_FN_R)(uint64_t, uint32_t, void*); -typedef KSTATUS (*RW_FN_W)(uint64_t, uint32_t, const void*); +static KSTATUS AhciWrite(void *ctx, uint64_t lba, uint32_t count, const void *buf) { + return do_write((AHCI_DRIVE *)ctx, lba, count, buf); +} -static const RW_FN_R kReadFns[MAX_AHCI_DRIVES] = { - AhciRead0, AhciRead1, AhciRead2, AhciRead3, - AhciRead4, AhciRead5, AhciRead6, AhciRead7 }; -static const RW_FN_W kWriteFns[MAX_AHCI_DRIVES] = { - AhciWrite0, AhciWrite1, AhciWrite2, AhciWrite3, - AhciWrite4, AhciWrite5, AhciWrite6, AhciWrite7 }; +// --------------------------------------------------------------------------- +// Async IRP dispatch +// --------------------------------------------------------------------------- + +// 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. +static KSTATUS AhciSubmitIrp(void *ctx, IRP *irp) { + AHCI_DRIVE *drv = (AHCI_DRIVE *)ctx; + irp->Status = STATUS_FAILURE; // sentinel: I/O has not completed yet + IrpQueueEnqueue(&drv->irpQueue, irp); + return STATUS_PENDING; +} + +// 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 +// ever having blocked in a driver call. +// +// NCQ TODO: replace the serialised loop with a multi-slot dispatcher: +// 1. KeWaitSemaphore() to wait for a free slot AND a pending IRP. +// 2. Pick the oldest IRP from the queue. +// 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. +static void AhciWorker(uintptr_t arg) { + AHCI_DRIVE *drv = (AHCI_DRIVE *)arg; + + while (true) { + // Block until AhciSubmitIrp signals a new IRP. + KeWaitSemaphore(drv->irpQueue.Semaphore); + + IRP *irp = IrpQueueDequeue(&drv->irpQueue); + if (!irp) continue; // spurious wakeup — should not happen + + bool write = !!(irp->Flags & IRP_FLAG_WRITE); + KSTATUS st; + if (write) + st = do_write(drv, irp->Lba, irp->SectorCount, irp->Buffer); + else + st = do_read (drv, irp->Lba, irp->SectorCount, irp->Buffer); + + // Publish result then fire completion hooks. + irp->Status = st; + if (irp->CompletionEvent) KeSignalEvent(irp->CompletionEvent); + if (irp->CompletionFn) irp->CompletionFn(irp, irp->CompletionCtx); + } +} // --------------------------------------------------------------------------- // AHCI controller initialisation @@ -734,10 +904,9 @@ static KSTATUS hba_reset(HBA_MEM *hba) { return STATUS_TIMEOUT; } - // Re-enable AHCI mode (reset cleared it) - hba->ghc |= GHC_AE; - - // Leave GHC.IE clear - we use polling only + // Re-enable AHCI mode (reset cleared it), then enable global interrupts. + // GHC.IE lets the HBA assert its PCI interrupt line when a port fires. + hba->ghc |= GHC_AE | GHC_IE; return STATUS_SUCCESS; } @@ -810,12 +979,25 @@ static KSTATUS port_comreset(AHCI_DRIVE *drv) { return STATUS_SUCCESS; } +// PORT_IE bits we care about: +// 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 +// HBFS (bit 29) - Host Bus Fatal error +// HBDS (bit 28) - Host Bus Data error +// IFS (bit 27) - Interface Fatal error +#define PORT_IE_NORMAL (0x0000000Fu) +#define PORT_IE_ERRORS (PORT_IS_TFES | PORT_IS_HBFS | PORT_IS_HBDS | PORT_IS_IFS) +#define PORT_IE_ALL (PORT_IE_NORMAL | PORT_IE_ERRORS) + KSTATUS AhciInitialize(const HAL_API *hal) { - g_hal = hal; + g_hal = hal; g_driveCount = 0; // ----------------------------------------------------------------------- - // 1. Locate AHCI controller via PCI class scan + // Locate AHCI controller via PCI class scan // ----------------------------------------------------------------------- uint8_t pciBus, pciDev, pciFn; if (!hal->Pci.FindClass(PCI_AHCI_CLASS, PCI_AHCI_SUBCLASS, @@ -826,54 +1008,60 @@ KSTATUS AhciInitialize(const HAL_API *hal) { DbgPrint("AHCI: controller at PCI %02x:%02x.%x\n", pciBus, pciDev, pciFn); // ----------------------------------------------------------------------- - // 2. Enable Bus Master + Memory Space in PCI command register + // Enable Bus Master + Memory Space in PCI command register // ----------------------------------------------------------------------- - uint32_t cmd = hal->Pci.ConfigRead32(pciBus, pciDev, pciFn, PCI_CMD_REG); - cmd |= (PCI_CMD_BUSMASTER | PCI_CMD_MEMSPACE); - hal->Pci.ConfigWrite32(pciBus, pciDev, pciFn, PCI_CMD_REG, cmd); + uint32_t cmd = hal->Pci.Read32(pciBus, pciDev, pciFn, PCI_CMD_REG); + cmd |= (PCI_CMD_BUSMASTER | PCI_CMD_MEMSPACE); cmd &= ~(1 << 10); cmd |= (1 << 2); + hal->Pci.Write32(pciBus, pciDev, pciFn, PCI_CMD_REG, cmd); // ----------------------------------------------------------------------- - // 3. Read ABAR (BAR5) and map as uncacheable - // The physical address is already identity-mapped via PhysToVirt. - // NOTE: real drivers should mark the mapping WC or UC; here we rely - // on the kernel not caching MMIO ranges (caller's responsibility). + // Read PCI IRQ line // ----------------------------------------------------------------------- - uint32_t bar5 = hal->Pci.ConfigRead32(pciBus, pciDev, pciFn, PCI_BAR5_REG); - uint64_t abarPhys = (uint64_t)(bar5 & ~0xFFFu); // strip flags - KSTATUS st = ahci_map_mmio(abarPhys); // establish PTEs + g_irq = hal->Pci.GetIrqLine(pciBus, pciDev, pciFn); + if (g_irq == 0xFF) { + 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); + + // ----------------------------------------------------------------------- + // Read ABAR (BAR5) and map as uncacheable + // ----------------------------------------------------------------------- + uint32_t bar5 = hal->Pci.Read32(pciBus, pciDev, pciFn, PCI_BAR5_REG); + uint64_t abarPhys = (uint64_t)(bar5 & ~0xFFFu); + KSTATUS st = ahci_map_mmio(abarPhys); if (!KSUCCESS(st)) return st; - HBA_MEM *hba = (HBA_MEM *)hal->PhysToVirt(abarPhys); - + g_hba = (HBA_MEM *)hal->PhysToVirt(abarPhys); DbgPrint("AHCI: ABAR phys=0x%llx version=0x%08x\n", - (unsigned long long)abarPhys, hba->vs); + (unsigned long long)abarPhys, g_hba->vs); // ----------------------------------------------------------------------- - // 4. HBA reset + BIOS handoff + // HBA reset + BIOS handoff (also enables GHC.IE) // ----------------------------------------------------------------------- - st = hba_reset(hba); + st = hba_reset(g_hba); if (!KSUCCESS(st)) return st; // ----------------------------------------------------------------------- - // 5. Log capability summary + // Log capability summary // ----------------------------------------------------------------------- - uint32_t cap = hba->cap; - uint32_t nPorts = (cap & 0x1F) + 1; // bits 4:0 + 1 - uint32_t nSlots = ((cap >> 8) & 0x1F) + 1; - bool s64a = !!(cap & (1u << 31)); + uint32_t cap = g_hba->cap; + uint32_t nPorts = (cap & 0x1F) + 1; + uint32_t nSlots = ((cap >> 8) & 0x1F) + 1; + bool s64a = !!(cap & (1u << 31)); DbgPrint("AHCI: cap=0x%08x ports=%u slots=%u 64bit=%d\n", cap, nPorts, nSlots, (int)s64a); // ----------------------------------------------------------------------- - // 6. Enumerate implemented ports + // Enumerate implemented ports // ----------------------------------------------------------------------- - uint32_t pi = hba->pi; + uint32_t pi = g_hba->pi; for (uint32_t portno = 0; portno < 32 && g_driveCount < MAX_AHCI_DRIVES; portno++) { if (!(pi & (1u << portno))) continue; - HBA_PORT *port = &hba->ports[portno]; + HBA_PORT *port = &g_hba->ports[portno]; - // Phase 1: check physical link only — sig is garbage until after COMRESET + // Phase 1: physical link check if (!port_link_up(port)) { DbgPrint("AHCI: port %u: no link (ssts=0x%08x)\n", portno, port->ssts); continue; @@ -884,31 +1072,48 @@ KSTATUS AhciInitialize(const HAL_API *hal) { int driveIdx = g_driveCount; AHCI_DRIVE *drv = &g_drives[driveIdx]; memset(drv, 0, sizeof(*drv)); - drv->port = port; + drv->port = port; + drv->portNo = (uint8_t)portno; - // Phase 2: set up command list + FIS buffer (enables FRE) + // Phase 2: allocate command list, FIS buffer, bounce buffer st = port_init_memory(drv); if (!KSUCCESS(st)) { DbgPrint("AHCI: port %u: memory init failed (0x%x)\n", portno, st); continue; } - // Phase 3: COMRESET to flush stale HBA state and force device to - // re-send D2H FIS so the SIG register gets a valid value + // 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); continue; } - // Phase 4: now sig is valid — filter to SATA disks only + // Phase 4: filter to SATA disks if (!port_is_sata_disk(port)) { DbgPrint("AHCI: port %u: not a SATA disk (sig=0x%08x), skipping\n", portno, port->sig); continue; } - // Phase 5: IDENTIFY to get sector count + // Phase 5: create per-drive completion event (auto-reset, initially clear) + st = KeEventCreate(EventAutoReset, 0, &drv->completionEvent); + if (!KSUCCESS(st)) { + DbgPrint("AHCI: port %u: event create failed (0x%x)\n", portno, st); + continue; + } + + // Phase 5b: create the IRP queue semaphore and initialise the queue. + // The semaphore starts at 0; each submitted IRP increments it by one. + SEMAPHORE *irpSem = (SEMAPHORE *)0; + st = SemaphoreCreate(0, 0x7FFFFFFF, &irpSem); + if (!KSUCCESS(st)) { + DbgPrint("AHCI: port %u: semaphore create failed (0x%x)\n", portno, st); + continue; + } + IrpQueueInit(&drv->irpQueue, irpSem); + + // 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); @@ -920,22 +1125,56 @@ KSTATUS AhciInitialize(const HAL_API *hal) { (unsigned long long)drv->sectorCount, (unsigned long long)(drv->sectorCount / 2048)); + // Phase 7: enable per-port interrupts. + // Clear any stale IS first so we don't get a spurious signal immediately. + port->is = (uint32_t)-1; + port->ie = PORT_IE_ALL; + + // Phase 8: register with disk subsystem using Ctx pointer DISK_DRIVER dd = { - .ReadSectors = kReadFns[driveIdx], - .WriteSectors = kWriteFns[driveIdx], + .Ctx = drv, + .ReadSectors = AhciRead, + .WriteSectors = AhciWrite, + .SubmitIrp = AhciSubmitIrp, // async IRP path .SectorCount = drv->sectorCount, }; st = IoRegisterDisk((uint8_t)driveIdx, &dd); if (KSUCCESS(st)) { DbgPrint("AHCI: drive %d registered\n", driveIdx); - g_driveCount++; // only increment on full success + g_driveCount++; } else { DbgPrint("AHCI: IoRegisterDisk(%d) failed (0x%x)\n", driveIdx, st); + continue; + } + + // Phase 9: spawn the per-drive IRP worker thread. + // The worker owns do_read / do_write exclusively once the scheduler + // is live; it is the only thread that may call port_issue_cmd(). + st = KeCreateKernelThread((uintptr_t)AhciWorker, (uintptr_t)drv, + PriorityHigh, &drv->workerThread); + if (!KSUCCESS(st)) { + DbgPrint("AHCI: drive %d: worker thread create failed (0x%x)\n", + driveIdx, st); + // Non-fatal: sync ReadSectors / WriteSectors still work via the + // IoSubmitIrpSync() fallback in hal_disk.c, but async callers + // will hang waiting on irp->CompletionEvent forever. } } - DbgPrint("AHCI: initialised %d drive(s)\n", g_driveCount); - return (g_driveCount > 0) ? STATUS_SUCCESS : STATUS_NOT_FOUND; + if (g_driveCount == 0) { + DbgPrint("AHCI: no drives found\n"); + return STATUS_NOT_FOUND; + } + + // ----------------------------------------------------------------------- + // Register the ISR. All ports on this HBA share one PCI IRQ line. + // Do this after all drives are initialised so the ISR sees valid state. + // ----------------------------------------------------------------------- + KeIrqRegister(g_irq, AhciIsr); + DbgPrint("AHCI: ISR registered on IRQ %u; initialised %d drive(s)\n", + (unsigned)g_irq, g_driveCount); + + return STATUS_SUCCESS; } KSTATUS DriverEntry(const HAL_API *hal) { diff --git a/code/drivers/fat32/fat32.c b/code/drivers/fat32/fat32.c index 9937a4c..58c7f22 100644 --- a/code/drivers/fat32/fat32.c +++ b/code/drivers/fat32/fat32.c @@ -1,14 +1,14 @@ // fat32.c - FAT32 filesystem driver // Registers itself via IoRegisterFilesystem after finding a FAT32 partition. // Supports read-only access; write support is not implemented. +#include +#include +#include #include #include #include #include #include -#include -#include -#include // --------------------------------------------------------------------------- // On-disk structures @@ -584,7 +584,7 @@ KSTATUS DriverEntry(const HAL_API* hal) { // Pass the new volParams structure KSTATUS status = IoRegisterFilesystem(gVol.DriveIndex, &gFs, &volParams); - DbgPrint("FAT32: [DEBUG] pIoRegisterFilesystem returned: %d\n", status); + DbgPrint("FAT32: [DEBUG] IoRegisterFilesystem returned: %d\n", status); DbgPrint("FAT32: Init complete.\n"); return STATUS_SUCCESS; diff --git a/code/drivers/ide/ide.c b/code/drivers/ide/ide.c index f2724b3..54d82a7 100644 --- a/code/drivers/ide/ide.c +++ b/code/drivers/ide/ide.c @@ -1,19 +1,18 @@ -// ide.c - PIO-mode IDE driver (legacy 28-bit LBA) -#include -#include -#include #include #include #include +#include +#include +#include +#include -// Primary channel I/O ports. #define IDE_DATA 0x1F0 #define IDE_ERR 0x1F1 #define IDE_SECCNT 0x1F2 #define IDE_LBA0 0x1F3 #define IDE_LBA1 0x1F4 #define IDE_LBA2 0x1F5 -#define IDE_DRIVE 0x1F6 // 0xE0=master 0xF0=slave +#define IDE_DRIVE 0x1F6 #define IDE_STATUS 0x1F7 #define IDE_CMD 0x1F7 #define IDE_ALT 0x3F6 @@ -23,69 +22,112 @@ #define IDE_ERR_F 0x01 #define IDE_DRQ 0x08 -#define CMD_READ 0x20 -#define CMD_WRITE 0x30 -#define CMD_IDENT 0xEC +// 48-bit LBA Commands +#define CMD_READ_EXT 0x24 +#define CMD_WRITE_EXT 0x34 +#define CMD_IDENT 0xEC static const HAL_API* gHal; static DISK_DRIVER gDrv; static uint64_t gSectorCount = 0; +static bool gSupportsLba48 = false; +// --------------------------------------------------------- +// Polling Upgrades: Yield instead of blocking the CPU +// --------------------------------------------------------- static void IdeWait(void) { - while (__inbyte(IDE_STATUS) & IDE_BUSY) _mm_pause(); + while (__inbyte(IDE_STATUS) & IDE_BUSY) { + KeYield(); // <-- Let other threads run while the disk thinks! + } } static KSTATUS IdeWaitData(void) { - // Read the Alternate Status port 4 times to waste 400ns - for (int i = 0; i < 4; i++) { - __inbyte(IDE_ALT); - } + for (int i = 0; i < 4; i++) __inbyte(IDE_ALT); - // Wait until the drive is not busy AND data is requested (DRQ) - while (1) { + while (true) { uint8_t st = __inbyte(IDE_STATUS); if (st & IDE_ERR_F) return STATUS_DEVICE_ERROR; if (!(st & IDE_BUSY) && (st & IDE_DRQ)) return STATUS_SUCCESS; - _mm_pause(); + KeYield(); // <-- Be a good citizen } } -static KSTATUS IdeRead(uint64_t lba, uint32_t count, void* buf) { +// --------------------------------------------------------- +// 48-Bit LBA Read/Write +// --------------------------------------------------------- +static KSTATUS IdeRead(void *ctx, uint64_t lba, uint32_t count, void* buf) { + (void)ctx; if (lba + count > gSectorCount) return STATUS_INVALID_PARAMETER; uint16_t* p = (uint16_t *)buf; + // For simplicity, handle one sector at a time in this loop for (uint32_t i = 0; i < count; i++, lba++) { - IdeWait(); // Wait for drive to be ready for commands - __outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF)); - __outbyte(IDE_SECCNT, 1); - __outbyte(IDE_LBA0, (uint8_t)lba); - __outbyte(IDE_LBA1, (uint8_t)(lba >> 8)); - __outbyte(IDE_LBA2, (uint8_t)(lba >> 16)); - __outbyte(IDE_CMD, CMD_READ); + IdeWait(); + + if (gSupportsLba48) { + // LBA48 requires writing high bytes first, then low bytes + __outbyte(IDE_DRIVE, 0x40); // 0x40 = LBA mode, drive 0 + __outbyte(IDE_SECCNT, 0); // Count High + __outbyte(IDE_LBA0, (lba >> 24) & 0xFF); // LBA 4 + __outbyte(IDE_LBA1, (lba >> 32) & 0xFF); // LBA 5 + __outbyte(IDE_LBA2, (lba >> 40) & 0xFF); // LBA 6 + + __outbyte(IDE_SECCNT, 1); // Count Low + __outbyte(IDE_LBA0, (uint8_t)lba); // LBA 1 + __outbyte(IDE_LBA1, (uint8_t)(lba >> 8)); // LBA 2 + __outbyte(IDE_LBA2, (uint8_t)(lba >> 16));// LBA 3 + + __outbyte(IDE_CMD, CMD_READ_EXT); + } else { + // Fallback to 28-bit + __outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF)); + __outbyte(IDE_SECCNT, 1); + __outbyte(IDE_LBA0, (uint8_t)lba); + __outbyte(IDE_LBA1, (uint8_t)(lba >> 8)); + __outbyte(IDE_LBA2, (uint8_t)(lba >> 16)); + __outbyte(IDE_CMD, 0x20); // CMD_READ (28-bit) + } - // Use the delay/DRQ wait if (IdeWaitData() != STATUS_SUCCESS) return STATUS_DEVICE_ERROR; - for (int w = 0; w < 256; w++) *p++ = __inword(IDE_DATA); } return STATUS_SUCCESS; } -static KSTATUS IdeWrite(uint64_t lba, uint32_t count, const void* buf) { +static KSTATUS IdeWrite(void *ctx, uint64_t lba, uint32_t count, const void* buf) { + (void)ctx; if (lba + count > gSectorCount) return STATUS_INVALID_PARAMETER; const uint16_t* p = (const uint16_t *)buf; for (uint32_t i = 0; i < count; i++, lba++) { IdeWait(); - __outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF)); - __outbyte(IDE_SECCNT, 1); - __outbyte(IDE_LBA0, (uint8_t)lba); - __outbyte(IDE_LBA1, (uint8_t)(lba >> 8)); - __outbyte(IDE_LBA2, (uint8_t)(lba >> 16)); - __outbyte(IDE_CMD, CMD_WRITE); + + if (gSupportsLba48) { + __outbyte(IDE_DRIVE, 0x40); + __outbyte(IDE_SECCNT, 0); + __outbyte(IDE_LBA0, (lba >> 24) & 0xFF); + __outbyte(IDE_LBA1, (lba >> 32) & 0xFF); + __outbyte(IDE_LBA2, (lba >> 40) & 0xFF); + + __outbyte(IDE_SECCNT, 1); + __outbyte(IDE_LBA0, (uint8_t)lba); + __outbyte(IDE_LBA1, (uint8_t)(lba >> 8)); + __outbyte(IDE_LBA2, (uint8_t)(lba >> 16)); + + __outbyte(IDE_CMD, CMD_WRITE_EXT); + } else { + __outbyte(IDE_DRIVE, 0xE0 | ((lba >> 24) & 0xF)); + __outbyte(IDE_SECCNT, 1); + __outbyte(IDE_LBA0, (uint8_t)lba); + __outbyte(IDE_LBA1, (uint8_t)(lba >> 8)); + __outbyte(IDE_LBA2, (uint8_t)(lba >> 16)); + __outbyte(IDE_CMD, 0x30); // CMD_WRITE (28-bit) + } + IdeWait(); for (int w = 0; w < 256; w++) __outword(IDE_DATA, *p++); - __outbyte(IDE_ALT, 0); // flush cache + __outbyte(IDE_ALT, 0); // flush cache + if (IdeWaitData() != STATUS_SUCCESS) return STATUS_DEVICE_ERROR; } return STATUS_SUCCESS; @@ -94,83 +136,67 @@ static KSTATUS IdeWrite(uint64_t lba, uint32_t count, const void* buf) { KSTATUS DriverEntry(const HAL_API* hal) { DbgPrint("IDE: Init\n"); gHal = hal; - uint8_t status; - int timeout; - // 1. Select Primary Master Drive (0xA0) + // Select Primary Master (Drive 0) __outbyte(IDE_DRIVE, 0xA0); - - // Waste 400ns to let the drive selection settle on the bus for(int i=0; i<4; i++) __inbyte(IDE_ALT); - // 2. Zero out the LBA and Sector Count registers __outbyte(IDE_SECCNT, 0); __outbyte(IDE_LBA0, 0); __outbyte(IDE_LBA1, 0); __outbyte(IDE_LBA2, 0); - // 3. Send the IDENTIFY command - DbgPrint("IDE: Sending CMD_IDENT (0xEC)...\n"); __outbyte(IDE_CMD, CMD_IDENT); - - // 4. Read status immediately. If it's 0, the drive doesn't exist at all. - status = __inbyte(IDE_STATUS); - // DbgPrint("IDE: Initial status after IDENTIFY: 0x%02X\n", status); - if (status == 0) { - DbgPrint("IDE ERROR: Drive does not exist (Status 0).\n"); + + if (__inbyte(IDE_STATUS) == 0) { + // QEMU usually falls in here because it uses AHCI by default, + // leaving the legacy IDE ports floating/unconnected. + DbgPrint("IDE ERROR: Drive does not exist on legacy port 0x1F0.\n"); return STATUS_NOT_FOUND; } - // 5. Wait for BSY (Busy) to clear, WITH A TIMEOUT - timeout = 100000; + // Wait for BSY to clear, yielding to scheduler + int timeout = 10000; while (__inbyte(IDE_STATUS) & IDE_BUSY) { - if (--timeout == 0) { - DbgPrint("IDE ERROR: Timeout waiting for BSY to clear!\n"); - return STATUS_DEVICE_ERROR; - } + if (--timeout == 0) return STATUS_DEVICE_ERROR; + KeYield(); } - // 6. Check for ATAPI (CD-ROM) or SATA signatures. - // If these registers aren't 0 after BSY clears, it's NOT a standard ATA disk! - uint8_t lba1 = __inbyte(IDE_LBA1); - uint8_t lba2 = __inbyte(IDE_LBA2); - if (lba1 != 0 || lba2 != 0) { - DbgPrint("IDE ERROR: Not an ATA drive! Signatures: LBA1=0x%02X, LBA2=0x%02X\n", lba1, lba2); - // (ATAPI CD-ROMs usually show LBA1=0x14, LBA2=0xEB) + if (__inbyte(IDE_LBA1) != 0 || __inbyte(IDE_LBA2) != 0) { + DbgPrint("IDE ERROR: Not a standard ATA drive (Possibly ATAPI).\n"); return STATUS_NOT_SUPPORTED; } - // 7. Wait for DRQ (Data Request) or ERR (Error) - timeout = 100000; - while (1) { - status = __inbyte(IDE_STATUS); - if (status & IDE_ERR_F) { - DbgPrint("IDE ERROR: Drive returned an error! Status: 0x%02X\n", status); - return STATUS_DEVICE_ERROR; - } - if (status & IDE_DRQ) { - break; // Data is ready! - } - if (--timeout == 0) { - DbgPrint("IDE ERROR: Timeout waiting for DRQ! Status: 0x%02X\n", status); - return STATUS_DEVICE_ERROR; - } + timeout = 10000; + while (true) { + uint8_t status = __inbyte(IDE_STATUS); + if (status & IDE_ERR_F) return STATUS_DEVICE_ERROR; + if (status & IDE_DRQ) break; + if (--timeout == 0) return STATUS_DEVICE_ERROR; + KeYield(); } - // 8. Success! Read the 256 words (512 bytes) of IDENTIFY data - // DbgPrint("IDE: DRQ set! Reading 256 words...\n"); uint16_t ident[256]; for (int i = 0; i < 256; i++) { ident[i] = __inword(IDE_DATA); } - // 9. Extract and print the sector count (Words 60 and 61 for 28-bit LBA) - gSectorCount = ((uint32_t)ident[61] << 16) | ident[60]; - DbgPrint("IDE: SUCCESS: Disk found! Sector count: %u\n", (uint32_t)gSectorCount); + // Word 83 Bit 10 determines LBA48 support + gSupportsLba48 = (ident[83] & (1 << 10)) != 0; + + if (gSupportsLba48) { + // Words 100-103 contain the 64-bit LBA sector count + gSectorCount = *(uint64_t*)&ident[100]; + DbgPrint("IDE: LBA48 Disk found! Sectors: %llu\n", gSectorCount); + } else { + // Fallback to 28-bit + gSectorCount = ((uint32_t)ident[61] << 16) | ident[60]; + DbgPrint("IDE: LBA28 Disk found! Sectors: %u\n", (uint32_t)gSectorCount); + } if (gSectorCount == 0) return STATUS_NOT_FOUND; - // Register driver globally (from the previous fix!) + gDrv.Ctx = NULL; gDrv.ReadSectors = IdeRead; gDrv.WriteSectors = IdeWrite; gDrv.SectorCount = gSectorCount; diff --git a/code/hal/hal_api.h b/code/hal/hal_api.h index 24792b8..2300cf0 100644 --- a/code/hal/hal_api.h +++ b/code/hal/hal_api.h @@ -3,7 +3,7 @@ #include #include #include -#include "ke_status.h" +#include typedef struct BOOT_INFO BOOT_INFO; @@ -21,6 +21,7 @@ typedef struct HAL_KEYBOARD { bool (*Poll)(void); uint8_t (*ReadScancode)(void); char (*ReadChar)(void); + uint32_t (*GetModifiers)(void); void (*SetCallback)(void (*cb)(uint8_t scancode)); void (*Isr)(void); uint8_t IrqNumber; @@ -43,14 +44,17 @@ typedef struct HAL_TIMER { } HAL_TIMER; typedef struct HAL_PCI { - uint32_t (*ConfigRead32)(uint8_t bus, uint8_t dev, + uint32_t (*Read32)(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg); - void (*ConfigWrite32)(uint8_t bus, uint8_t dev, + void (*Write32)(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg, uint32_t val); bool (*FindDevice)(uint16_t vendor, uint16_t device, uint8_t* bus, uint8_t* dev, uint8_t* fn); bool (*FindClass)(uint8_t classCode, uint8_t subclass, uint8_t* bus, uint8_t* dev, uint8_t* fn); + // Returns the IRQ line from PCI config space (register 0x3C, byte 0). + // Returns 0xFF if no interrupt is wired. + uint8_t (*GetIrqLine)(uint8_t bus, uint8_t dev, uint8_t fn); } HAL_PCI; typedef struct HAL_INTC { diff --git a/code/hal/hal_apic.c b/code/hal/hal_apic.c index 13403c9..ed3a9cd 100644 --- a/code/hal/hal_apic.c +++ b/code/hal/hal_apic.c @@ -1,6 +1,6 @@ // hal_apic.c - Local APIC + I/O APIC -#include "hal_apic.h" -#include "hal_api.h" +#include +#include #include extern HAL_API gApi; diff --git a/code/hal/hal_debug.h b/code/hal/hal_debug.h index b081c15..9eb8b7f 100644 --- a/code/hal/hal_debug.h +++ b/code/hal/hal_debug.h @@ -1,4 +1,4 @@ #pragma once // hal_debug.h - HAL debug output function -// Do not include outside HAL. + void HalDbgPrint(const char* fmt, ...); \ No newline at end of file diff --git a/code/hal/hal_disk.c b/code/hal/hal_disk.c index 72e857c..7e663ce 100644 --- a/code/hal/hal_disk.c +++ b/code/hal/hal_disk.c @@ -1,15 +1,26 @@ -// hal_disk.c - drive registry; drivers register themselves via IoRegisterDisk -#include "hal_api.h" -#include "io_driver.h" +// hal_disk.c - drive registry; drivers register themselves via IoRegisterDisk. +// +// This file lives in the HAL layer and must NOT reference any kernel sync +// objects (KeEventCreate, KeSignalEvent, KeWaitForEvent, ObDereferenceObject). +// All IRP event signalling is done in kernel/ke_irp.c, which sits above us +// in the dependency graph. +// +// HalDiskDispatch is the one bridge: it calls the driver's SubmitIrp (async) +// or runs the sync callbacks inline and writes irp->Status. It does not +// touch irp->CompletionEvent — that is ke_irp.c's responsibility. + #include #include +#include +#include +#include #define MAX_DRIVES 8 static DISK_DRIVER gDrives[MAX_DRIVES]; static bool gPresent[MAX_DRIVES]; -KSTATUS IoRegisterDisk(uint8_t index, const DISK_DRIVER* driver) { +KSTATUS IoRegisterDisk(uint8_t index, const DISK_DRIVER *driver) { if (index >= MAX_DRIVES || !driver) return STATUS_INVALID_PARAMETER; if (gPresent[index]) return STATUS_ALREADY_EXISTS; gDrives[index] = *driver; @@ -17,27 +28,66 @@ KSTATUS IoRegisterDisk(uint8_t index, const DISK_DRIVER* driver) { return STATUS_SUCCESS; } +// ------------------------------------------------------------------------- +// HalDiskDispatch — called exclusively by ke_irp.c::IoSubmitIrp() +// ------------------------------------------------------------------------- +// Separating dispatch here keeps hal_disk.c free of event primitives while +// still allowing ke_irp.c (kernel layer) to fire completion hooks after we +// return. +// +// Returns STATUS_PENDING — driver accepted the IRP asynchronously. +// ke_irp.c must NOT touch irp->CompletionEvent +// yet; the driver will fire it. +// Returns STATUS_SUCCESS — sync fallback ran; irp->Status is already +// written. ke_irp.c should fire completion hooks. +// Returns STATUS_NOT_FOUND — drive not registered. + +KSTATUS HalDiskDispatch(uint8_t drive, IRP *irp) { + if (drive >= MAX_DRIVES || !gPresent[drive]) return STATUS_NOT_FOUND; + DISK_DRIVER *drv = &gDrives[drive]; + + if (drv->SubmitIrp) + return drv->SubmitIrp(drv->Ctx, irp); // STATUS_PENDING + + // Sync fallback: execute inline, write result, let caller fire hooks. + bool write = !!(irp->Flags & IRP_FLAG_WRITE); + if (write) + irp->Status = drv->WriteSectors(drv->Ctx, irp->Lba, irp->SectorCount, irp->Buffer); + else + irp->Status = drv->ReadSectors (drv->Ctx, irp->Lba, irp->SectorCount, irp->Buffer); + + return STATUS_SUCCESS; // ran synchronously +} + +// ------------------------------------------------------------------------- +// Legacy HAL_DISK vtable — direct sync calls, no IRP involvement. +// Existing callers (FAT32, shell) use this path unchanged. +// ------------------------------------------------------------------------- + static KSTATUS DiskRead(uint8_t drive, uint64_t lba, - uint32_t count, void* buf) { + uint32_t count, void *buf) { if (drive >= MAX_DRIVES || !gPresent[drive]) return STATUS_NOT_FOUND; - return gDrives[drive].ReadSectors(lba, count, buf); + return gDrives[drive].ReadSectors(gDrives[drive].Ctx, lba, count, buf); } + static KSTATUS DiskWrite(uint8_t drive, uint64_t lba, - uint32_t count, const void* buf) { + uint32_t count, const void *buf) { if (drive >= MAX_DRIVES || !gPresent[drive]) return STATUS_NOT_FOUND; - return gDrives[drive].WriteSectors(lba, count, buf); + return gDrives[drive].WriteSectors(gDrives[drive].Ctx, lba, count, buf); } + static bool DiskPresent(uint8_t drive) { return drive < MAX_DRIVES && gPresent[drive]; } + static uint64_t DiskSectorCount(uint8_t drive) { if (drive >= MAX_DRIVES || !gPresent[drive]) return 0; return gDrives[drive].SectorCount; } -void HalDiskInitialize(HAL_DISK* out) { - out->ReadSectors = DiskRead; - out->WriteSectors = DiskWrite; - out->DrivePresent = DiskPresent; +void HalDiskInitialize(HAL_DISK *out) { + out->ReadSectors = DiskRead; + out->WriteSectors = DiskWrite; + out->DrivePresent = DiskPresent; out->DriveSectorCount = DiskSectorCount; } diff --git a/code/hal/hal_keyboard.c b/code/hal/hal_keyboard.c index 5f4bf59..8daa319 100644 --- a/code/hal/hal_keyboard.c +++ b/code/hal/hal_keyboard.c @@ -1,71 +1,298 @@ -// hal_keyboard.c - PS/2 keyboard, interrupt-driven, 256-entry ring buffer -#include "hal_api.h" -#include "hal_apic.h" -#include "hal_debug.h" +// hal_keyboard.c – PS/2 keyboard, interrupt-driven, 256-entry ring buffer +// +// Scan-code set 1 (XT-compatible), as output by the 8042's built-in +// translation layer. Supports make/break for all standard keys, Shift / +// Ctrl / Alt / CapsLock / NumLock modifiers, and the E0-prefixed extended +// key set (arrows, Home/End/PgUp/PgDn/Ins/Del, RCtrl, RAlt, numpad-Enter, +// numpad-divide). +// +// Threading model +// --------------- +// Single-producer (ISR on IRQ 1) / single-consumer ring buffer. All +// ISR-written state is declared volatile. On a uniprocessor, volatile +// plus the natural atomicity of aligned int reads/writes is sufficient. +// On SMP, add acquire/release memory barriers around gHead and gTail. +// +// Caller responsibilities +// ----------------------- +// 1. Register KbdIsr with your interrupt dispatcher for IRQ 1. +// 2. Send the APIC/PIC EOI *after* KbdIsr returns, in the surrounding +// wrapper – not inside KbdIsr itself. +// 3. Unmask IRQ 1 in the APIC or 8259 before expecting any interrupts. +// #include #include #include +#include +#include +#include -#define PS2_DATA 0x60 -#define PS2_STAT 0x64 -#define PS2_CMD 0x64 +// -- Port addresses ------------------------------------------------------------ +#define PS2_DATA 0x60u +#define PS2_STAT 0x64u +#define PS2_CMD 0x64u -#define KBD_BUF_SIZE 256 +// -- Ring buffer --------------------------------------------------------------- +// KBD_BUF_SIZE must be a power of two; KBD_BUF_MASK replaces % with &. +#define KBD_BUF_SIZE 256u +#define KBD_BUF_MASK (KBD_BUF_SIZE - 1u) -static uint8_t gBuf[KBD_BUF_SIZE]; -static volatile int gHead = 0, gTail = 0; -static void (*gCallback)(uint8_t) = (void *)0; +static uint8_t gBuf[KBD_BUF_SIZE]; +static volatile int gHead = 0; // consumer read pointer (updated by reader) +static volatile int gTail = 0; // producer write pointer (updated by ISR) -// Minimal Set-2 to ASCII translation (unshifted only). -static const char gScanToAscii[128] = { - 0,0,'1','2','3','4','5','6','7','8','9','0','-','=','\b', - '\t','q','w','e','r','t','y','u','i','o','p','[',']','\n', - 0,'a','s','d','f','g','h','j','k','l',';','\'','`', - 0,'\\','z','x','c','v','b','n','m',',','.','/',0,'*', - 0,' ',0,0,0,0,0,0,0,0,0,0,0,0,0, - '7','8','9','-','4','5','6','+','1','2','3','0','.',0,0,0, +// -- Scan-code set 1 constants ------------------------------------------------- +#define SC1_BREAK_BIT 0x80u // OR'd into every break (key-up) scan code +#define SC1_MAKE_MASK 0x7Fu // complement of SC1_BREAK_BIT; avoids ~uint cast +#define SC1_EXT_PREFIX 0xE0u // one-byte prefix for extended keys + +// -- Modifier flags ------------------------------------------------------------ +#define MOD_LSHIFT (1u << 0) +#define MOD_RSHIFT (1u << 1) +#define MOD_LCTRL (1u << 2) +#define MOD_RCTRL (1u << 3) +#define MOD_LALT (1u << 4) +#define MOD_RALT (1u << 5) +#define MOD_CAPS (1u << 6) // toggle, not a held key +#define MOD_NUMLOCK (1u << 7) // toggle, not a held key + +#define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT) +#define MOD_CTRL (MOD_LCTRL | MOD_RCTRL) +#define MOD_ALT (MOD_LALT | MOD_RALT) + +static volatile uint32_t gModifiers = 0; +static volatile bool gNextExtended = false; // previous byte was 0xE0 + +// -- Raw-scancode callback (invoked from ISR context) -------------------------- +// The pointer must be volatile so the compiler re-reads it on every ISR entry +// rather than caching the value seen at startup. +static void (* volatile gCallback)(uint8_t) = NULL; + +// -- ASCII translation tables – scan-code set 1, non-extended keys only -------- +// Index = 7-bit make code (SC1_BREAK_BIT cleared). 0 = no printable glyph. +// Numpad digits yield their digit characters; a NumLock-aware wrapper can +// substitute cursor-movement codes (e.g. HAL_KEY_HOME) when NumLock is clear. +// +// Layout verified against the IBM PC/AT scan-code set 1 reference. + +static const char gUnshifted[128] = { +/*00*/ 0, +/*01*/ 0, // ESC +/*02*/ '1','2','3','4','5','6','7','8','9','0','-','=', // 02–0D +/*0E*/ '\b', +/*0F*/ '\t', +/*10*/ 'q','w','e','r','t','y','u','i','o','p','[',']', // 10–1B +/*1C*/ '\n', +/*1D*/ 0, // Left Ctrl +/*1E*/ 'a','s','d','f','g','h','j','k','l',';','\'','`', // 1E–29 +/*2A*/ 0, // Left Shift +/*2B*/ '\\', +/*2C*/ 'z','x','c','v','b','n','m',',','.','/', // 2C–35 +/*36*/ 0, // Right Shift +/*37*/ '*', // Numpad * (also PrtSc on some keyboards) +/*38*/ 0, // Left Alt +/*39*/ ' ', +/*3A*/ 0, // Caps Lock +/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1–F10 (3B–44) +/*45*/ 0, // Num Lock +/*46*/ 0, // Scroll Lock +/*47*/ '7','8','9', // Numpad 7–9 +/*4A*/ '-', // Numpad – +/*4B*/ '4','5','6', // Numpad 4–6 +/*4E*/ '+', // Numpad + +/*4F*/ '1','2','3', // Numpad 1–3 +/*52*/ '0', // Numpad 0 +/*53*/ '.', // Numpad . +/*54*/ 0,0,0, +/*57*/ 0, // F11 +/*58*/ 0, // F12 +/*59*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 59–68 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 69–78 + 0,0,0,0,0,0,0, // 79–7F }; +static const char gShifted[128] = { +/*00*/ 0, +/*01*/ 0, // ESC +/*02*/ '!','@','#','$','%','^','&','*','(',')','_','+', // 02–0D +/*0E*/ '\b', +/*0F*/ '\t', +/*10*/ 'Q','W','E','R','T','Y','U','I','O','P','{','}', // 10–1B +/*1C*/ '\n', +/*1D*/ 0, // Left Ctrl +/*1E*/ 'A','S','D','F','G','H','J','K','L',':','"','~', // 1E–29 +/*2A*/ 0, // Left Shift +/*2B*/ '|', +/*2C*/ 'Z','X','C','V','B','N','M','<','>','?', // 2C–35 +/*36*/ 0, // Right Shift +/*37*/ '*', +/*38*/ 0, // Left Alt +/*39*/ ' ', +/*3A*/ 0, // Caps Lock +/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1–F10 +/*45*/ 0, +/*46*/ 0, +/*47*/ '7','8','9', +/*4A*/ '-', +/*4B*/ '4','5','6', +/*4E*/ '+', +/*4F*/ '1','2','3', +/*52*/ '0', +/*53*/ '.', +/*54*/ 0,0,0, +/*57*/ 0, // F11 +/*58*/ 0, // F12 +/*59*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0, +}; + +// -- Interrupt service routine -------------------------------------------------- +// Called with interrupts disabled (inside the hardware IRQ handler). +// The caller's wrapper must send the APIC/PIC EOI after this returns. static void KbdIsr(void) { uint8_t sc = __inbyte(PS2_DATA); - if (sc & 0x80) return; // key-up, ignore - int next = (gTail + 1) % KBD_BUF_SIZE; - if (next != gHead) { - gBuf[gTail] = sc; - gTail = next; + + // E0 prefix: the following byte is an extended make/break code. + if (sc == SC1_EXT_PREFIX) { + gNextExtended = true; + return; } - if (gCallback) gCallback(sc); + + bool extended = gNextExtended; + bool is_break = (sc & SC1_BREAK_BIT) != 0; + uint8_t make = sc & SC1_MAKE_MASK; + + gNextExtended = false; + + // -- Update modifier state ------------------------------------------------- + if (!extended) { + switch (make) { + case 0x2A: // Left Shift + gModifiers = is_break ? (gModifiers & ~MOD_LSHIFT) + : (gModifiers | MOD_LSHIFT); + return; + case 0x36: // Right Shift + gModifiers = is_break ? (gModifiers & ~MOD_RSHIFT) + : (gModifiers | MOD_RSHIFT); + return; + case 0x1D: // Left Ctrl + gModifiers = is_break ? (gModifiers & ~MOD_LCTRL) + : (gModifiers | MOD_LCTRL); + return; + case 0x38: // Left Alt + gModifiers = is_break ? (gModifiers & ~MOD_LALT) + : (gModifiers | MOD_LALT); + return; + case 0x3A: // Caps Lock – toggle on make only + if (!is_break) gModifiers ^= MOD_CAPS; + return; + case 0x45: // Num Lock – toggle on make only + if (!is_break) gModifiers ^= MOD_NUMLOCK; + return; + default: + break; + } + } else { + // Extended make codes that map to modifier keys. + switch (make) { + case 0x1D: // Right Ctrl (E0 1D) + gModifiers = is_break ? (gModifiers & ~MOD_RCTRL) + : (gModifiers | MOD_RCTRL); + return; + case 0x38: // Right Alt / AltGr (E0 38) + gModifiers = is_break ? (gModifiers & ~MOD_RALT) + : (gModifiers | MOD_RALT); + return; + default: + break; + } + // All other extended keys (arrows, Home/End, etc.) fall through + // and are enqueued as make codes for consumer-side interpretation. + } + + // -- Discard break codes for non-modifier keys ----------------------------- + if (is_break) return; + + // -- Enqueue make code; silently drop if buffer is full -------------------- + int next = (gTail + 1) & (int)KBD_BUF_MASK; + if (next != gHead) { + gBuf[gTail] = make; + gTail = next; + } + + // -- Notify optional callback with the raw make code ----------------------- + // Take a local snapshot so the compiler cannot reorder the NULL check and + // the call across a volatile barrier, and to avoid double-fetching. + void (*cb)(uint8_t) = (void (*)(uint8_t))gCallback; + if (cb) cb(make); } +// -- Consumer API -------------------------------------------------------------- + static bool KbdPoll(void) { return gHead != gTail; } +// Blocking read. Spins with PAUSE until a make code is available. +// On a preemptive kernel, replace the _mm_pause() loop with a +// condition variable or sti/hlt to avoid wasting CPU cycles. static uint8_t KbdReadScancode(void) { - while (gHead == gTail) { _mm_pause(); } + while (gHead == gTail) { + _mm_pause(); + } uint8_t sc = gBuf[gHead]; - gHead = (gHead + 1) % KBD_BUF_SIZE; + gHead = (gHead + 1) & (int)KBD_BUF_MASK; return sc; } +// Returns the ASCII character for the next keypress, applying the current +// Shift and CapsLock state. Returns 0 for keys with no ASCII mapping +// (function keys, arrows, unmodified Ctrl/Alt keys, etc.). +// +// CapsLock inverts shift only for alphabetic keys (a–z); digit and symbol +// keys are always governed solely by the physical Shift state. static char KbdReadChar(void) { - uint8_t sc = KbdReadScancode(); - if (sc < 128) return gScanToAscii[sc]; - return 0; + uint8_t make = KbdReadScancode(); + if (make >= 128) return 0; + + uint32_t mods = gModifiers; // snapshot; safe outside ISR + bool shifted = (mods & MOD_SHIFT) != 0; + + // CapsLock toggles the effective shift for alphabetic keys only. + if (mods & MOD_CAPS) { + char base = gUnshifted[make]; + if (base >= 'a' && base <= 'z') { + shifted = !shifted; + } + } + + return shifted ? gShifted[make] : gUnshifted[make]; } -static void KbdSetCallback(void (*cb)(uint8_t sc)) { +static void KbdSetCallback(void (*cb)(uint8_t)) { gCallback = cb; } +// Returns the current modifier bitmask (OR of MOD_* flags above). +// Snapshot is taken outside ISR and may lag by at most one interrupt. +static uint32_t KbdGetModifiers(void) { + return gModifiers; +} + +// -- Initializer --------------------------------------------------------------- void HalKeyboardInitialize(HAL_KEYBOARD *out) { - // Flush PS/2 output buffer - while (__inbyte(PS2_STAT) & 1) __inbyte(PS2_DATA); + // Drain any stale bytes sitting in the PS/2 output buffer before + // enabling interrupts, so the first ISR call sees a clean state. + while (__inbyte(PS2_STAT) & 1) { + __inbyte(PS2_DATA); + } out->Poll = KbdPoll; out->ReadScancode = KbdReadScancode; out->ReadChar = KbdReadChar; out->SetCallback = KbdSetCallback; + out->GetModifiers = KbdGetModifiers; out->Isr = KbdIsr; out->IrqNumber = 1; } diff --git a/code/hal/hal_main.c b/code/hal/hal_main.c index 32d37ca..be70523 100644 --- a/code/hal/hal_main.c +++ b/code/hal/hal_main.c @@ -1,10 +1,10 @@ // hal_main.c - HAL initialisation and HalGetApi -#include "hal_api.h" -#include "hal_apic.h" +#include +#include #include #include #include -#include "printf.h" +#include // Declared in other hal_*.c files: void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out); @@ -310,7 +310,7 @@ static void HalShutdown(void) { if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB); // Firmware should have powered off by now. If it didn't, halt. - for (;;) __halt(); + while (true) __halt(); } // --------------------------------------------------------------------------- diff --git a/code/hal/hal_pci.c b/code/hal/hal_pci.c index 90f1a72..5d9f6e5 100644 --- a/code/hal/hal_pci.c +++ b/code/hal/hal_pci.c @@ -1,6 +1,6 @@ // hal_pci.c - PCI configuration space via Mechanism 1 port I/O -#include "hal_api.h" #include +#include #define PCI_CONFIG_ADDR 0xCF8 #define PCI_CONFIG_DATA 0xCFC @@ -55,9 +55,16 @@ static bool PciFindClass(uint8_t classCode, uint8_t subclass, 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 +} + void HalPciInitialize(HAL_PCI* out) { - out->ConfigRead32 = PciRead32; - out->ConfigWrite32 = PciWrite32; + out->Read32 = PciRead32; + out->Write32 = PciWrite32; out->FindDevice = PciFindDevice; out->FindClass = PciFindClass; + out->GetIrqLine = PciGetIrqLine; } diff --git a/code/hal/hal_screen.c b/code/hal/hal_screen.c index 0ce6892..6a671aa 100644 --- a/code/hal/hal_screen.c +++ b/code/hal/hal_screen.c @@ -3,10 +3,10 @@ // The 8x16 bitmap font is a standard VGA/PC font. We embed just the // printable ASCII range (0x20–0x7E). Each glyph is 16 bytes; bit 7 of // each byte is the leftmost pixel. -#include "hal_api.h" #include #include #include +#include static uint32_t* gFb = (uint32_t *)0; static int gWidth = 0; diff --git a/code/hal/hal_timer.c b/code/hal/hal_timer.c index 85934de..c308acf 100644 --- a/code/hal/hal_timer.c +++ b/code/hal/hal_timer.c @@ -1,9 +1,9 @@ -// hal_timer.c - Local APIC timer, calibrated against PIT channel 2 -#include "hal_api.h" -#include "hal_apic.h" -#include "hal_debug.h" +// hal_timer.c - Local APIC timer #include #include +#include +#include +#include #define LAPIC_TIMER_LVT 0x320 #define LAPIC_TIMER_INIT 0x380 diff --git a/code/hal/io_driver.h b/code/hal/io_driver.h index 5c4550c..574d82a 100644 --- a/code/hal/io_driver.h +++ b/code/hal/io_driver.h @@ -1,17 +1,40 @@ #pragma once -// io_driver.h - driver-side interface for registering disk controllers -#include "hal_api.h" -#include "ke_status.h" -#include "../kernel/io_file.h" +// io_driver.h - driver-side interface for registering disk controllers +// +// DISK_DRIVER.SubmitIrp is the async path. IRP is forward-declared here so +// this header (and hal_disk.c) remain free of kernel sync-object dependencies. +// Drivers that implement SubmitIrp include ke_irp.h themselves. + #include +#include +#include +#include typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API* hal); typedef void (*DRIVER_UNLOAD_FN)(void); +// Forward declaration only — avoids dragging ke_event.h into the HAL layer. +struct IRP; + typedef struct DISK_DRIVER { - KSTATUS (*ReadSectors)(uint64_t lba, uint32_t count, void* buf); - KSTATUS (*WriteSectors)(uint64_t lba, uint32_t count, const void* buf); + void *Ctx; + + // --- Synchronous path (always present) --------------------------------- + KSTATUS (*ReadSectors) (void *ctx, uint64_t lba, uint32_t count, void *buf); + KSTATUS (*WriteSectors)(void *ctx, uint64_t lba, uint32_t count, const void *buf); + + // --- Asynchronous path (optional) -------------------------------------- + // NULL = driver has not implemented async; IoSubmitIrp() falls back to + // the sync path. Returns STATUS_PENDING when the IRP is accepted. + KSTATUS (*SubmitIrp)(void *ctx, struct IRP *irp); + uint64_t SectorCount; } DISK_DRIVER; -KSTATUS IoRegisterDisk(uint8_t driveIndex, const DISK_DRIVER* driver); +KSTATUS IoRegisterDisk (uint8_t driveIndex, const DISK_DRIVER *driver); + +// Internal dispatch used by ke_irp.c. Calls SubmitIrp if present, otherwise +// runs the sync callbacks inline and writes irp->Status; does NOT touch any +// kernel sync objects so it is safe to call from the HAL layer. +// Returns STATUS_PENDING (async path taken) or STATUS_SUCCESS (ran inline). +KSTATUS HalDiskDispatch (uint8_t drive, struct IRP *irp); diff --git a/code/ini_parser.c b/code/ini_parser.c index adbd93b..aa45d4e 100644 --- a/code/ini_parser.c +++ b/code/ini_parser.c @@ -1,8 +1,8 @@ // ini_parser.c - minimal [section] key=value parser // Used by the bootloader to read boot.ini and drivers/drivers.ini. // No heap; caller provides fixed-size output buffers. -#include "ini_parser.h" #include +#include static inline int IsWs(char c) { return c == ' ' || c == '\t' || c == '\r'; } diff --git a/code/kernel/boot_info.h b/code/kernel/boot_info.h index 4186585..66f756e 100644 --- a/code/kernel/boot_info.h +++ b/code/kernel/boot_info.h @@ -2,7 +2,7 @@ // boot_info.h - shared between bootx64.efi and kernel.exe // The bootloader fills this structure and passes a pointer to KernelMain. #include -#include "memory_map.h" +#include #define BOOT_INFO_MAGIC 0x424F4F54494E464FULL /* 'BOOTINFO' */ #define MAX_BOOT_MODULES 32 diff --git a/code/kernel/io_file.c b/code/kernel/io_file.c index cdaecc7..2c7a6bf 100644 --- a/code/kernel/io_file.c +++ b/code/kernel/io_file.c @@ -1,9 +1,9 @@ // io_file.c - VFS dispatch layer -#include "ke_list.h" -#include "io_file.h" -#include "mm_virt.h" -#include "ob_object.h" #include +#include +#include +#include +#include #define MAX_DRIVES 8 diff --git a/code/kernel/io_file.h b/code/kernel/io_file.h index c85c558..c12cb8b 100644 --- a/code/kernel/io_file.h +++ b/code/kernel/io_file.h @@ -2,10 +2,10 @@ // io_file.h - VFS layer // Filesystem drivers register an IO_FILESYSTEM vtable via IoRegisterFilesystem. // Kernel and user code open files through IoOpenFile. -#include "ke_status.h" -#include "ob_object.h" #include #include +#include +#include // --------------------------------------------------------------------------- // Filesystem driver vtable - filled in by fat32.sys (or any future fs driver) diff --git a/code/kernel/ke_debug.c b/code/kernel/ke_debug.c index 7f0405c..f2a88a3 100644 --- a/code/kernel/ke_debug.c +++ b/code/kernel/ke_debug.c @@ -1,9 +1,10 @@ // ke_debug.c - debugging utilities -#include "hal_api.h" #include #include #include -#include "printf.h" +#include +#include +#include int _fltused; // fuckass int gDebugEnabled = 0; @@ -16,10 +17,13 @@ void kernel_putchar(char c) { void DbgPrint(const char* fmt, ...) { if (!gDebugEnabled) return; + static SPINLOCK sl = {0}; + uintptr_t flags = KeAcquireSpinlock(&sl); va_list va; va_start(va, fmt); vprintf(fmt, va); va_end(va); + KeReleaseSpinlock(&sl, flags); } void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), int v) { diff --git a/code/kernel/ke_event.c b/code/kernel/ke_event.c index 285b486..066c932 100644 --- a/code/kernel/ke_event.c +++ b/code/kernel/ke_event.c @@ -1,8 +1,9 @@ // ke_event.c - synchronisation events -#include "ke_event.h" -#include "ke_thread.h" -#include "ke_sched.h" -#include "ob_object.h" +#include +#include +#include +#include +#include static void EventDestroy(OBJECT* obj) { (void)obj; } @@ -33,6 +34,7 @@ void KeSignalEvent(EVENT* event) { flags = KeAcquireSpinlock(&event->Lock); } } else { + event->Signaled = 1; if (!LIST_IS_EMPTY(&event->WaitList)) { LIST_ENTRY* node = ListPopHead(&event->WaitList); THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink); @@ -40,7 +42,6 @@ void KeSignalEvent(EVENT* event) { KeUnblockThread(t); return; } - event->Signaled = 1; } KeReleaseSpinlock(&event->Lock, flags); } @@ -56,7 +57,7 @@ KSTATUS KeWaitForEvent(EVENT* event) { if (!event) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); - for (;;) { + while (true) { uintptr_t flags = KeAcquireSpinlock(&event->Lock); if (event->Signaled) { if (event->Type == EventAutoReset) event->Signaled = 0; diff --git a/code/kernel/ke_event.h b/code/kernel/ke_event.h index 1549570..11e6ebe 100644 --- a/code/kernel/ke_event.h +++ b/code/kernel/ke_event.h @@ -1,10 +1,10 @@ #pragma once // ke_event.h - auto-reset and manual-reset events -#include "ob_object.h" -#include "ke_spinlock.h" -#include "ke_list.h" -#include "ke_status.h" #include +#include +#include +#include +#include typedef enum EVENT_TYPE { EventAutoReset, EventManualReset } EVENT_TYPE; diff --git a/code/kernel/ke_idt.c b/code/kernel/ke_idt.c index d2a2d13..da6adb7 100644 --- a/code/kernel/ke_idt.c +++ b/code/kernel/ke_idt.c @@ -8,9 +8,9 @@ // 0x20 ring-3 data (DPL=3) - note: 0x1B/0x23 = 0x18|3 / 0x20|3 // 0x28 TSS low // 0x30 TSS high -#include "ke_idt.h" #include #include +#include // External assembly procedures extern void KeFlushGdt(void* pGdtr, uint16_t codeSel, uint16_t dataSel, uint16_t tssSel); diff --git a/code/kernel/ke_irp.c b/code/kernel/ke_irp.c new file mode 100644 index 0000000..8da55c9 --- /dev/null +++ b/code/kernel/ke_irp.c @@ -0,0 +1,112 @@ +// ke_irp.c - I/O Request Packet dispatch and queue helpers +// +// This file lives in the kernel layer and is the ONLY place that bridges the +// IRP abstraction to kernel sync objects. All calls to KeEventCreate, +// KeSignalEvent, KeWaitForEvent, KeSignalSemaphore, and ObDereferenceObject +// are confined here; hal_disk.c and io_driver.h remain free of those symbols. +// +// Dependency graph (→ = includes / calls): +// ke_irp.c → ke_irp.h (IRP type, IRP_QUEUE, forward decls) +// → ke_event.h (KeEventCreate, KeSignalEvent, KeWaitForEvent) +// → ke_semaphore.h (KeSignalSemaphore) +// → ob_object.h (ObDereferenceObject) +// → io_driver.h (HalDiskDispatch) +// hal_disk.c → io_driver.h only (no ke_event.h, no ke_semaphore.h) + +#include +#include +#include +#include +#include + +// ------------------------------------------------------------------------- +// IRP_QUEUE operations +// ------------------------------------------------------------------------- +// These are defined here (not as static inlines in ke_irp.h) so that +// ke_irp.h can avoid including ke_semaphore.h, keeping the header safe +// for inclusion from the HAL layer. + +void IrpQueueInit(IRP_QUEUE *q, struct SEMAPHORE *sem) { + q->Lock = (SPINLOCK)SPINLOCK_INIT; + q->Semaphore = sem; + LIST_INIT(&q->Head); +} + +void IrpQueueEnqueue(IRP_QUEUE *q, IRP *irp) { + uintptr_t flags = KeAcquireSpinlock(&q->Lock); + if (irp->Flags & IRP_FLAG_PRIORITY) + ListInsertHead(&q->Head, &irp->Link); + else + ListInsertTail(&q->Head, &irp->Link); + KeReleaseSpinlock(&q->Lock, flags); + // Signal outside the spinlock — safe, and avoids priority-inversion risk. + KeSignalSemaphore((SEMAPHORE *)q->Semaphore); +} + +IRP *IrpQueueDequeue(IRP_QUEUE *q) { + uintptr_t flags = KeAcquireSpinlock(&q->Lock); + LIST_ENTRY *node = ListPopHead(&q->Head); + KeReleaseSpinlock(&q->Lock, flags); + if (!node) return (IRP *)0; + return CONTAINING_RECORD(node, IRP, Link); +} + +// ------------------------------------------------------------------------- +// IoSubmitIrp +// ------------------------------------------------------------------------- +// Calls HalDiskDispatch() which is HAL-layer safe (no event references). +// If the dispatch ran inline (sync fallback), we fire the completion hooks +// here in the kernel layer where we can legally call KeSignalEvent. + +KSTATUS IoSubmitIrp(IRP *irp) { + if (!irp) return STATUS_INVALID_PARAMETER; + + KSTATUS st = HalDiskDispatch(irp->DriveIndex, irp); + + if (st == STATUS_NOT_FOUND) + return STATUS_NOT_FOUND; + + if (st == STATUS_SUCCESS) { + // Sync fallback ran; irp->Status is already written. + // Fire completion hooks now — we are in kernel context so this is safe. + if (irp->CompletionEvent) + KeSignalEvent((EVENT *)irp->CompletionEvent); + if (irp->CompletionFn) + irp->CompletionFn(irp, irp->CompletionCtx); + } + + // Return STATUS_PENDING in both cases so callers have a uniform contract: + // "completion is signalled via the event/callback, not this return value." + return STATUS_PENDING; +} + +// ------------------------------------------------------------------------- +// IoSubmitIrpSync +// ------------------------------------------------------------------------- +// Creates a stack-allocated auto-reset event, attaches it to the IRP, +// submits, and blocks. Callers that already provided a CompletionEvent +// should use IoSubmitIrp() directly and wait on their own event instead. + +KSTATUS IoSubmitIrpSync(IRP *irp) { + if (!irp) return STATUS_INVALID_PARAMETER; + + EVENT *ev = (EVENT *)0; + KSTATUS st = KeEventCreate(EventAutoReset, 0, &ev); + if (!KSUCCESS(st)) return st; + + // Attach our event; preserve any callback the caller may have set. + irp->CompletionEvent = ev; + + st = IoSubmitIrp(irp); + // IoSubmitIrp always returns STATUS_PENDING on the non-error paths. + // The event is signalled either by the driver worker (async) or by + // IoSubmitIrp itself (sync fallback), so KeWaitForEvent will always + // return promptly in the sync-fallback case. + if (st == STATUS_PENDING) + KeWaitForEvent(ev); + + // Release our reference to the temporary event object. + ObDereferenceObject(&ev->Header); + + return irp->Status; +} diff --git a/code/kernel/ke_irp.h b/code/kernel/ke_irp.h new file mode 100644 index 0000000..0e99dc0 --- /dev/null +++ b/code/kernel/ke_irp.h @@ -0,0 +1,95 @@ +#pragma once +// ke_irp.h - I/O Request Packet +// +// This header intentionally includes only ke_list.h, ke_spinlock.h, and +// ke_status.h — all of which are pure-inline / macro headers with no +// external symbol references. ke_event.h and ke_semaphore.h are forward- +// declared here so the HAL layer (hal_disk.c, io_driver.h) can include this +// header without dragging in kernel sync-object link dependencies. +// +// Files that actually call KeEventCreate / KeSignalEvent / KeWaitForEvent +// (ke_irp.c, ahci.c) include ke_event.h and ke_semaphore.h themselves. + +#include +#include +#include +#include +#include + +// Forward declarations — full types live in ke_event.h / ke_semaphore.h. +// We only ever hold pointers in IRP and IRP_QUEUE, so a forward decl suffices. +struct EVENT; +struct SEMAPHORE; + +// ------------------------------------------------------------------------- +// IRP flags +// ------------------------------------------------------------------------- + +#define IRP_FLAG_WRITE (1u << 0) // direction: write to device +#define IRP_FLAG_PRIORITY (1u << 1) // insert at head of queue (elevated) + +// ------------------------------------------------------------------------- +// Forward declaration (IRP is self-referential via CompletionFn) +// ------------------------------------------------------------------------- + +typedef struct IRP IRP; + +// Completion callback: invoked in driver worker context; must not block. +typedef void (*IRP_COMPLETION_FN)(IRP *irp, void *ctx); + +// ------------------------------------------------------------------------- +// I/O Request Packet +// ------------------------------------------------------------------------- + +typedef struct IRP { + // Embedded list node — driver-internal; do not touch after submit. + LIST_ENTRY Link; + + // ---- Request parameters (caller fills before IoSubmitIrp) ------------ + + uint8_t DriveIndex; + uint32_t Flags; // IRP_FLAG_* + uint64_t Lba; + uint32_t SectorCount; + void *Buffer; // kernel VA; valid until completion + + // ---- Completion (caller fills before submit; driver writes Status) ---- + + volatile KSTATUS Status; + + // Forward-declared pointer: callers that create events include ke_event.h. + struct EVENT *CompletionEvent; + + IRP_COMPLETION_FN CompletionFn; + void *CompletionCtx; + + // ---- Driver-internal -------------------------------------------------- + + uint8_t HwSlot; + void *DriverCtx; +} IRP; + +// ------------------------------------------------------------------------- +// IRP_QUEUE (per-device, driver-internal) +// ------------------------------------------------------------------------- + +typedef struct IRP_QUEUE { + SPINLOCK Lock; + LIST_ENTRY Head; + struct SEMAPHORE *Semaphore; // forward-declared pointer +} IRP_QUEUE; + +// Non-inline: defined in ke_irp.c, which can pull in ke_semaphore.h safely. +void IrpQueueInit (IRP_QUEUE *q, struct SEMAPHORE *sem); +void IrpQueueEnqueue (IRP_QUEUE *q, IRP *irp); +IRP *IrpQueueDequeue (IRP_QUEUE *q); + +// ------------------------------------------------------------------------- +// Public dispatch API — implemented in kernel/ke_irp.c +// ------------------------------------------------------------------------- + +// Submit asynchronously. Returns STATUS_PENDING on success. +KSTATUS IoSubmitIrp (IRP *irp); + +// Submit and block until complete. Returns irp->Status. +KSTATUS IoSubmitIrpSync(IRP *irp); diff --git a/code/kernel/ke_irq.c b/code/kernel/ke_irq.c index fd3a46c..cedb976 100644 --- a/code/kernel/ke_irq.c +++ b/code/kernel/ke_irq.c @@ -1,6 +1,6 @@ // ke_irq.c — IRQ dispatch, kernel-side -#include "ke_irq.h" -#include "hal_api.h" +#include +#include #define MAX_IRQS 24 diff --git a/code/kernel/ke_irq.h b/code/kernel/ke_irq.h index ba87dae..10ba7c2 100644 --- a/code/kernel/ke_irq.h +++ b/code/kernel/ke_irq.h @@ -1,6 +1,6 @@ #pragma once -#include "hal_api.h" #include +#include void KeIrqInitialize(const HAL_API *hal); void KeIrqRegister(uint8_t irq, void (*handler)(void)); diff --git a/code/kernel/ke_mutex.c b/code/kernel/ke_mutex.c index 1de91a2..8d4f048 100644 --- a/code/kernel/ke_mutex.c +++ b/code/kernel/ke_mutex.c @@ -1,9 +1,10 @@ // ke_mutex.c - binary ownership mutex -#include "ke_mutex.h" -#include "ke_thread.h" -#include "ke_sched.h" -#include "mm_virt.h" -#include "ob_object.h" +#include +#include +#include +#include +#include +#include static void MutexDestroy(OBJECT* obj) { (void)obj; } @@ -23,7 +24,7 @@ KSTATUS KeAcquireMutex(MUTEX* mutex) { if (!mutex) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); - for (;;) { + while (true) { uintptr_t flags = KeAcquireSpinlock(&mutex->Lock); if (!mutex->Owner) { mutex->Owner = self; diff --git a/code/kernel/ke_mutex.h b/code/kernel/ke_mutex.h index cb2e843..ad3c466 100644 --- a/code/kernel/ke_mutex.h +++ b/code/kernel/ke_mutex.h @@ -1,9 +1,9 @@ #pragma once // ke_mutex.h - binary ownership mutex -#include "ob_object.h" -#include "ke_spinlock.h" -#include "ke_list.h" -#include "ke_status.h" +#include +#include +#include +#include typedef struct MUTEX { OBJECT Header; diff --git a/code/kernel/ke_process.c b/code/kernel/ke_process.c index 7c16f42..0909bf1 100644 --- a/code/kernel/ke_process.c +++ b/code/kernel/ke_process.c @@ -1,11 +1,11 @@ // ke_process.c - process model -#include "ke_process.h" -#include "ke_thread.h" -#include "ke_event.h" -#include "mm_virt.h" -#include "ob_object.h" #include #include +#include +#include +#include +#include +#include // --------------------------------------------------------------------------- // Global process list diff --git a/code/kernel/ke_process.h b/code/kernel/ke_process.h index 0b3c0b2..bc455a3 100644 --- a/code/kernel/ke_process.h +++ b/code/kernel/ke_process.h @@ -2,10 +2,10 @@ // ke_process.h - process model // A process owns a handle table and a list of threads. // There is one shared address space - processes do not own one. -#include "ob_object.h" -#include "ke_list.h" -#include "ke_spinlock.h" -#include "ke_event.h" +#include +#include +#include +#include #define PROCESS_NAME_MAX 64 #define HANDLE_TABLE_SIZE 256 diff --git a/code/kernel/ke_queue.c b/code/kernel/ke_queue.c index 08b1549..5b1421e 100644 --- a/code/kernel/ke_queue.c +++ b/code/kernel/ke_queue.c @@ -1,9 +1,9 @@ // ke_queue.c - ring-buffer message queue -#include "ke_queue.h" -#include "ke_thread.h" -#include "ke_sched.h" -#include "ob_object.h" #include +#include +#include +#include +#include static void QueueDestroy(OBJECT* obj) { (void)obj; } @@ -24,7 +24,7 @@ KSTATUS KeSendMessage(QUEUE* queue, const QUEUE_MESSAGE* msg, bool blocking) { if (!queue || !msg) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); - for (;;) { + while (true) { uintptr_t flags = KeAcquireSpinlock(&queue->Lock); if (queue->Count < QUEUE_DEPTH) { queue->Ring[queue->Tail] = *msg; @@ -56,7 +56,7 @@ KSTATUS KeReceiveMessage(QUEUE* queue, QUEUE_MESSAGE* msg, bool blocking) { if (!queue || !msg) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); - for (;;) { + while (true) { uintptr_t flags = KeAcquireSpinlock(&queue->Lock); if (queue->Count > 0) { *msg = queue->Ring[queue->Head]; diff --git a/code/kernel/ke_queue.h b/code/kernel/ke_queue.h index aafd8d2..9a3c48c 100644 --- a/code/kernel/ke_queue.h +++ b/code/kernel/ke_queue.h @@ -2,12 +2,12 @@ // ke_queue.h - fixed-depth message queue // Processes share an address space, so a pointer in Param[] is valid // on both sides. The queue is only a synchronisation and ordering tool. -#include "ob_object.h" -#include "ke_spinlock.h" -#include "ke_list.h" -#include "ke_status.h" #include #include +#include +#include +#include +#include #define QUEUE_MESSAGE_SIZE 64 /* bytes, fixed */ #define QUEUE_DEPTH 256 diff --git a/code/kernel/ke_sched.c b/code/kernel/ke_sched.c index 818bdd1..508a681 100644 --- a/code/kernel/ke_sched.c +++ b/code/kernel/ke_sched.c @@ -1,10 +1,11 @@ // ke_sched.c - MLFQ scheduler -#include "ke_sched.h" -#include "ke_thread.h" -#include "ke_spinlock.h" -#include "ke_list.h" -#include "mm_virt.h" #include +#include +#include +#include +#include +#include +#include #define DEMOTION_QUANTA 4 /* ticks at a level before demotion */ @@ -20,7 +21,7 @@ THREAD* KeCurrentThread(void) { return gCurrentThread; } void KeSetCurrentThread(THREAD* t){ gCurrentThread = t; } static void IdleLoop(void) { - for (;;) { __halt(); } + while (true) { __halt(); } } void KeInitializeScheduler(void) { diff --git a/code/kernel/ke_sched.h b/code/kernel/ke_sched.h index ee1328c..d307123 100644 --- a/code/kernel/ke_sched.h +++ b/code/kernel/ke_sched.h @@ -1,7 +1,7 @@ #pragma once // ke_sched.h - Multi-Level Feedback Queue scheduler // Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick. -#include "ke_thread.h" +#include #define QUANTUM_MS 10 /* base time slice in milliseconds */ diff --git a/code/kernel/ke_semaphore.c b/code/kernel/ke_semaphore.c index 633c696..152ccab 100644 --- a/code/kernel/ke_semaphore.c +++ b/code/kernel/ke_semaphore.c @@ -1,8 +1,9 @@ // ke_semaphore.c - counting semaphore -#include "ke_semaphore.h" -#include "ke_thread.h" -#include "ke_sched.h" -#include "ob_object.h" +#include +#include +#include +#include +#include static void SemDestroy(OBJECT* obj) { (void)obj; } @@ -24,7 +25,7 @@ KSTATUS KeWaitSemaphore(SEMAPHORE* sem) { if (!sem) return STATUS_INVALID_PARAMETER; THREAD* self = KeCurrentThread(); - for (;;) { + while (true) { uintptr_t flags = KeAcquireSpinlock(&sem->Lock); if (sem->Count > 0) { sem->Count--; diff --git a/code/kernel/ke_semaphore.h b/code/kernel/ke_semaphore.h index 1d6f9f2..befed8b 100644 --- a/code/kernel/ke_semaphore.h +++ b/code/kernel/ke_semaphore.h @@ -1,10 +1,10 @@ #pragma once // ke_semaphore.h - counting semaphore -#include "ob_object.h" -#include "ke_spinlock.h" -#include "ke_list.h" -#include "ke_status.h" #include +#include +#include +#include +#include typedef struct SEMAPHORE { OBJECT Header; diff --git a/code/kernel/ke_status.h b/code/kernel/ke_status.h index 0275a8b..f3a90c4 100644 --- a/code/kernel/ke_status.h +++ b/code/kernel/ke_status.h @@ -11,6 +11,7 @@ typedef uint32_t KSTATUS; #define STATUS_FAILURE ((KSTATUS)0x0001) #define STATUS_NOT_SUPPORTED ((KSTATUS)0x0002) #define STATUS_TIMEOUT ((KSTATUS)0x0003) +#define STATUS_PENDING ((KSTATUS)0x0004) // --- Validation & Parameters (0x0010) --- #define STATUS_INVALID_PARAMETER ((KSTATUS)0x0010) diff --git a/code/kernel/ke_syscall.c b/code/kernel/ke_syscall.c index 212618c..f19d1ad 100644 --- a/code/kernel/ke_syscall.c +++ b/code/kernel/ke_syscall.c @@ -30,22 +30,22 @@ // call: the object model already handled it; the dedicated syscall was only // adding a footgun where callers could bypass the unified refcount path. -#include "ke_thread.h" -#include "ke_sched.h" -#include "ke_process.h" -#include "ke_mutex.h" -#include "ke_semaphore.h" -#include "ke_event.h" -#include "ke_queue.h" -#include "ke_debug.h" -#include "mm_virt.h" -#include "ldr_image.h" -#include "syscall_nr.h" -#include "hal_api.h" -#include "io_file.h" #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // --------------------------------------------------------------------------- // HAL pointer - set once at boot by KeSyscallSetHal() @@ -106,18 +106,18 @@ static uint64_t SyscallExitProcess(uint64_t a0, uint64_t a1, uint64_t a2, PROCESS* proc = t ? t->Owner : NULL; if (proc) KeExitProcess(proc, (int)a0); if (t) KeExitThread(t, (int)a0); - for (;;) ; // unreachable - KeExitThread does not return + while (true) ; // unreachable - KeExitThread does not return } // --------------------------------------------------------------------------- -// SYS_EXIT_THREAD (1) a0 = exitCode +// SYS_EXIT_THREAD (true) a0 = exitCode // --------------------------------------------------------------------------- static uint64_t SyscallExitThread(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; THREAD* t = KeCurrentThread(); if (t) KeExitThread(t, (int)a0); - for (;;) ; + while (true) ; } // --------------------------------------------------------------------------- diff --git a/code/kernel/ke_syscall.h b/code/kernel/ke_syscall.h index bbe73bb..0f2b553 100644 --- a/code/kernel/ke_syscall.h +++ b/code/kernel/ke_syscall.h @@ -1,7 +1,7 @@ #pragma once #include -#include "hal_api.h" +#include // Set the HAL API pointer for syscalls void KeSyscallSetHal(const HAL_API* hal); diff --git a/code/kernel/ke_thread.c b/code/kernel/ke_thread.c index c118fce..f7db4ea 100644 --- a/code/kernel/ke_thread.c +++ b/code/kernel/ke_thread.c @@ -1,10 +1,10 @@ // ke_thread.c - thread model -#include "ke_thread.h" -#include "ke_sched.h" -#include "mm_virt.h" -#include "ob_object.h" #include #include +#include +#include +#include +#include static uint32_t gNextThreadId = 1; extern void KeThreadEntry(void); diff --git a/code/kernel/ke_thread.h b/code/kernel/ke_thread.h index 3dd1b6f..6bae3e7 100644 --- a/code/kernel/ke_thread.h +++ b/code/kernel/ke_thread.h @@ -1,10 +1,10 @@ #pragma once // ke_thread.h - thread model // A THREAD is the unit of scheduling. Every thread belongs to exactly one process. -#include "ob_object.h" -#include "ke_list.h" -#include "ke_process.h" #include +#include +#include +#include #define KSTACK_SIZE (64 * 1024) /* 64 KB kernel stack */ #define USTACK_SIZE (512 * 1024) /* 512 KB user stack */ diff --git a/code/kernel/kmain.c b/code/kernel/kmain.c index 4ea5ae6..411ac75 100644 --- a/code/kernel/kmain.c +++ b/code/kernel/kmain.c @@ -1,22 +1,22 @@ -// main.c - kernel entry point -#include "boot_info.h" -#include "mm_phys.h" -#include "mm_virt.h" -#include "ob_object.h" -#include "ke_process.h" -#include "ke_thread.h" -#include "ke_sched.h" -#include "ke_idt.h" -#include "ke_irq.h" -#include "ke_syscall.h" -#include "ke_debug.h" -#include "ldr_image.h" -#include "hal_api.h" -#include "io_file.h" -#include "ini_parser.h" +// kmain.c - kernel entry point #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // dynamic arrays bitch typedef struct { @@ -72,6 +72,11 @@ typedef struct { #define arr_free(arr) MmFreePool((Header*)(arr) - 1) +typedef struct { + BOOT_INFO* bootInfo; + const HAL_API* hal; +} SYSTEM_INIT_ARGS; + typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *); // HAL pointer cached at boot so kernel threads can reach it without @@ -107,7 +112,7 @@ static void DriversIniCb(const char* section, const char* key, } static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { - DbgPrint("KernelMain: Initializing drivers...\n"); + DbgPrint("InitializeDrivers: Initializing drivers...\n"); // boot drivers (provided by the bootloader) for (uint32_t i = 0; i < bootInfo->ModuleCount; i++) { @@ -117,13 +122,13 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { size_t len = strlen(name); if (len < 4 || strcmp(name + len - 4, ".sys")) continue; - DbgPrint("KernelMain: Executing DriverEntry for %s\n", name); + DbgPrint("InitializeDrivers: Executing DriverEntry for %s\n", name); DRIVER_ENTRY_FN entry = (DRIVER_ENTRY_FN)(LdrGetEntryPoint(bootInfo->Modules[i].VirtBase)); KSTATUS ds = entry(hal); if (!KSUCCESS(ds)) { - DbgPrint("KernelMain: Driver %s failed to init!\n", name); + DbgPrint("InitializeDrivers: Driver %s failed to init!\n", name); } } @@ -133,17 +138,17 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { s = IoOpenFile("drivers\\drivers.ini", &f); if (s != STATUS_SUCCESS) { - DbgPrint("KernelMain: Failed to open drivers.ini\n"); - _disable(); while(1) __halt(); + DbgPrint("InitializeDrivers: Failed to open drivers.ini\n"); + _disable(); while(true) __halt(); } uint64_t size = IoGetFileSize(f) + 1; char* mem = MmAllocatePool(size); size_t got; if (!KSUCCESS(IoReadFile(f, mem, size, &got))) { - DbgPrint("KernelMain: Failed to read drivers.ini\n"); + DbgPrint("InitializeDrivers: Failed to read drivers.ini\n"); IoCloseFile(f); - _disable(); while(1) __halt(); + _disable(); while(true) __halt(); } mem[got] = '\0'; @@ -159,17 +164,18 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { size_t len = strlen(runtimeKeys[i]); char* path = MmAllocatePool(len + 10); if (!path) { - DbgPrint("KernelMain: Failed to allocate memory for driver path. How?\n"); + DbgPrint("InitializeDrivers: Failed to allocate memory for driver path. How?\n"); continue; } - // memcpy manouvers - memcpy(path, "drivers\\", 8); - memcpy(path + 8, runtimeKeys[i], len + 1); + // Assemble the path + strncpy(path, "drivers\\", sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + strncat(path, runtimeKeys[i], sizeof(path) - strlen(path) - 1); s = IoOpenFile(path, &f); if (s != STATUS_SUCCESS) { - DbgPrint("KernelMain: Failed to open runtime driver: %s\n", runtimeKeys[i]); + DbgPrint("InitializeDrivers: Failed to open runtime driver: %s\n", runtimeKeys[i]); continue; // Skip this driver but keep going with others } @@ -178,7 +184,7 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { uint64_t drvSize = IoGetFileSize(f); void* drvMem = MmAllocatePool(drvSize); if (!KSUCCESS(IoReadFile(f, drvMem, drvSize, &got))) { - DbgPrint("KernelMain: Failed to read runtime driver: %s\n", runtimeKeys[i]); + DbgPrint("InitializeDrivers: Failed to read runtime driver: %s\n", runtimeKeys[i]); IoCloseFile(f); continue; } @@ -186,7 +192,7 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { uintptr_t mappedBase = 0; if (!KSUCCESS(LdrLoadImage(drvMem, drvSize, &mappedBase, false))) { // Load into kernel space - DbgPrint("KernelMain: Failed to load runtime driver: %s\n", runtimeKeys[i]); + DbgPrint("InitializeDrivers: Failed to load runtime driver: %s\n", runtimeKeys[i]); continue; } @@ -194,7 +200,7 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) { KSTATUS ds = entry(hal); if (!KSUCCESS(ds)) { - DbgPrint("KernelMain: Driver %s failed to init!\n", runtimeKeys[i]); + DbgPrint("InitializeDrivers: Driver %s failed to init!\n", runtimeKeys[i]); } } @@ -223,7 +229,7 @@ static void LoadAndRegisterDll(const char* filename) { // Read the DLL from disk s = IoOpenFile(filename, &f); if (s != STATUS_SUCCESS) { - DbgPrint("KernelMain: Failed to open DLL: %s\n", filename); + DbgPrint("LoadAndRegisterDll: Failed to open DLL: %s\n", filename); return; } @@ -231,7 +237,7 @@ static void LoadAndRegisterDll(const char* filename) { void* mem = MmAllocatePool(size); // Temporary pool for the raw file size_t got; if (!KSUCCESS(IoReadFile(f, mem, size, &got))) { - DbgPrint("KernelMain: Failed to read %s\n", filename); + DbgPrint("LoadAndRegisterDll: Failed to read %s\n", filename); IoCloseFile(f); return; } @@ -242,7 +248,7 @@ static void LoadAndRegisterDll(const char* filename) { uintptr_t mappedBase = 0; s = LdrLoadImage(mem, size, &mappedBase, true); if (!KSUCCESS(s)) { - DbgPrint("KernelMain: Failed to map %s\n", filename); + DbgPrint("LoadAndRegisterDll: Failed to map %s\n", filename); return; } @@ -262,23 +268,23 @@ static PROCESS* LaunchUserProcess(const char* filePath) { // Read the executable file into a raw buffer s = IoOpenFile(filePath, &f); - DbgPrint("KernelMain: IoOpenFile: %d\n", s); + DbgPrint("LaunchUserProcess: IoOpenFile: %d\n", s); if (s != STATUS_SUCCESS) { DbgPrint("Failed to load file: \"%s\"!\n", filePath); return (PROCESS*)0; } uint64_t size = IoGetFileSize(f); - DbgPrint("KernelMain: IoGetFileSize: %llu\n", size); + DbgPrint("LaunchUserProcess: IoGetFileSize: %llu\n", size); void* mem = MmAllocateUserMemory(size); - DbgPrint("KernelMain: MmAllocateUserMemory: 0x%llX\n", mem); + DbgPrint("LaunchUserProcess: MmAllocateUserMemory: 0x%llX\n", mem); size_t got; s = IoReadFile(f, mem, size, &got); - DbgPrint("KernelMain: IoReadFile: %d\n", s); + DbgPrint("LaunchUserProcess: IoReadFile: %d\n", s); IoCloseFile(f); - DbgPrint("KernelMain: IoCloseFile\n"); + DbgPrint("LaunchUserProcess: IoCloseFile\n"); // Create the process object PROCESS* proc; @@ -295,17 +301,17 @@ static PROCESS* LaunchUserProcess(const char* filePath) { uintptr_t imageBase = 0; s = LdrLoadImage(mem, size, &imageBase, true); if (!KSUCCESS(s)) { - DbgPrint("KernelMain: LdrLoadImage failed with status: %d\n", s); + DbgPrint("LaunchUserProcess: LdrLoadImage failed with status: %d\n", s); } uintptr_t entryPoint = LdrGetEntryPoint(imageBase); - DbgPrint("KernelMain: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint); + DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint); // Create the main thread, pointing it to the PE Entry Point s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread); if (!KSUCCESS(s)) { - DbgPrint("KernelMain: Failed to create thread. Status: %d\n", s); + DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s); } - DbgPrint("KernelMain: Thread created and queued!\n"); + DbgPrint("LaunchUserProcess: Thread created and queued!\n"); // Caller owns the reference returned by KeCreateProcess. return proc; @@ -318,11 +324,11 @@ static PROCESS* LaunchUserProcess(const char* filePath) { static void ProcessWatcherThread(void* arg) { PROCESS* proc = (PROCESS*)arg; KeWaitForEvent(proc->ExitEvent); - DbgPrint("KernelMain: Process \"%s\" (PID %u) exited with code %d.\n", + DbgPrint("ProcessWatcherThread: Process \"%s\" (PID %u) exited with code %d.\n", proc->Name, proc->ProcessId, proc->ExitCode); ObDereferenceObject(&proc->Header); - DbgPrint("KernelMain: Shutting down.\n"); + DbgPrint("ProcessWatcherThread: Shutting down.\n"); gKmainHal->Power.Shutdown(); // does not return } @@ -345,6 +351,47 @@ static void PrintProcessList() { } } +void SystemInitThread(void* arg) { + SYSTEM_INIT_ARGS* args = (SYSTEM_INIT_ARGS*)arg; + + DbgPrint("SystemInitThread: Thread started. Igniting the system...\n"); + + InitializeDrivers(args->bootInfo, args->hal); + + ResolveSystemImports(args->bootInfo); + + DbgPrint("SystemInitThread: Loading system DLLs...\n"); + LoadAndRegisterDll("syscall.dll"); + LoadAndRegisterDll("win32.dll"); + LoadAndRegisterDll("bink2w64.dll"); + + // Now that they are in memory and registered, we link them together + // (e.g., win32.dll resolving its imports against syscall.dll) + ResolveSystemImports(args->bootInfo); + + // Print memory status now that the system is fully alive + DbgPrint("Memory Status:\n"); + DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f)); + DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f)); + DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f)); + + args->hal->Screen.Clear(0x000000); + + // Launch User-Mode Shell (this will trigger AHCI & FAT32 which CAN safely block now) + PROCESS* shellProc = LaunchUserProcess(args->bootInfo->CommandLine); + + if (shellProc) { + KSTATUS ws = KeCreateKernelThread((uintptr_t)ProcessWatcherThread, + (uintptr_t)shellProc, + PriorityNormal, (THREAD**)0); + if (!KSUCCESS(ws)) { + DbgPrint("SystemInitThread: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws); + } + } + + MmFreePool(args); +} + // --------------------------------------------------------------------------- // Main Kernel Entry // --------------------------------------------------------------------------- @@ -381,7 +428,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) { .DebugEnabled = bootInfo->DebugEnabled }; KSTATUS s = HalInitialize(&halParams); - if (!KSUCCESS(s)) for (;;) __halt(); // there's nothing we can do. + if (!KSUCCESS(s)) while (true) __halt(); // there's nothing we can do. const HAL_API* hal = HalGetApi(); gKmainHal = hal; @@ -400,53 +447,37 @@ extern void KernelMain(BOOT_INFO* bootInfo) { // Dynamic Linking & Driver Execution // ResolveSystemImports(bootInfo); - InitializeDrivers(bootInfo, hal); + //InitializeDrivers(bootInfo, hal); // // Scheduler Prep & Status Update // KeInitializeScheduler(); - hal->Screen.Clear(0x000000); - DbgPrint("KernelMain: Loading system DLLs...\n"); - LoadAndRegisterDll("syscall.dll"); - LoadAndRegisterDll("win32.dll"); - LoadAndRegisterDll("bink2w64.dll"); + // Pack the arguments for the init thread + SYSTEM_INIT_ARGS* initArgs = MmAllocatePool(sizeof(SYSTEM_INIT_ARGS)); + initArgs->bootInfo = bootInfo; + initArgs->hal = hal; - // Now that they are in memory and registered, we link them together - // (e.g., win32.dll resolving its imports against syscall.dll) - ResolveSystemImports(bootInfo); - - // - // User-Mode Launch - // - PROCESS* shellProc = LaunchUserProcess(bootInfo->CommandLine); - - // Spawn a kernel thread that blocks on the process exit event and prints - // when it fires. The watcher takes ownership of the LaunchUserProcess - // reference so we do not hold a dangling pointer in KernelMain. - if (shellProc) { - KSTATUS ws = KeCreateKernelThread((uintptr_t)ProcessWatcherThread, - (uintptr_t)shellProc, - PriorityNormal, (THREAD**)0); - if (!KSUCCESS(ws)) { - DbgPrint("KernelMain: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws); - } - // Ownership of shellProc transferred to watcher; do not use it below. + // Spawn the Initialization Thread + THREAD* initThread = (THREAD*)0; + KSTATUS st = KeCreateKernelThread((uintptr_t)SystemInitThread, + (uintptr_t)initArgs, + PriorityNormal, &initThread); + + if (!KSUCCESS(st)) { + DbgPrint("KernelMain: PANIC - Failed to create SystemInitThread! (0x%x)\n", st); + _disable(); + for(;;) __halt(); } - // - // Ignite the System - // - DbgPrint("KernelMain: Starting scheduler... Handing over control.\n"); + DbgPrint("KernelMain: Handing over to scheduler...\n"); + + // Ignite the system _enable(); - DbgPrint("Memory Status:\n"); - DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f)); - DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f)); - DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f)); - - while (1) { - KeYield(); // nothing to do + // The Idle Loop: The CPU rests here whenever all other threads are waiting on events. + while (true) { + __halt(); } } diff --git a/code/kernel/ldr_image.c b/code/kernel/ldr_image.c index ef60de3..4e43fda 100644 --- a/code/kernel/ldr_image.c +++ b/code/kernel/ldr_image.c @@ -1,14 +1,10 @@ // ldr_image.c - PE32+ loader and export resolver -// -// At boot all images are already mapped by the bootloader; LdrLoadImage is -// used for any image loaded later (currently unused - all modules come in via -// BOOT_INFO). LdrGetExport scans the PE export directory of a registered module. -#include "ldr_image.h" -#include "boot_info.h" -#include "mm_virt.h" -#include "ke_debug.h" -#include #include +#include +#include +#include +#include +#include // --------------------------------------------------------------------------- // PE32+ structures (just what we need) diff --git a/code/kernel/ldr_image.h b/code/kernel/ldr_image.h index a6490bc..3b93aa8 100644 --- a/code/kernel/ldr_image.h +++ b/code/kernel/ldr_image.h @@ -1,10 +1,10 @@ #pragma once // ldr_image.h - minimal PE32+ loader // Loads an already-mapped PE image and resolves its imports. -#include "ke_status.h" #include #include #include +#include // Map a PE32+ image from 'buffer' into the kernel virtual address space. // On success, *baseOut receives the virtual base address. diff --git a/code/kernel/mm_phys.c b/code/kernel/mm_phys.c index 74c9425..30a6e25 100644 --- a/code/kernel/mm_phys.c +++ b/code/kernel/mm_phys.c @@ -6,10 +6,11 @@ // bootloader at PhysicalWindowBase (always 0xFFFF880000000000). // // Lock: a single spinlock guards Push/Pop. O(1) for both operations. -#include "mm_phys.h" -#include "ke_spinlock.h" #include #include +#include +#include +#include // --------------------------------------------------------------------------- // Physical-to-virtual translation via the direct map window. @@ -70,7 +71,7 @@ void MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) { } if (!stackPhys) { - for (;;) { __halt(); } + while (true) { __halt(); } } gFrameStack.Stack = (PHYSICAL_ADDRESS *)PhysToVirt(stackPhys); diff --git a/code/kernel/mm_phys.h b/code/kernel/mm_phys.h index fc0c8b9..cb314ea 100644 --- a/code/kernel/mm_phys.h +++ b/code/kernel/mm_phys.h @@ -3,7 +3,7 @@ // Stack allocator over 4 KB frames, seeded from MEMORY_MAP. #include #include -#include "memory_map.h" +#include #define PAGE_SIZE 4096ULL #define PAGE_MASK (~(PAGE_SIZE - 1)) diff --git a/code/kernel/mm_virt.c b/code/kernel/mm_virt.c index 8d89564..7ff6cc0 100644 --- a/code/kernel/mm_virt.c +++ b/code/kernel/mm_virt.c @@ -12,10 +12,10 @@ // User heap: // A single best-fit free list in the lower half. // Backed on demand from the PMM. No per-process heaps. -#include "mm_virt.h" -#include "ke_spinlock.h" -#include #include +#include +#include +#include // --------------------------------------------------------------------------- // Helpers - physical window access diff --git a/code/kernel/mm_virt.h b/code/kernel/mm_virt.h index 2bc0ff0..2a6d0fa 100644 --- a/code/kernel/mm_virt.h +++ b/code/kernel/mm_virt.h @@ -3,8 +3,8 @@ // Manages the single shared PML4, kernel pool, and user heap. #include #include -#include "mm_phys.h" -#include "ke_status.h" +#include +#include // --------------------------------------------------------------------------- // Page flags (x86-64 PTE bits) diff --git a/code/kernel/ob_object.c b/code/kernel/ob_object.c index 6b86572..f6b2535 100644 --- a/code/kernel/ob_object.c +++ b/code/kernel/ob_object.c @@ -1,8 +1,8 @@ // ob_object.c - executive object manager -#include "ob_object.h" -#include "mm_virt.h" // MmAllocatePool / MmFreePool #include #include +#include +#include OBJECT* ObAllocateObject(OBJECT_TYPE type, size_t size) { if (size < sizeof(OBJECT)) size = sizeof(OBJECT); @@ -13,7 +13,7 @@ OBJECT* ObAllocateObject(OBJECT_TYPE type, size_t size) { memset(obj, 0, size); obj->Type = type; obj->RefCount = 1; - obj->Destroy = (void *)0; /* caller fills this in */ + obj->Destroy = (void *)0; // caller fills this in return obj; } @@ -26,7 +26,7 @@ void ObDereferenceObject(OBJECT* object) { if (!object) return; long prev = _InterlockedDecrement(&object->RefCount); if (prev == 0) { - /* Reference count hit zero - run the destructor then free. */ + // Reference count hit zero - run the destructor then free. if (object->Destroy) { object->Destroy(object); } diff --git a/code/kernel/ob_object.h b/code/kernel/ob_object.h index 478d3b7..03acf1a 100644 --- a/code/kernel/ob_object.h +++ b/code/kernel/ob_object.h @@ -4,7 +4,7 @@ // The OBJECT header must be the first member of any derived struct. #include #include -#include "ke_status.h" +#include typedef enum OBJECT_TYPE { ObjectTypeProcess = 1, @@ -18,7 +18,7 @@ typedef enum OBJECT_TYPE { typedef struct OBJECT { OBJECT_TYPE Type; - volatile long RefCount; /* interlocked; long = 4 bytes on MSVC x64 */ + volatile long RefCount; // interlocked; long = 4 bytes on MSVC x64 void (*Destroy)(struct OBJECT* self); } OBJECT; diff --git a/code/libc.c b/code/libc.c index 08e106c..46f3e2c 100644 --- a/code/libc.c +++ b/code/libc.c @@ -1,6 +1,6 @@ // everyone needs a little libc -#include #include +#include char* strcpy(char* dest, const char* src) { char* d = dest; @@ -16,6 +16,35 @@ char* strncpy(char* dest, const char* src, size_t n) { return dest; } +char* strcat(char* dest, const char* src) { + char* ptr = dest; + while (*ptr != '\0') { + ptr++; + } + while (*src != '\0') { + *ptr = *src; + ptr++; + src++; + } + *ptr = '\0'; + return dest; +} + +char* strncat(char* dest, const char* src, size_t n) { + char* ptr = dest; + while (*ptr != '\0') { + ptr++; + } + while (n > 0 && *src != '\0') { + *ptr = *src; + ptr++; + src++; + n--; + } + *ptr = '\0'; + return dest; +} + int strcmp(const char* s1, const char* s2) { while (*s1 && *s1 == *s2) s1++, s2++; return (unsigned char)*s1 - (unsigned char)*s2; diff --git a/code/shell/shell.c b/code/shell/shell.c index 10c6661..40ca376 100644 --- a/code/shell/shell.c +++ b/code/shell/shell.c @@ -1,5 +1,6 @@ #include #include +#include #include // --------------------------------------------------------------------------- @@ -76,7 +77,7 @@ static void printUInt(uint32_t n) { static void readLine(HANDLE hIn, char* buf, int len) { int pos = 0; - while (1) { + while (true) { char c; DWORD nRead; ReadConsoleA(hIn, &c, 1, &nRead, 0); @@ -165,7 +166,7 @@ int main() { print("shell: ready\n"); - while (1) { + while (true) { print("> "); readLine(hIn, lineBuf, MAX_CMD); if (runCommand(trim(lineBuf))) diff --git a/code/video/rrCore.h b/code/video/rrCore.h index 0d830f0..93d5724 100644 --- a/code/video/rrCore.h +++ b/code/video/rrCore.h @@ -1329,7 +1329,7 @@ #define RAD_STATEMENT_END_TRUE \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ - } while(1) \ + } while(true) \ __pragma(warning(pop)) #else diff --git a/code/win32/kernel32.c b/code/win32/kernel32.c index 10b4a51..ce9e75f 100644 --- a/code/win32/kernel32.c +++ b/code/win32/kernel32.c @@ -103,7 +103,7 @@ __declspec(dllexport) void SetLastError(DWORD err) { // --------------------------------------------------------------------------- __declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) { SysExitProcess((int)code); - for (;;) ; // unreachable + while (true) ; // unreachable } // --------------------------------------------------------------------------- @@ -382,7 +382,7 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule, __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) { (void)hProcess; SysExitProcess((int)uExitCode); - for (;;) ; + while (true) ; } // STARTUPINFOA – only cb matters for our stub. @@ -839,7 +839,7 @@ __declspec(dllexport) void RaiseException(DWORD dwExceptionCode, DWORD dwExcepti (void)dwExceptionCode; (void)dwExceptionFlags; (void)nNumberOfArguments; (void)lpArguments; // Best-effort: nothing to unwind; just halt. - for (;;) ; + while (true) ; } typedef struct _EXCEPTION_POINTERS { void* ExceptionRecord; void* ContextRecord; } EXCEPTION_POINTERS; @@ -910,7 +910,7 @@ __declspec(dllexport) void RtlUnwindEx(void* TargetFrame, void* TargetIp, (void)TargetFrame; (void)TargetIp; (void)ExceptionRecord; (void)ReturnValue; (void)ContextRecord; (void)HistoryTable; // No real unwind possible; halt rather than corrupt the stack silently. - for (;;) ; + while (true) ; } // --------------------------------------------------------------------------- diff --git a/readme.md b/readme.md index 193e09f..2538372 100644 --- a/readme.md +++ b/readme.md @@ -64,7 +64,7 @@ drives 0–7. a drive only works if a filesystem registered on it. ## screenshots -![shell.exe playing a video with bink 2](image.png) +![video.exe running bink video 2](image.png) ## faq diff --git a/runa.bat b/runa.bat new file mode 100644 index 0000000..2027537 --- /dev/null +++ b/runa.bat @@ -0,0 +1 @@ +@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=raw,file=\\.\PhysicalDrive7,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -serial stdio diff --git a/runad.bat b/runad.bat new file mode 100644 index 0000000..fa2278a --- /dev/null +++ b/runad.bat @@ -0,0 +1 @@ +@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=raw,file=\\.\PhysicalDrive7,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown diff --git a/rund.bat b/rund.bat index fa2278a..4c5cff4 100644 --- a/rund.bat +++ b/rund.bat @@ -1 +1 @@ -@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=raw,file=\\.\PhysicalDrive7,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown +@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M.fd -drive format=raw,file=\\.\PhysicalDrive7 -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown