fix the return value of LdrLoadImage, make use of __declspec(dllexport), speed up blitting, add dummy TEB, redesign the syscall interface, fix stack alignent so movaps doesn't crash, implement a shitload of win32, implement a simple shell, add bink as video.exe 😎, minor kmain.c cleanup, HAL now supports power-off events, implemented \b in hal_screen.c, fix many deadlocks, add wait for exit event for processes, completely new error codes, small readme changes.

This commit is contained in:
maksw committed 2026-05-03 13:43:29 +02:00
1 parent c13dd99150
commit 7f4052f41c
33 files changed
+6396 -735

No files matched your search

+24
View File
@@ -1,6 +1,7 @@
// 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>
@@ -40,6 +41,12 @@ static void ProcessDestroy(OBJECT* obj) {
}
}
// Release the exit event.
if (proc->ExitEvent) {
ObDereferenceObject(&proc->ExitEvent->Header);
proc->ExitEvent = (EVENT *)0;
}
// Remove from global list.
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
ListRemove(&proc->GlobalLink);
@@ -76,6 +83,12 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
LIST_INIT(&proc->GlobalLink);
// Create the manual-reset exit event (initially unsignaled).
if (!KSUCCESS(KeEventCreate(EventManualReset, 0, &proc->ExitEvent))) {
ObDereferenceObject(&proc->Header);
return STATUS_NO_MEMORY;
}
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
ListInsertTail(&gProcessList, &proc->GlobalLink);
KeReleaseSpinlock(&gProcessListLock, flags);
@@ -90,11 +103,22 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
void KeExitProcess(PROCESS* process, int exitCode) {
if (!process) return;
process->ExitCode = exitCode;
// Wake any threads waiting for this process to finish.
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
// The last thread exiting via KeExitThread will drop the process refcount.
// This function is a policy hook; actual cleanup is in ProcessDestroy.
ObDereferenceObject(&process->Header);
}
// ---------------------------------------------------------------------------
// KeGetProcessExitCode
// ---------------------------------------------------------------------------
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut) {
if (!process || !exitCodeOut) return STATUS_INVALID_PARAMETER;
*exitCodeOut = process->ExitCode;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// Handle table
// ---------------------------------------------------------------------------
+7
View File
@@ -5,6 +5,7 @@
#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
@@ -26,11 +27,17 @@ typedef struct PROCESS {
SPINLOCK HandleLock;
int ExitCode;
/* Signaled (manual-reset) when the process exits.
Waiters block on this via KeWaitForEvent. */
EVENT* ExitEvent;
LIST_ENTRY GlobalLink; /* node in the global process list */
} PROCESS;
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
/* Handle table operations. */
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
+11 -4
View File
@@ -158,11 +158,11 @@ void KeYield(void) {
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
cur->SchedLockFlags = flags;
// Issue 4 Fix: Deleted misleading voluntary promotion from here.
THREAD* next = PickNext();
if (next == cur) {
if (cur != gIdleThread)
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead caller
// has already placed SchedulerLink elsewhere (or is discarding the thread).
if (cur != gIdleThread && cur->State == ThreadRunning)
ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
KeReleaseSpinlock(&gSchedLock, flags);
return;
@@ -171,8 +171,15 @@ void KeYield(void) {
next->State = ThreadRunning;
gCurrentThread = next;
if (cur != gIdleThread && cur->State == ThreadReady)
// Only re-queue the outgoing thread if it is still in the running state.
// If KeBlockThread set State = ThreadWaiting, SchedulerLink was already
// inserted into the sync object's wait list — inserting it here too would
// corrupt both lists simultaneously (same intrusive node in two lists).
// If KeExitThread set State = ThreadDead, the thread must not be re-queued.
if (cur != gIdleThread && cur->State == ThreadRunning) {
cur->State = ThreadReady;
ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
}
gOutgoingThread = cur;
KeSwitchContext(cur, next);
+25 -15
View File
@@ -6,20 +6,30 @@
typedef uint32_t KSTATUS;
#define STATUS_SUCCESS ((KSTATUS)0x00000000)
#define STATUS_FAILURE ((KSTATUS)0x00000001)
#define STATUS_INVALID_PARAMETER ((KSTATUS)0x00000002)
#define STATUS_NO_MEMORY ((KSTATUS)0x00000003)
#define STATUS_NOT_FOUND ((KSTATUS)0x00000004)
#define STATUS_ACCESS_DENIED ((KSTATUS)0x00000005)
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x00000006)
#define STATUS_NOT_SUPPORTED ((KSTATUS)0x00000007)
#define STATUS_TIMEOUT ((KSTATUS)0x00000008)
#define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x00000009)
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0000000A)
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0000000B)
#define STATUS_QUEUE_FULL ((KSTATUS)0x0000000C)
#define STATUS_QUEUE_EMPTY ((KSTATUS)0x0000000D)
#define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0000000E)
// --- General & Control (0x0000) ---
#define STATUS_SUCCESS ((KSTATUS)0x0000)
#define STATUS_FAILURE ((KSTATUS)0x0001)
#define STATUS_NOT_SUPPORTED ((KSTATUS)0x0002)
#define STATUS_TIMEOUT ((KSTATUS)0x0003)
// --- Validation & Parameters (0x0010) ---
#define STATUS_INVALID_PARAMETER ((KSTATUS)0x0010)
#define STATUS_INVALID_HANDLE ((KSTATUS)0x0011)
#define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x0012)
#define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0013)
// --- Resource & Access (0x0020) ---
#define STATUS_NO_MEMORY ((KSTATUS)0x0020)
#define STATUS_ACCESS_DENIED ((KSTATUS)0x0021)
#define STATUS_NOT_FOUND ((KSTATUS)0x0022)
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x0023)
// --- State & Lifecycle (0x0030) ---
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0030)
#define STATUS_QUEUE_FULL ((KSTATUS)0x0031)
#define STATUS_QUEUE_EMPTY ((KSTATUS)0x0032)
// --- Hardware & External (0x0040) ---
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0040)
#define KSUCCESS(s) ((s) == STATUS_SUCCESS)
+55 -1
View File
@@ -1,6 +1,6 @@
; ke_switch.asm - context switch and thread entry stubs (MASM64 / ML64)
;
; Generated struct offsets (regenerate with gen_offsets.exe if structs change):
; Struct offsets:
; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8)
; THREAD.SavedContext offset = 88
; THREAD.TssRsp0 offset = 104
@@ -8,6 +8,7 @@
;
THREAD_SavedContext EQU 88
THREAD_TssRsp0 EQU 104
THREAD_Teb EQU 112
TSS_RSP0_OFFSET EQU 4
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
@@ -33,6 +34,16 @@ KeSwitchContext PROC
; rsp = to->SavedContext
mov rsp, [rdx + THREAD_SavedContext]
; Update MSR_GS_BASE (0xC0000101) with to->Teb
mov rax, [rdx + THREAD_Teb] ; Fetch the incoming TEB pointer
mov ecx, 0C0000101h ; IA32_GS_BASE MSR
mov r8, rdx ; Save RDX ('to' thread) as WRMSR clobbers EDX
mov rdx, rax ; Copy TEB pointer to RDX
shr rdx, 32 ; EDX = High 32 bits of TEB
; EAX = Low 32 bits of TEB (upper 32 bits ignored by CPU)
wrmsr ; Write to MSR
mov rdx, r8 ; Restore RDX
; Update TSS.RSP0 so that the next interrupt/syscall uses the
; incoming thread's kernel stack top.
mov rax, [rdx + THREAD_TssRsp0]
@@ -65,6 +76,8 @@ KeSwitchContext ENDP
; We load RCX from the stack (first arg per MS x64), then IRETQ.
; -------------------------------------------------------------------------
EXTERN KeSchedulerPostSwitch : PROC
EXTERN KeCurrentThread : PROC
EXTERN KeExitThread : PROC
KeThreadEntry PROC
sub rsp, 32 ; Reserve shadow space
@@ -74,4 +87,45 @@ KeThreadEntry PROC
iretq ; -> ring 3
KeThreadEntry ENDP
; -------------------------------------------------------------------------
; KeKernelThreadEntry - landing pad for kernel-mode threads created by
; KeCreateKernelThread. Never touches ring 3; no IRETQ.
;
; Stack layout when we arrive here (above the KCONTEXT already popped by
; KeSwitchContext's ret):
; [rsp+00] fn ptr (kernel function: void fn(void*))
; [rsp+08] arg ptr
;
; We pop both into non-volatile registers so KeSchedulerPostSwitch can
; clobber volatile ones freely, then call fn(arg). On return we call
; KeExitThread so the thread is cleanly destroyed.
; -------------------------------------------------------------------------
KeKernelThreadEntry PROC
pop rbx ; rbx = fn ptr (non-volatile, survives the call below)
pop r12 ; r12 = arg (non-volatile)
sub rsp, 32
call KeSchedulerPostSwitch ; release scheduler spinlock, reap dead threads
add rsp, 32
mov rcx, r12 ; first arg = arg
sub rsp, 32
call rbx ; fn(arg) -- still ring 0
add rsp, 32
; Thread function returned -- exit cleanly.
sub rsp, 32
call KeCurrentThread ; rax = current THREAD*
add rsp, 32
mov rcx, rax ; arg1 = thread*
xor edx, edx ; arg2 = exitCode 0
sub rsp, 32
call KeExitThread
add rsp, 32
; KeExitThread -> KeYield -> KeSwitchContext never returns here, but
; the assembler needs a defined end.
@@: jmp @B
KeKernelThreadEntry ENDP
END
+1 -1
View File
@@ -1,4 +1,4 @@
; ke_syscall.asm - INT 0x2E kernel entry (MASM64)
; ke_syscall.asm - INT 0x40 kernel entry (MASM64)
;
; On entry the CPU has pushed (ring-3 -> ring-0 transition):
; SS, RSP, RFLAGS, CS, RIP (on the kernel stack via TSS.RSP0)
+665 -276
View File
File diff suppressed because it is too large. Load diff
+84 -1
View File
@@ -66,7 +66,18 @@ KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
return STATUS_NO_MEMORY;
}
thread->UserStackBase = ustack;
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE;
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8; // i don't know why, i don't want to know why
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness
// this magic number will hunt me in my dreams
// Initialize the dummy TEB
// offset 0x10 is StackLimit, which __chkstk checks.
uint64_t* teb = (uint64_t*)ustack;
teb[0] = 0; // ExceptionList (GS:0x00)
teb[1] = 0; // StackBase (GS:0x08)
teb[2] = 0; // StackLimit (GS:0x10) - __chkstk will see 0 and safely exit
thread->Teb = teb;
thread->UserEntry = entry;
thread->UserArg = arg;
@@ -108,6 +119,78 @@ void KeExitThread(THREAD* thread, int code) {
KeYield();
}
// ---------------------------------------------------------------------------
// KeCreateKernelThread
// ---------------------------------------------------------------------------
// Kernel threads never go to ring 3: they have no user stack, no TEB, and
// their entry stub (KeKernelThreadEntry) calls the function directly then
// calls KeExitThread instead of IRETQ-ing.
//
// Stack layout built here (from KernelStackTop downward):
// [ksp - 8 ] arg <-- popped by KeKernelThreadEntry as r12
// [ksp - 16] fn <-- popped by KeKernelThreadEntry as rbx
// [ksp - 16 - sizeof(KCONTEXT)] KCONTEXT { .Rip = KeKernelThreadEntry }
// ---------------------------------------------------------------------------
extern void KeKernelThreadEntry(void);
KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out) {
// Kernel threads are owned by the idle process. We need a PROCESS* for
// the owner field; grab it from the idle/init thread that already exists.
THREAD* idleThread = KeCurrentThread(); // safe to call before scheduler starts
PROCESS* owner = idleThread ? idleThread->Owner : (PROCESS*)0;
if (!owner) return STATUS_INVALID_PARAMETER;
if (priority >= PriorityCount) return STATUS_INVALID_PARAMETER;
THREAD* thread = (THREAD*)ObAllocateObject(ObjectTypeThread, sizeof(THREAD));
if (!thread) return STATUS_NO_MEMORY;
thread->Header.Destroy = ThreadDestroy;
thread->ThreadId = _InterlockedExchangeAdd((volatile long*)&gNextThreadId, 1);
thread->State = ThreadReady;
thread->Priority = priority;
thread->QuantaConsumed = 0;
thread->WaitObject = (OBJECT*)0;
thread->SchedLockFlags = 0x202ULL;
thread->UserStackBase = (void*)0; // no user stack
thread->Teb = (void*)0; // no TEB; GS_BASE will be 0 in kernel ctx
void* kstack = MmAllocatePool(KSTACK_SIZE);
if (!kstack) {
ObDereferenceObject(&thread->Header);
return STATUS_NO_MEMORY;
}
memset(kstack, 0, KSTACK_SIZE);
thread->KernelStackBase = kstack;
thread->KernelStackTop = (uintptr_t)kstack + KSTACK_SIZE;
thread->TssRsp0 = thread->KernelStackTop;
// Build the initial stack: arg, fn, then KCONTEXT.
uintptr_t* ksp = (uintptr_t*)thread->KernelStackTop;
*(--ksp) = arg; // popped second (-> r12)
*(--ksp) = fn; // popped first (-> rbx)
ksp -= sizeof(KCONTEXT) / sizeof(uintptr_t);
KCONTEXT* ctx = (KCONTEXT*)ksp;
memset(ctx, 0, sizeof(KCONTEXT));
ctx->Rip = (uint64_t)KeKernelThreadEntry;
thread->SavedContext = ctx;
// Register with the owning process.
ObReferenceObject(&owner->Header);
thread->Owner = owner;
uintptr_t flags = KeAcquireSpinlock(&owner->HandleLock);
ListInsertTail(&owner->ThreadList, &thread->ProcessLink);
owner->ThreadCount++;
KeReleaseSpinlock(&owner->HandleLock, flags);
if (out) *out = thread;
KeReadyThread(thread);
return STATUS_SUCCESS;
}
void KeBlockThread(THREAD* thread, OBJECT* waitObject) {
thread->State = ThreadWaiting;
thread->WaitObject = waitObject;
+13 -3
View File
@@ -46,7 +46,7 @@ typedef struct THREAD {
LIST_ENTRY SchedulerLink; /* node in run queue or wait list */
// Kernel stack - never PAGE_USER, pointed to by TssRsp0.
void* KernelStackBase;
void* KernelStackBase;
uintptr_t KernelStackTop;
// Saved context pointer - set by KeSwitchContext.
@@ -58,17 +58,27 @@ typedef struct THREAD {
// Updated on every context switch so the CPU loads the right kernel stack.
uintptr_t TssRsp0;
// Thread Environment Block
void* Teb;
// Ring-3 entry point and argument for newly created threads.
uintptr_t UserEntry;
uintptr_t UserArg;
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
} THREAD;
KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out);
// Create a kernel-mode-only thread. fn is called directly in ring 0 with
// arg; when fn returns the thread exits automatically. No user stack or TEB
// is allocated. The thread is owned by the idle process and runs at the
// given priority.
KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out);
// Mark a thread dead and deschedule it permanently.
void KeExitThread(THREAD* thread, int code);
+64 -32
View File
@@ -74,6 +74,10 @@ typedef struct {
typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *);
// HAL pointer cached at boot so kernel threads can reach it without
// threading a parameter through every call chain.
static const HAL_API* gKmainHal;
// ---------------------------------------------------------------------------
// Phase Helper: Resolve Imports for all registered modules
// ---------------------------------------------------------------------------
@@ -210,7 +214,7 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
}
// ---------------------------------------------------------------------------
// Phase Helper: Load a DLL from disk and register it as a system module
// Phase Helper: Load a DLL from disk and register it
// ---------------------------------------------------------------------------
static void LoadAndRegisterDll(const char* filename) {
KSTATUS s;
@@ -219,8 +223,8 @@ static void LoadAndRegisterDll(const char* filename) {
// Read the DLL from disk
s = IoOpenFile(filename, &f);
if (s != STATUS_SUCCESS) {
DbgPrint("KernelMain: Failed to open critical system DLL: %s\n", filename);
_disable(); while(1) __halt();
DbgPrint("KernelMain: Failed to open DLL: %s\n", filename);
return;
}
uint64_t size = IoGetFileSize(f);
@@ -229,7 +233,7 @@ static void LoadAndRegisterDll(const char* filename) {
if (!KSUCCESS(IoReadFile(f, mem, size, &got))) {
DbgPrint("KernelMain: Failed to read %s\n", filename);
IoCloseFile(f);
_disable(); while(1) __halt();
return;
}
IoCloseFile(f);
@@ -239,7 +243,7 @@ static void LoadAndRegisterDll(const char* filename) {
s = LdrLoadImage(mem, size, &mappedBase, true);
if (!KSUCCESS(s)) {
DbgPrint("KernelMain: Failed to map %s\n", filename);
_disable(); while(1) __halt();
return;
}
MmFreePool(mem); // Free the raw file buffer, we only need the mapped image now
@@ -248,18 +252,20 @@ static void LoadAndRegisterDll(const char* filename) {
}
// ---------------------------------------------------------------------------
// Phase Helper: Load and launch the init process from the filesystem
// Phase Helper: Load and launch a process from the filesystem.
// Returns the new PROCESS* with one reference owned by the caller.
// Returns NULL on failure.
// ---------------------------------------------------------------------------
static void LaunchInitProcess(BOOT_INFO* bootInfo) {
static PROCESS* LaunchUserProcess(const char* filePath) {
KSTATUS s;
FILE_OBJECT* f;
// Read the executable file into a raw buffer
s = IoOpenFile(bootInfo->CommandLine, &f);
s = IoOpenFile(filePath, &f);
DbgPrint("KernelMain: IoOpenFile: %d\n", s);
if (s != STATUS_SUCCESS) {
DbgPrint("Failed to load file: \"%s\"!\n", bootInfo->CommandLine);
_disable(); while(1) __halt();
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
return (PROCESS*)0;
}
uint64_t size = IoGetFileSize(f);
@@ -278,29 +284,46 @@ static void LaunchInitProcess(BOOT_INFO* bootInfo) {
PROCESS* proc;
THREAD* thread;
s = KeCreateProcess("init", &proc);
s = KeCreateProcess(filePath, &proc);
if (!KSUCCESS(s)) {
DbgPrint("Failed to create init process. System halted.\n");
_disable(); while(1) __halt();
DbgPrint("Failed to create process. System halted.\n");
return (PROCESS*)0;
}
DbgPrint("Created init process (PID %u) with command line: %s\n", proc->ProcessId, bootInfo->CommandLine);
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
// Parse the raw PE file and map it into executable memory
uintptr_t entryPoint = 0;
s = LdrLoadImage(mem, size, &entryPoint, true);
uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &imageBase, true);
if (!KSUCCESS(s)) {
DbgPrint("KernelMain: LdrLoadImage failed with status: %d\n", s);
_disable(); while(1) __halt();
}
DbgPrint("KernelMain: PE Image mapped! Entry point at 0x%llX\n", (void*)entryPoint);
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
DbgPrint("KernelMain: 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 init thread. Status: %d\n", s);
_disable(); while(1) __halt();
DbgPrint("KernelMain: Failed to create thread. Status: %d\n", s);
}
DbgPrint("KernelMain: Thread created and queued!\n");
// Caller owns the reference returned by KeCreateProcess.
return proc;
}
// ---------------------------------------------------------------------------
// Kernel thread: wait for a process to exit, then log a message and shut down.
// Takes ownership of the PROCESS* reference passed as arg; releases it on exit.
// ---------------------------------------------------------------------------
static void ProcessWatcherThread(void* arg) {
PROCESS* proc = (PROCESS*)arg;
KeWaitForEvent(proc->ExitEvent);
DbgPrint("KernelMain: Process \"%s\" (PID %u) exited with code %d.\n",
proc->Name, proc->ProcessId, proc->ExitCode);
ObDereferenceObject(&proc->Header);
DbgPrint("KernelMain: Shutting down.\n");
gKmainHal->Power.Shutdown(); // does not return
}
static void PrintProcessList() {
@@ -360,6 +383,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
KSTATUS s = HalInitialize(&halParams);
if (!KSUCCESS(s)) for (;;) __halt(); // there's nothing we can do.
const HAL_API* hal = HalGetApi();
gKmainHal = hal;
KeInitDebug(hal->Screen.PrintChar, bootInfo->DebugEnabled); // Enable debug
@@ -382,11 +406,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
// Scheduler Prep & Status Update
//
KeInitializeScheduler();
// hal->Screen.Clear(0x000000);
hal->Screen.Clear(0x000000);
DbgPrint("KernelMain: 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)
@@ -395,7 +420,20 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
//
// User-Mode Launch
//
LaunchInitProcess(bootInfo);
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.
}
//
// Ignite the System
@@ -403,18 +441,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
DbgPrint("KernelMain: Starting scheduler... Handing over control.\n");
_enable();
float freeMB = (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f);
float kernelMB = (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f);
float userMB = (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f);
DbgPrint("Memory Status:\n");
DbgPrint(" -> Free (PMM): %.2f MB\n", freeMB);
DbgPrint(" -> Kernel Pool: %.2f MB\n", kernelMB);
DbgPrint(" -> User Heap: %.2f MB\n", userMB);
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) {
//DbgPrint("KernelMain: Idle loop. Free memory: %.2f MB.\n",
//(float)(MmFreeFrameCount() * PAGE_SIZE) / (1024.0f * 1024.0f));
__halt(); // nothing to do
KeYield(); // nothing to do
}
}
+1 -1
View File
@@ -299,6 +299,6 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
KSTATUS status = LdrResolveImports((uintptr_t)mem);
if (status != STATUS_SUCCESS) return status;
*baseOut = (uintptr_t)mem + opt->AddressOfEntryPoint;
*baseOut = (uintptr_t)mem;
return STATUS_SUCCESS;
}
+108 -32
View File
@@ -1,37 +1,113 @@
#pragma once
// syscall_nr.h - system call numbers
// Shared between kernel (ke_syscall.c) and user mode (syscall.dll).
// Keep in sync with the dispatch table in ke_syscall.c.
// Keep in sync with gSyscallTable in ke_syscall.c and SYS_MAX below.
//
// Calling convention (INT 0x40, MS x64):
// RAX = syscall number
// RCX, RDX, R8, R9 = args 0-3
// R10, R11 = args 4-5 (loaded by SYSCALL_STUB6 in syscall_stubs.asm)
// Return value in RAX (KSTATUS or data).
//
// Handle semantics:
// Every kernel object (file, mutex, semaphore, event, queue) is reference-
// counted. SYS_CLOSE_HANDLE is the ONE way to close any handle regardless
// of the object type behind it. KeCloseHandle calls ObDereferenceObject,
// which fires the type-specific Destroy callback (e.g. FileDestroy)
// automatically when the refcount reaches zero. There is no separate
// SYS_CLOSE_FILE - that was redundant.
#define SYS_EXIT_THREAD 0
#define SYS_EXIT_PROCESS 1
#define SYS_SPAWN 2
#define SYS_SLEEP 3
#define SYS_YIELD 4
#define SYS_ALLOC 5
#define SYS_FREE 6
#define SYS_CREATE_THREAD 7
#define SYS_OPEN_HANDLE 8
#define SYS_CLOSE_HANDLE 9
#define SYS_DBG_PRINT 10
#define SYS_MUTEX_CREATE 11
#define SYS_MUTEX_ACQUIRE 12
#define SYS_MUTEX_RELEASE 13
#define SYS_SEM_CREATE 14
#define SYS_SEM_WAIT 15
#define SYS_SEM_SIGNAL 16
#define SYS_EVENT_CREATE 17
#define SYS_EVENT_SET 18
#define SYS_EVENT_WAIT 19
#define SYS_QUEUE_CREATE 20
#define SYS_QUEUE_SEND 21
#define SYS_QUEUE_RECV 22
#define SYS_SCREEN_BLIT 23
#define SYS_GET_TICK 24
#define SYS_OPEN 25
#define SYS_READ 26
#define SYS_FCLOSE 27
#define SYS_FSEEK 28
#define SYS_FSIZE 29
// ---------------------------------------------------------------------------
// Process & Thread
// ---------------------------------------------------------------------------
#define SYS_EXIT_PROCESS 0 // (int exitCode) -> void
#define SYS_EXIT_THREAD 1 // (int exitCode) -> void
#define SYS_CREATE_THREAD 2 // (uint64_t entry, uint64_t arg, uint32_t priority, uint32_t* tidOut) -> KSTATUS
#define SYS_GET_CURRENT_TID 3 // () -> uint32_t
#define SYS_GET_CURRENT_PID 4 // () -> uint32_t
#define SYS_YIELD 5 // () -> void
#define SYS_SLEEP 6 // (uint32_t ms) -> void
#define SYS_SET_THREAD_PRIORITY 7 // (uint32_t priority) -> KSTATUS
#define SYS_MAX 30
// ---------------------------------------------------------------------------
// Memory
// ---------------------------------------------------------------------------
#define SYS_ALLOC 8 // (size_t size) -> void* (NULL on failure)
#define SYS_FREE 9 // (void* ptr, size_t size) -> void
#define SYS_REALLOC 10 // (void* ptr, size_t newSize) -> void*
// ---------------------------------------------------------------------------
// Synchronisation Objects
// ---------------------------------------------------------------------------
#define SYS_MUTEX_CREATE 11 // (uint32_t* handleOut) -> KSTATUS
#define SYS_MUTEX_ACQUIRE 12 // (uint32_t handle) -> KSTATUS
#define SYS_MUTEX_TRY_ACQUIRE 13 // (uint32_t handle) -> KSTATUS
#define SYS_MUTEX_RELEASE 14 // (uint32_t handle) -> KSTATUS
#define SYS_SEM_CREATE 15 // (int32_t initial, int32_t max, uint32_t* handleOut) -> KSTATUS
#define SYS_SEM_WAIT 16 // (uint32_t handle) -> KSTATUS
#define SYS_SEM_TRY_WAIT 17 // (uint32_t handle) -> KSTATUS
#define SYS_SEM_SIGNAL 18 // (uint32_t handle) -> void
#define SYS_EVENT_CREATE 19 // (uint32_t type, uint32_t initialState, uint32_t* handleOut) -> KSTATUS
#define SYS_EVENT_SIGNAL 20 // (uint32_t handle) -> void
#define SYS_EVENT_CLEAR 21 // (uint32_t handle) -> void
#define SYS_EVENT_WAIT 22 // (uint32_t handle) -> KSTATUS
// ---------------------------------------------------------------------------
// Message Queues
// ---------------------------------------------------------------------------
#define SYS_QUEUE_CREATE 23 // (uint32_t* handleOut) -> KSTATUS
#define SYS_QUEUE_SEND 24 // (uint32_t handle, const QUEUE_MESSAGE* msg, uint32_t blocking) -> KSTATUS
#define SYS_QUEUE_RECV 25 // (uint32_t handle, QUEUE_MESSAGE* msg, uint32_t blocking) -> KSTATUS
// ---------------------------------------------------------------------------
// Handle Management
// ---------------------------------------------------------------------------
#define SYS_CLOSE_HANDLE 26 // (uint32_t handle) -> void
// ---------------------------------------------------------------------------
// File I/O
// (Close a file handle via SYS_CLOSE_HANDLE, same as any other object.)
// ---------------------------------------------------------------------------
#define SYS_OPEN_FILE 27 // (const char* path, uint32_t* handleOut) -> KSTATUS
#define SYS_READ_FILE 28 // (uint32_t handle, void* buf, size_t size, size_t* bytesReadOut) -> KSTATUS
#define SYS_SEEK_FILE 29 // (uint32_t handle, uint64_t offset) -> KSTATUS
#define SYS_GET_FILE_SIZE 30 // (uint32_t handle) -> uint64_t
// ---------------------------------------------------------------------------
// Screen / Graphics
// ---------------------------------------------------------------------------
#define SYS_SCREEN_BLIT 31 // (int x, int y, int w, int h, const uint32_t* pixels) -> void
#define SYS_SCREEN_CLEAR 32 // (uint32_t color) -> void
#define SYS_SCREEN_GET_SIZE 33 // (int* widthOut, int* heightOut) -> void
#define SYS_SCREEN_PUT_CHAR 34 // (char c, int x, int y, uint32_t fg, uint32_t bg) -> void
// ---------------------------------------------------------------------------
// Keyboard
// ---------------------------------------------------------------------------
#define SYS_KB_POLL 35 // () -> uint32_t (1 if key available, 0 otherwise)
#define SYS_KB_READ_CHAR 36 // () -> char (blocks, yields while waiting)
#define SYS_KB_READ_SCANCODE 37 // () -> uint8_t (blocks, yields while waiting)
// ---------------------------------------------------------------------------
// Timer
// ---------------------------------------------------------------------------
#define SYS_GET_TICK 38 // () -> uint64_t (milliseconds since boot)
// ---------------------------------------------------------------------------
// Debug
// ---------------------------------------------------------------------------
#define SYS_DEBUG_PRINT 39 // (const char* str) -> void
// ---------------------------------------------------------------------------
// Process launch & lifecycle
// ---------------------------------------------------------------------------
#define SYS_CREATE_PROCESS 40 // (const char* path, uint32_t* handleOut) -> KSTATUS
#define SYS_WAIT_FOR_PROCESS 41 // (uint32_t handle) -> KSTATUS
#define SYS_GET_PROCESS_EXIT_CODE 42 // (uint32_t handle, int* exitCodeOut) -> KSTATUS
// ---------------------------------------------------------------------------
// Sentinel
// ---------------------------------------------------------------------------
#define SYS_MAX 43