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:
1 parent
c13dd99150
commit
7f4052f41c
33 files changed
+6396
-735
No files matched your search
@@ -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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
@@ -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,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
File diff suppressed because it is too large.
Load diff
+84
-1
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
Reference in new issue
Block a user