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:
maksw committed 2026-05-03 19:35:48 +02:00
1 parent 7f4052f41c
commit 9fa2a7e0db
61 files changed
+1311 -456

No files matched your search

+1 -1
View File
@@ -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
+4 -4
View File
@@ -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 -2
View File
@@ -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)
+6 -2
View File
@@ -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) {
+7 -6
View File
@@ -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;
+4 -4
View File
@@ -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;
+1 -1
View File
@@ -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);
+112
View File
@@ -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;
}
+95
View File
@@ -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);
+2 -2
View File
@@ -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 -1
View File
@@ -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));
+7 -6
View File
@@ -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;
+4 -4
View File
@@ -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;
+5 -5
View File
@@ -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
+4 -4
View File
@@ -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
+6 -6
View File
@@ -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];
+4 -4
View File
@@ -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
+7 -6
View File
@@ -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 -1
View File
@@ -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 */
+6 -5
View File
@@ -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--;
+4 -4
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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);
+4 -4
View File
@@ -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);
+3 -3
View File
@@ -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
View File
@@ -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();
}
}
+5 -9
View File
@@ -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 -1
View File
@@ -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.
+4 -3
View File
@@ -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);
+1 -1
View File
@@ -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))
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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)
+4 -4
View File
@@ -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);
}
+2 -2
View File
@@ -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;