colonel.exe 🤑, refactor all include directives, AHCI is now interrupt driven, upgrades to IDE, implemented I/O Request Packets, fix numerous keyboard issues, change all for (;;) and while (1) to while (true), DbgPrint guarded by spinlock, fix KeSignalEvent, major KernelMain refactor, added SystemInitThread because of new blocking I/O, replace memcpy manouvers with strncpy and strncat, add: run for ide, runa for ahci, rund for debug and ide, runad for debug and ahci.
This commit is contained in:
1 parent
7f4052f41c
commit
9fa2a7e0db
61 files changed
+1311
-456
No files matched your search
+9
-8
@@ -15,12 +15,13 @@
|
||||
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
int _fltused = 0;
|
||||
#include <boot_info.h>
|
||||
#include <memory_map.h>
|
||||
#include <ini_parser.h>
|
||||
|
||||
#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");
|
||||
|
||||
+316
-77
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <io_driver.h>
|
||||
#include <mm_phys.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_irq.h>
|
||||
#include <ke_irp.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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) {
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <io_driver.h>
|
||||
#include <hal_api.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
+109
-83
@@ -1,19 +1,18 @@
|
||||
// ide.c - PIO-mode IDE driver (legacy 28-bit LBA)
|
||||
#include <io_driver.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <io_driver.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_sched.h>
|
||||
|
||||
// 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;
|
||||
|
||||
+7
-3
@@ -3,7 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "ke_status.h"
|
||||
#include <ke_status.h>
|
||||
|
||||
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 {
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
// hal_apic.c - Local APIC + I/O APIC
|
||||
#include "hal_apic.h"
|
||||
#include "hal_api.h"
|
||||
#include <hal_apic.h>
|
||||
#include <hal_api.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern HAL_API gApi;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
// hal_debug.h - HAL debug output function
|
||||
// Do not include outside HAL.
|
||||
|
||||
void HalDbgPrint(const char* fmt, ...);
|
||||
+62
-12
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <hal_api.h>
|
||||
#include <io_driver.h>
|
||||
#include <ke_irp.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
+260
-33
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
#include <hal_api.h>
|
||||
#include <hal_apic.h>
|
||||
#include <hal_debug.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
// hal_main.c - HAL initialisation and HalGetApi
|
||||
#include "hal_api.h"
|
||||
#include "hal_apic.h"
|
||||
#include <hal_api.h>
|
||||
#include <hal_apic.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include "printf.h"
|
||||
#include <printf.h>
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
// hal_pci.c - PCI configuration space via Mechanism 1 port I/O
|
||||
#include "hal_api.h"
|
||||
#include <stdint.h>
|
||||
#include <hal_api.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <hal_api.h>
|
||||
|
||||
static uint32_t* gFb = (uint32_t *)0;
|
||||
static int gWidth = 0;
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <intrin.h>
|
||||
#include <hal_api.h>
|
||||
#include <hal_apic.h>
|
||||
#include <hal_debug.h>
|
||||
|
||||
#define LAPIC_TIMER_LVT 0x320
|
||||
#define LAPIC_TIMER_INIT 0x380
|
||||
|
||||
+30
-7
@@ -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 <stdint.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_status.h>
|
||||
#include <io_file.h>
|
||||
|
||||
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);
|
||||
+1
-1
@@ -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 <string.h>
|
||||
#include <ini_parser.h>
|
||||
|
||||
static inline int IsWs(char c) { return c == ' ' || c == '\t' || c == '\r'; }
|
||||
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include "memory_map.h"
|
||||
#include <memory_map.h>
|
||||
|
||||
#define BOOT_INFO_MAGIC 0x424F4F54494E464FULL /* 'BOOTINFO' */
|
||||
#define MAX_BOOT_MODULES 32
|
||||
|
||||
@@ -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 <string.h>
|
||||
#include <ke_list.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
#define MAX_DRIVES 8
|
||||
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Filesystem driver vtable - filled in by fat32.sys (or any future fs driver)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// ke_debug.c - debugging utilities
|
||||
#include "hal_api.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "printf.h"
|
||||
#include <hal_api.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <printf.h>
|
||||
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) {
|
||||
|
||||
@@ -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 <stdbool.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
typedef enum EVENT_TYPE { EventAutoReset, EventManualReset } EVENT_TYPE;
|
||||
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ke_idt.h>
|
||||
|
||||
// External assembly procedures
|
||||
extern void KeFlushGdt(void* pGdtr, uint16_t codeSel, uint16_t dataSel, uint16_t tssSel);
|
||||
|
||||
@@ -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 <ke_irp.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ob_object.h>
|
||||
#include <io_driver.h>
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
// 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);
|
||||
@@ -1,6 +1,6 @@
|
||||
// ke_irq.c — IRQ dispatch, kernel-side
|
||||
#include "ke_irq.h"
|
||||
#include "hal_api.h"
|
||||
#include <ke_irq.h>
|
||||
#include <hal_api.h>
|
||||
|
||||
#define MAX_IRQS 24
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "hal_api.h"
|
||||
#include <stdint.h>
|
||||
#include <hal_api.h>
|
||||
|
||||
void KeIrqInitialize(const HAL_API *hal);
|
||||
void KeIrqRegister(uint8_t irq, void (*handler)(void));
|
||||
|
||||
@@ -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 <stdbool.h>
|
||||
#include <ke_mutex.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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 <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
typedef struct MUTEX {
|
||||
OBJECT Header;
|
||||
|
||||
@@ -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 <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_event.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global process list
|
||||
|
||||
@@ -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 <ob_object.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_event.h>
|
||||
|
||||
#define PROCESS_NAME_MAX 64
|
||||
#define HANDLE_TABLE_SIZE 256
|
||||
|
||||
@@ -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 <string.h>
|
||||
#include <ke_queue.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
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];
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
#define QUEUE_MESSAGE_SIZE 64 /* bytes, fixed */
|
||||
#define QUEUE_DEPTH 256
|
||||
|
||||
@@ -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 <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <mm_virt.h>
|
||||
|
||||
#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) {
|
||||
|
||||
@@ -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 <ke_thread.h>
|
||||
|
||||
#define QUANTUM_MS 10 /* base time slice in milliseconds */
|
||||
|
||||
|
||||
@@ -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 <stdbool.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
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--;
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
typedef struct SEMAPHORE {
|
||||
OBJECT Header;
|
||||
|
||||
@@ -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)
|
||||
|
||||
+16
-16
@@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#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>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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) ;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hal_api.h"
|
||||
#include <hal_api.h>
|
||||
|
||||
// Set the HAL API pointer for syscalls
|
||||
void KeSyscallSetHal(const HAL_API* hal);
|
||||
|
||||
@@ -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 <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
|
||||
static uint32_t gNextThreadId = 1;
|
||||
extern void KeThreadEntry(void);
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_process.h>
|
||||
|
||||
#define KSTACK_SIZE (64 * 1024) /* 64 KB kernel stack */
|
||||
#define USTACK_SIZE (512 * 1024) /* 512 KB user stack */
|
||||
|
||||
+114
-83
@@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#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>
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -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 <string.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ldr_image.h>
|
||||
#include <boot_info.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PE32+ structures (just what we need)
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
// Map a PE32+ image from 'buffer' into the kernel virtual address space.
|
||||
// On success, *baseOut receives the virtual base address.
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <mm_phys.h>
|
||||
#include <ke_spinlock.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Stack allocator over 4 KB frames, seeded from MEMORY_MAP.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "memory_map.h"
|
||||
#include <memory_map.h>
|
||||
|
||||
#define PAGE_SIZE 4096ULL
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
|
||||
@@ -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 <string.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_spinlock.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers - physical window access
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// Manages the single shared PML4, kernel pool, and user heap.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "mm_phys.h"
|
||||
#include "ke_status.h"
|
||||
#include <mm_phys.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Page flags (x86-64 PTE bits)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// ob_object.c - executive object manager
|
||||
#include "ob_object.h"
|
||||
#include "mm_virt.h" // MmAllocatePool / MmFreePool
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ob_object.h>
|
||||
#include <mm_virt.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// The OBJECT header must be the first member of any derived struct.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "ke_status.h"
|
||||
#include <ke_status.h>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
+30
-1
@@ -1,6 +1,6 @@
|
||||
// everyone needs a little libc
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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;
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -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)))
|
||||
|
||||
+1
-1
@@ -1329,7 +1329,7 @@
|
||||
#define RAD_STATEMENT_END_TRUE \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable:4127)) \
|
||||
} while(1) \
|
||||
} while(true) \
|
||||
__pragma(warning(pop))
|
||||
|
||||
#else
|
||||
|
||||
@@ -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) ;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user