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
+6394 -733

No files matched your search

Binary file not shown.
Binary file not shown.
+17 -21
View File
@@ -14,7 +14,8 @@ mkdir "obj\hal" 2>nul
mkdir "obj\syscall" 2>nul mkdir "obj\syscall" 2>nul
mkdir "obj\win32" 2>nul mkdir "obj\win32" 2>nul
mkdir "obj\drivers" 2>nul mkdir "obj\drivers" 2>nul
mkdir "obj\shell" 2>nul mkdir "obj\shell" 2>nul
mkdir "obj\video" 2>nul
mkdir "bin" 2>nul mkdir "bin" 2>nul
set CFLAGS=/nologo /W4 /WX /Od /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi /Zi /std:c17 /D_CRT_SECURE_NO_WARNINGS /I"%SRC%\kernel" /I"%SRC%\hal" set CFLAGS=/nologo /W4 /WX /Od /GS- /Gs999999 /Gz /Gy /EHsc- /GR- /Oi /Zi /std:c17 /D_CRT_SECURE_NO_WARNINGS /I"%SRC%\kernel" /I"%SRC%\hal"
@@ -77,16 +78,7 @@ link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:KernelMain /OUT:"bin\kernel.exe" ^
:: ---- Compile syscall.dll ---- :: ---- Compile syscall.dll ----
echo Compiling syscall.dll... echo Compiling syscall.dll...
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^ link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^
/OUT:"bin\syscall.dll" ^ /OUT:"bin\syscall.dll" obj\syscall\stubs.obj || exit /b 1
/EXPORT:SysYield /EXPORT:SysAlloc /EXPORT:SysFree ^
/EXPORT:SysCreateThread /EXPORT:SysCloseHandle ^
/EXPORT:SysMutexCreate /EXPORT:SysMutexAcquire /EXPORT:SysMutexRelease ^
/EXPORT:SysSemCreate /EXPORT:SysSemWait /EXPORT:SysSemSignal ^
/EXPORT:SysEventCreate /EXPORT:SysEventSet /EXPORT:SysEventWait ^
/EXPORT:SysQueueCreate /EXPORT:SysQueueSend /EXPORT:SysQueueRecv ^
/EXPORT:SysScreenBlit /EXPORT:SysGetTick /EXPORT:SysSleep ^
/EXPORT:SysExitThread /EXPORT:SysExitProcess /EXPORT:SysDbgPrint ^
obj\syscall\stubs.obj || exit /b 1
:: ---- Compile win32.dll ---- :: ---- Compile win32.dll ----
echo Compiling win32.dll... echo Compiling win32.dll...
@@ -94,15 +86,7 @@ for %%f in (%SRC%\win32\*.c) do (
cl %CFLAGS% /c /Fo"obj\win32\%%~nf.obj" "%%f" || exit /b 1 cl %CFLAGS% /c /Fo"obj\win32\%%~nf.obj" "%%f" || exit /b 1
) )
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^ link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^
/OUT:"bin\win32.dll" ^ /OUT:"bin\win32.dll" bin\syscall.lib obj\libc.obj obj\win32\*.obj || exit /b 1
/EXPORT:HeapAlloc /EXPORT:HeapFree /EXPORT:HeapReAlloc ^
/EXPORT:CreateThread /EXPORT:ExitProcess /EXPORT:ExitThread ^
/EXPORT:Sleep /EXPORT:SwitchToThread ^
/EXPORT:CreateMutex /EXPORT:ReleaseMutex /EXPORT:WaitForSingleObject ^
/EXPORT:CreateEvent /EXPORT:SetEvent /EXPORT:CloseHandle ^
/EXPORT:GetTickCount /EXPORT:GetTickCount64 ^
/EXPORT:BitBlt /EXPORT:GetMessage /EXPORT:DispatchMessage /EXPORT:OutputDebugStringA ^
bin\syscall.lib obj\win32\*.obj || exit /b 1
:: ---- Compile drivers ---- :: ---- Compile drivers ----
echo Compiling drivers... echo Compiling drivers...
@@ -137,6 +121,17 @@ echo Linking shell.exe...
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^ link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
/OUT:"bin\shell.exe" /PDB:"bin\shell.pdb" bin\win32.lib obj\libc.obj obj\shell\*.obj || exit /b 1 /OUT:"bin\shell.exe" /PDB:"bin\shell.pdb" bin\win32.lib obj\libc.obj obj\shell\*.obj || exit /b 1
:: ---- Compile video player ----
echo Compiling video...
for %%f in (%SRC%\video\*.c) do (
cl %CFLAGS% /c /Fo"obj\video\%%~nf.obj" "%%f" || exit /b 1
)
:: ---- Link video.exe ----
echo Linking video.exe...
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
/OUT:"bin\video.exe" /PDB:"bin\video.pdb" bin\win32.lib obj\libc.obj %SRC%\video\bink2w64.lib obj\video\*.obj || exit /b 1
:: ---- Copy to ESP layout ---- :: ---- Copy to ESP layout ----
echo Assembling ESP... echo Assembling ESP...
mkdir "%DEST%\drivers" 2>nul mkdir "%DEST%\drivers" 2>nul
@@ -150,7 +145,8 @@ copy /y "bin\ide.sys" "%DEST%\drivers\"
copy /y "bin\ahci.sys" "%DEST%\drivers\" copy /y "bin\ahci.sys" "%DEST%\drivers\"
copy /y "bin\fat32.sys" "%DEST%\drivers\" copy /y "bin\fat32.sys" "%DEST%\drivers\"
copy /y "bin\usb.sys" "%DEST%\drivers\" copy /y "bin\usb.sys" "%DEST%\drivers\"
copy /y "bin\shell.exe" "%DEST%\" copy /y "bin\shell.exe" "%DEST%\"
copy /y "bin\video.exe" "%DEST%\"
copy /y "%SRC%\boot.ini" "%DEST%\" copy /y "%SRC%\boot.ini" "%DEST%\"
copy /y "%SRC%\drivers\drivers.ini" "%DEST%\drivers\" copy /y "%SRC%\drivers\drivers.ini" "%DEST%\drivers\"
+1 -1
View File
@@ -697,7 +697,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
UINT64 start = desc->PhysicalStart; UINT64 start = desc->PhysicalStart;
UINT64 end = start + (desc->NumberOfPages * PAGE_SIZE); UINT64 end = start + (desc->NumberOfPages * PAGE_SIZE);
// CRITICAL FIX: Add 2 pages (8KB) of padding to the end of the region. // Add 2 pages (8KB) of padding to the end of the region.
// This catches ACPI tables that cross page boundaries into unmapped // This catches ACPI tables that cross page boundaries into unmapped
// memory because of buggy UEFI size reporting. // memory because of buggy UEFI size reporting.
end += 2 * PAGE_SIZE; end += 2 * PAGE_SIZE;
+7
View File
@@ -60,6 +60,12 @@ typedef struct HAL_INTC {
void (*SendEoi)(void); void (*SendEoi)(void);
} HAL_INTC; } HAL_INTC;
typedef struct HAL_POWER {
// ACPI S5 soft-off. Disables interrupts, writes SLP_EN to PM1a/b_CNT,
// and halts if firmware doesn't respond. Does not return.
void (*Shutdown)(void);
} HAL_POWER;
typedef struct HAL_API { typedef struct HAL_API {
uint32_t Version; uint32_t Version;
void *(*PhysToVirt)(uint64_t physAddr); void *(*PhysToVirt)(uint64_t physAddr);
@@ -69,6 +75,7 @@ typedef struct HAL_API {
HAL_TIMER Timer; HAL_TIMER Timer;
HAL_PCI Pci; HAL_PCI Pci;
HAL_INTC Intc; HAL_INTC Intc;
HAL_POWER Power;
} HAL_API; } HAL_API;
typedef struct HAL_INIT_PARAMS { typedef struct HAL_INIT_PARAMS {
+1 -1
View File
@@ -1,6 +1,7 @@
// hal_keyboard.c - PS/2 keyboard, interrupt-driven, 256-entry ring buffer // hal_keyboard.c - PS/2 keyboard, interrupt-driven, 256-entry ring buffer
#include "hal_api.h" #include "hal_api.h"
#include "hal_apic.h" #include "hal_apic.h"
#include "hal_debug.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <intrin.h> #include <intrin.h>
@@ -48,7 +49,6 @@ static uint8_t KbdReadScancode(void) {
} }
static char KbdReadChar(void) { static char KbdReadChar(void) {
if (gHead == gTail) return 0;
uint8_t sc = KbdReadScancode(); uint8_t sc = KbdReadScancode();
if (sc < 128) return gScanToAscii[sc]; if (sc < 128) return gScanToAscii[sc];
return 0; return 0;
+126 -9
View File
@@ -81,6 +81,12 @@ static uintptr_t gLapicPhys = 0xFEE00000;
static uintptr_t gIoapicPhys = 0xFEC00000; static uintptr_t gIoapicPhys = 0xFEC00000;
static uintptr_t gHpetPhys = 0xFED00000; static uintptr_t gHpetPhys = 0xFED00000;
// ACPI power-off state (filled in by ParseFadt / ScanDsdtForS5)
static uint16_t gPm1aCntBlk = 0;
static uint16_t gPm1bCntBlk = 0;
static uint8_t gSlpTypA = 5; // S5 SLP_TYP for PM1a; 5 is correct for QEMU/most VMs
static uint8_t gSlpTypB = 5; // S5 SLP_TYP for PM1b
int debugEnabled = 0; int debugEnabled = 0;
int _fltused; // fuckass int _fltused; // fuckass
static void hal_putchar(char c) { static void hal_putchar(char c) {
@@ -99,6 +105,101 @@ static void *HalPhysToVirt(uint64_t phys) {
return (void *)(phys + gPhysBase); return (void *)(phys + gPhysBase);
} }
// FADT (Fixed ACPI Description Table) - partial layout up to PM1b_CNT_BLK.
// We only need offsets 40 (DSDT ptr), 64 (PM1a_CNT_BLK), 68 (PM1b_CNT_BLK).
// The full table is ~276 bytes (ACPI 2.0+); we never access beyond offset 68.
#pragma pack(push, 1)
typedef struct {
ACPISDTHeader Header; // 0 (36 bytes)
uint32_t FirmwareCtrl; // 36
uint32_t Dsdt; // 40 - physical addr of DSDT
uint8_t Reserved1; // 44
uint8_t PreferredPmProfile; // 45
uint16_t SciInt; // 46
uint32_t SmiCmd; // 48
uint8_t AcpiEnable; // 52
uint8_t AcpiDisable; // 53
uint8_t S4BiosReq; // 54
uint8_t PstateCnt; // 55
uint32_t Pm1aEvtBlk; // 56
uint32_t Pm1bEvtBlk; // 60
uint32_t Pm1aCntBlk; // 64 - I/O port for PM1a control register
uint32_t Pm1bCntBlk; // 68 - I/O port for PM1b control register
} FADT;
#pragma pack(pop)
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values.
// The encoding looks like:
// 0x08 '_' 'S' '5' '_' ; NameOp + "_S5_"
// 0x12 <PkgLen> ; PackageOp + encoded length
// <NumElements> ; usually 0x04
// 0x0A <slp_typ_a> ; BytePrefix + value (or 0x00 for zero)
// 0x0A <slp_typ_b>
static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) {
for (uint32_t i = 0; i + 9 < len; i++) {
// Match NameOp + "_S5_" + PackageOp
if (aml[i] != 0x08) continue;
if (aml[i+1] != 0x5F || aml[i+2] != 0x53 ||
aml[i+3] != 0x35 || aml[i+4] != 0x5F) continue;
if (aml[i+5] != 0x12) continue;
// PkgLength encoding: low 6 bits = byte count (0 → only this byte);
// bits [7:6] = number of following bytes in the length field.
uint8_t pkgByte = aml[i+6];
uint32_t pkgExtra = pkgByte >> 6; // 0..3 extra bytes
uint32_t j = i + 7 + pkgExtra; // skip PkgLen field
if (j + 2 >= len) break;
j++; // skip NumElements byte
// SLP_TYP_A
if (aml[j] == 0x0A && j + 1 < len) {
gSlpTypA = aml[j + 1]; j += 2;
} else if (aml[j] == 0x00) {
gSlpTypA = 0; j++;
} else {
break; // unexpected encoding - keep defaults
}
// SLP_TYP_B
if (j < len && aml[j] == 0x0A && j + 1 < len)
gSlpTypB = aml[j + 1];
else if (j < len && aml[j] == 0x00)
gSlpTypB = 0;
HalDbgPrint("HAL: _S5_ SLP_TYP_A=0x%02x SLP_TYP_B=0x%02x\n",
gSlpTypA, gSlpTypB);
return;
}
HalDbgPrint("HAL: _S5_ not found in DSDT, using defaults (A=0x%02x B=0x%02x)\n",
gSlpTypA, gSlpTypB);
}
static int validate_checksum(void* table, uint32_t length) {
uint8_t sum = 0;
uint8_t* bytes = (uint8_t*)table;
for (uint32_t i = 0; i < length; i++) {
sum += bytes[i];
}
return sum == 0;
}
static void ParseFadt(const FADT* fadt) {
gPm1aCntBlk = (uint16_t)fadt->Pm1aCntBlk;
gPm1bCntBlk = (uint16_t)fadt->Pm1bCntBlk;
HalDbgPrint("HAL: FADT PM1a_CNT=0x%04x PM1b_CNT=0x%04x DSDT=0x%08x\n",
gPm1aCntBlk, gPm1bCntBlk, fadt->Dsdt);
if (fadt->Dsdt) {
const ACPISDTHeader* dsdt =
(const ACPISDTHeader*)((uintptr_t)fadt->Dsdt + gPhysBase);
if (memcmp(dsdt->Signature, "DSDT", 4) == 0 &&
validate_checksum((void*)dsdt, dsdt->Length)) {
const uint8_t* aml = (const uint8_t*)(dsdt + 1);
ScanDsdtForS5(aml, dsdt->Length - (uint32_t)sizeof(ACPISDTHeader));
}
}
}
static void ParseMadt(MADT* madt) { static void ParseMadt(MADT* madt) {
gLapicPhys = madt->LocalAPICAddress; gLapicPhys = madt->LocalAPICAddress;
uint8_t* p = (uint8_t *)(madt + 1); uint8_t* p = (uint8_t *)(madt + 1);
@@ -112,15 +213,6 @@ static void ParseMadt(MADT* madt) {
} }
} }
static int validate_checksum(void* table, uint32_t length) {
uint8_t sum = 0;
uint8_t* bytes = (uint8_t*)table;
for (uint32_t i = 0; i < length; i++) {
sum += bytes[i];
}
return sum == 0;
}
static void WalkAcpi(uintptr_t rsdpPhys) { static void WalkAcpi(uintptr_t rsdpPhys) {
if (!rsdpPhys) return; if (!rsdpPhys) return;
@@ -189,6 +281,10 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
if (memcmp(header->Signature, "APIC", 4) == 0) { if (memcmp(header->Signature, "APIC", 4) == 0) {
ParseMadt((MADT*)header); ParseMadt((MADT*)header);
} }
// Parse FADT (signature "FACP") for PM1 control ports and DSDT pointer
else if (memcmp(header->Signature, "FACP", 4) == 0) {
ParseFadt((const FADT*)header);
}
// Parse HPET // Parse HPET
else if (memcmp(header->Signature, "HPET", 4) == 0) { else if (memcmp(header->Signature, "HPET", 4) == 0) {
// HPET base address block is located at offset 44 // HPET base address block is located at offset 44
@@ -198,6 +294,25 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
} }
} }
// ---------------------------------------------------------------------------
// HalShutdown - ACPI S5 soft power-off. Does not return.
// ---------------------------------------------------------------------------
#include <intrin.h>
static void HalShutdown(void) {
_disable();
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10)
uint16_t valA = (uint16_t)((gSlpTypA << 10) | (1 << 13));
uint16_t valB = (uint16_t)((gSlpTypB << 10) | (1 << 13));
if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA);
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
// Firmware should have powered off by now. If it didn't, halt.
for (;;) __halt();
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) { KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
if (gInitialised) return STATUS_ALREADY_EXISTS; if (gInitialised) return STATUS_ALREADY_EXISTS;
@@ -236,6 +351,8 @@ KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
gApi.Intc.UnmaskIrq = HalApicUnmaskIrq; gApi.Intc.UnmaskIrq = HalApicUnmaskIrq;
gApi.Intc.SendEoi = HalApicSendEoi; gApi.Intc.SendEoi = HalApicSendEoi;
gApi.Power.Shutdown = HalShutdown;
gApi.Version = HAL_API_VERSION; gApi.Version = HAL_API_VERSION;
gInitialised = 1; gInitialised = 1;
+50 -4
View File
@@ -45,9 +45,47 @@ static void ScreenPutPixel(int x, int y, uint32_t color) {
} }
static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) { static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) {
for (int row = 0; row < h; row++) // Calculate clipping bounds
for (int col = 0; col < w; col++) int startX = (x < 0) ? 0 : x;
PutPixelRaw(x + col, y + row, pixels[row * w + col]); int startY = (y < 0) ? 0 : y;
int endX = (x + w > gWidth) ? gWidth : (x + w);
int endY = (y + h > gHeight) ? gHeight : (y + h);
int drawW = endX - startX;
int drawH = endY - startY;
// If the image is entirely off-screen, do nothing
if (drawW <= 0 || drawH <= 0) {
return;
}
// Adjust the source pointer if the image was clipped on the top or left
int srcOffsetX = startX - x;
int srcOffsetY = startY - y;
// Calculate bytes to copy per row
size_t bytesPerRow = (size_t)drawW * sizeof(uint32_t);
// If the width we are drawing exactly matches the physical screen stride,
// AND it matches the original source image width, memory is perfectly contiguous.
if (drawW == gStride && drawW == w) {
uint32_t* dst = gFb + (startY * gStride) + startX;
const uint32_t* src = pixels + (srcOffsetY * w) + srcOffsetX;
memcpy(dst, src, bytesPerRow * drawH);
return;
}
// Copy row-by-row
for (int row = 0; row < drawH; row++) {
// Destination in the actual framebuffer (accounting for stride)
uint32_t* dst = gFb + ((startY + row) * gStride) + startX;
// Source from the provided pixel buffer
const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX;
memcpy(dst, src, bytesPerRow);
}
} }
static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; } static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; }
@@ -68,7 +106,8 @@ static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) {
for (int row = 0; row < 16; row++) { for (int row = 0; row < 16; row++) {
uint8_t bits = glyph[row]; uint8_t bits = glyph[row];
for (int col = 0; col < 8; col++) { for (int col = 0; col < 8; col++) {
uint32_t color = (bits & (1 << col)) ? fg : bg; // i don't know why, i don't want to know why, i shouldn't have to wonder why but the font is backwards unless we do this terriblness uint32_t color = (bits & (1 << col)) ? fg : bg; // i don't know why, i don't want to know why
// i shouldn't have to wonder why but the font is backwards unless we do this terriblness
PutPixelRaw(x + col, y + row, color); PutPixelRaw(x + col, y + row, color);
} }
} }
@@ -88,6 +127,13 @@ static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
gCursorY++; gCursorY++;
} else if (c == '\r') { } else if (c == '\r') {
gCursorX = 0; gCursorX = 0;
} else if (c == '\b') {
if (gCursorX > 0) {
gCursorX--;
} else if (gCursorY > 0) {
gCursorY--;
gCursorX = cols - 1;
}
} else { } else {
ScreenPutChar(c, gCursorX * FONT_W, gCursorY * FONT_H, fg, bg); ScreenPutChar(c, gCursorX * FONT_W, gCursorY * FONT_H, fg, bg);
if (++gCursorX >= cols) { gCursorX = 0; gCursorY++; } if (++gCursorX >= cols) { gCursorX = 0; gCursorY++; }
+24
View File
@@ -1,6 +1,7 @@
// ke_process.c - process model // ke_process.c - process model
#include "ke_process.h" #include "ke_process.h"
#include "ke_thread.h" #include "ke_thread.h"
#include "ke_event.h"
#include "mm_virt.h" #include "mm_virt.h"
#include "ob_object.h" #include "ob_object.h"
#include <string.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. // Remove from global list.
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock); uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
ListRemove(&proc->GlobalLink); ListRemove(&proc->GlobalLink);
@@ -76,6 +83,12 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
LIST_INIT(&proc->GlobalLink); 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); uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
ListInsertTail(&gProcessList, &proc->GlobalLink); ListInsertTail(&gProcessList, &proc->GlobalLink);
KeReleaseSpinlock(&gProcessListLock, flags); KeReleaseSpinlock(&gProcessListLock, flags);
@@ -90,11 +103,22 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
void KeExitProcess(PROCESS* process, int exitCode) { void KeExitProcess(PROCESS* process, int exitCode) {
if (!process) return; if (!process) return;
process->ExitCode = exitCode; 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. // The last thread exiting via KeExitThread will drop the process refcount.
// This function is a policy hook; actual cleanup is in ProcessDestroy. // This function is a policy hook; actual cleanup is in ProcessDestroy.
ObDereferenceObject(&process->Header); 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 // Handle table
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+7
View File
@@ -5,6 +5,7 @@
#include "ob_object.h" #include "ob_object.h"
#include "ke_list.h" #include "ke_list.h"
#include "ke_spinlock.h" #include "ke_spinlock.h"
#include "ke_event.h"
#define PROCESS_NAME_MAX 64 #define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256 #define HANDLE_TABLE_SIZE 256
@@ -26,11 +27,17 @@ typedef struct PROCESS {
SPINLOCK HandleLock; SPINLOCK HandleLock;
int ExitCode; 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 */ LIST_ENTRY GlobalLink; /* node in the global process list */
} PROCESS; } PROCESS;
KSTATUS KeCreateProcess(const char* name, PROCESS **out); KSTATUS KeCreateProcess(const char* name, PROCESS **out);
void KeExitProcess(PROCESS* process, int exitCode); void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
/* Handle table operations. */ /* Handle table operations. */
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut); 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); uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
cur->SchedLockFlags = flags; cur->SchedLockFlags = flags;
// Issue 4 Fix: Deleted misleading voluntary promotion from here.
THREAD* next = PickNext(); THREAD* next = PickNext();
if (next == cur) { 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); ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
KeReleaseSpinlock(&gSchedLock, flags); KeReleaseSpinlock(&gSchedLock, flags);
return; return;
@@ -171,8 +171,15 @@ void KeYield(void) {
next->State = ThreadRunning; next->State = ThreadRunning;
gCurrentThread = next; 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); ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
}
gOutgoingThread = cur; gOutgoingThread = cur;
KeSwitchContext(cur, next); KeSwitchContext(cur, next);
+25 -15
View File
@@ -6,20 +6,30 @@
typedef uint32_t KSTATUS; typedef uint32_t KSTATUS;
#define STATUS_SUCCESS ((KSTATUS)0x00000000) // --- General & Control (0x0000) ---
#define STATUS_FAILURE ((KSTATUS)0x00000001) #define STATUS_SUCCESS ((KSTATUS)0x0000)
#define STATUS_INVALID_PARAMETER ((KSTATUS)0x00000002) #define STATUS_FAILURE ((KSTATUS)0x0001)
#define STATUS_NO_MEMORY ((KSTATUS)0x00000003) #define STATUS_NOT_SUPPORTED ((KSTATUS)0x0002)
#define STATUS_NOT_FOUND ((KSTATUS)0x00000004) #define STATUS_TIMEOUT ((KSTATUS)0x0003)
#define STATUS_ACCESS_DENIED ((KSTATUS)0x00000005)
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x00000006) // --- Validation & Parameters (0x0010) ---
#define STATUS_NOT_SUPPORTED ((KSTATUS)0x00000007) #define STATUS_INVALID_PARAMETER ((KSTATUS)0x0010)
#define STATUS_TIMEOUT ((KSTATUS)0x00000008) #define STATUS_INVALID_HANDLE ((KSTATUS)0x0011)
#define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x00000009) #define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x0012)
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0000000A) #define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0013)
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0000000B)
#define STATUS_QUEUE_FULL ((KSTATUS)0x0000000C) // --- Resource & Access (0x0020) ---
#define STATUS_QUEUE_EMPTY ((KSTATUS)0x0000000D) #define STATUS_NO_MEMORY ((KSTATUS)0x0020)
#define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0000000E) #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) #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) ; 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) ; sizeof(OBJECT) = 16 (type:4, refcount:4, destroy:8)
; THREAD.SavedContext offset = 88 ; THREAD.SavedContext offset = 88
; THREAD.TssRsp0 offset = 104 ; THREAD.TssRsp0 offset = 104
@@ -8,6 +8,7 @@
; ;
THREAD_SavedContext EQU 88 THREAD_SavedContext EQU 88
THREAD_TssRsp0 EQU 104 THREAD_TssRsp0 EQU 104
THREAD_Teb EQU 112
TSS_RSP0_OFFSET EQU 4 TSS_RSP0_OFFSET EQU 4
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
@@ -33,6 +34,16 @@ KeSwitchContext PROC
; rsp = to->SavedContext ; rsp = to->SavedContext
mov rsp, [rdx + THREAD_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 ; Update TSS.RSP0 so that the next interrupt/syscall uses the
; incoming thread's kernel stack top. ; incoming thread's kernel stack top.
mov rax, [rdx + THREAD_TssRsp0] mov rax, [rdx + THREAD_TssRsp0]
@@ -65,6 +76,8 @@ KeSwitchContext ENDP
; We load RCX from the stack (first arg per MS x64), then IRETQ. ; We load RCX from the stack (first arg per MS x64), then IRETQ.
; ------------------------------------------------------------------------- ; -------------------------------------------------------------------------
EXTERN KeSchedulerPostSwitch : PROC EXTERN KeSchedulerPostSwitch : PROC
EXTERN KeCurrentThread : PROC
EXTERN KeExitThread : PROC
KeThreadEntry PROC KeThreadEntry PROC
sub rsp, 32 ; Reserve shadow space sub rsp, 32 ; Reserve shadow space
@@ -74,4 +87,45 @@ KeThreadEntry PROC
iretq ; -> ring 3 iretq ; -> ring 3
KeThreadEntry ENDP 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 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): ; On entry the CPU has pushed (ring-3 -> ring-0 transition):
; SS, RSP, RFLAGS, CS, RIP (on the kernel stack via TSS.RSP0) ; 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; return STATUS_NO_MEMORY;
} }
thread->UserStackBase = ustack; 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->UserEntry = entry;
thread->UserArg = arg; thread->UserArg = arg;
@@ -108,6 +119,78 @@ void KeExitThread(THREAD* thread, int code) {
KeYield(); 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) { void KeBlockThread(THREAD* thread, OBJECT* waitObject) {
thread->State = ThreadWaiting; thread->State = ThreadWaiting;
thread->WaitObject = waitObject; thread->WaitObject = waitObject;
+13 -3
View File
@@ -46,7 +46,7 @@ typedef struct THREAD {
LIST_ENTRY SchedulerLink; /* node in run queue or wait list */ LIST_ENTRY SchedulerLink; /* node in run queue or wait list */
// Kernel stack - never PAGE_USER, pointed to by TssRsp0. // Kernel stack - never PAGE_USER, pointed to by TssRsp0.
void* KernelStackBase; void* KernelStackBase;
uintptr_t KernelStackTop; uintptr_t KernelStackTop;
// Saved context pointer - set by KeSwitchContext. // 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. // Updated on every context switch so the CPU loads the right kernel stack.
uintptr_t TssRsp0; uintptr_t TssRsp0;
// Thread Environment Block
void* Teb;
// Ring-3 entry point and argument for newly created threads. // Ring-3 entry point and argument for newly created threads.
uintptr_t UserEntry; uintptr_t UserEntry;
uintptr_t UserArg; uintptr_t UserArg;
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
} THREAD; } THREAD;
KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg, KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
THREAD_PRIORITY priority, THREAD **out); 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. // Mark a thread dead and deschedule it permanently.
void KeExitThread(THREAD* thread, int code); void KeExitThread(THREAD* thread, int code);
+64 -32
View File
@@ -74,6 +74,10 @@ typedef struct {
typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API *); 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 // 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) { static void LoadAndRegisterDll(const char* filename) {
KSTATUS s; KSTATUS s;
@@ -219,8 +223,8 @@ static void LoadAndRegisterDll(const char* filename) {
// Read the DLL from disk // Read the DLL from disk
s = IoOpenFile(filename, &f); s = IoOpenFile(filename, &f);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
DbgPrint("KernelMain: Failed to open critical system DLL: %s\n", filename); DbgPrint("KernelMain: Failed to open DLL: %s\n", filename);
_disable(); while(1) __halt(); return;
} }
uint64_t size = IoGetFileSize(f); uint64_t size = IoGetFileSize(f);
@@ -229,7 +233,7 @@ static void LoadAndRegisterDll(const char* filename) {
if (!KSUCCESS(IoReadFile(f, mem, size, &got))) { if (!KSUCCESS(IoReadFile(f, mem, size, &got))) {
DbgPrint("KernelMain: Failed to read %s\n", filename); DbgPrint("KernelMain: Failed to read %s\n", filename);
IoCloseFile(f); IoCloseFile(f);
_disable(); while(1) __halt(); return;
} }
IoCloseFile(f); IoCloseFile(f);
@@ -239,7 +243,7 @@ static void LoadAndRegisterDll(const char* filename) {
s = LdrLoadImage(mem, size, &mappedBase, true); s = LdrLoadImage(mem, size, &mappedBase, true);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("KernelMain: Failed to map %s\n", filename); 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 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; KSTATUS s;
FILE_OBJECT* f; FILE_OBJECT* f;
// Read the executable file into a raw buffer // Read the executable file into a raw buffer
s = IoOpenFile(bootInfo->CommandLine, &f); s = IoOpenFile(filePath, &f);
DbgPrint("KernelMain: IoOpenFile: %d\n", s); DbgPrint("KernelMain: IoOpenFile: %d\n", s);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
DbgPrint("Failed to load file: \"%s\"!\n", bootInfo->CommandLine); DbgPrint("Failed to load file: \"%s\"!\n", filePath);
_disable(); while(1) __halt(); return (PROCESS*)0;
} }
uint64_t size = IoGetFileSize(f); uint64_t size = IoGetFileSize(f);
@@ -278,29 +284,46 @@ static void LaunchInitProcess(BOOT_INFO* bootInfo) {
PROCESS* proc; PROCESS* proc;
THREAD* thread; THREAD* thread;
s = KeCreateProcess("init", &proc); s = KeCreateProcess(filePath, &proc);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("Failed to create init process. System halted.\n"); DbgPrint("Failed to create process. System halted.\n");
_disable(); while(1) __halt(); 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 // Parse the raw PE file and map it into executable memory
uintptr_t entryPoint = 0; uintptr_t imageBase = 0;
s = LdrLoadImage(mem, size, &entryPoint, true); s = LdrLoadImage(mem, size, &imageBase, true);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("KernelMain: LdrLoadImage failed with status: %d\n", 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 // Create the main thread, pointing it to the PE Entry Point
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread); s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("KernelMain: Failed to create init thread. Status: %d\n", s); DbgPrint("KernelMain: Failed to create thread. Status: %d\n", s);
_disable(); while(1) __halt();
} }
DbgPrint("KernelMain: Thread created and queued!\n"); 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() { static void PrintProcessList() {
@@ -360,6 +383,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
KSTATUS s = HalInitialize(&halParams); KSTATUS s = HalInitialize(&halParams);
if (!KSUCCESS(s)) for (;;) __halt(); // there's nothing we can do. if (!KSUCCESS(s)) for (;;) __halt(); // there's nothing we can do.
const HAL_API* hal = HalGetApi(); const HAL_API* hal = HalGetApi();
gKmainHal = hal;
KeInitDebug(hal->Screen.PrintChar, bootInfo->DebugEnabled); // Enable debug KeInitDebug(hal->Screen.PrintChar, bootInfo->DebugEnabled); // Enable debug
@@ -382,11 +406,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
// Scheduler Prep & Status Update // Scheduler Prep & Status Update
// //
KeInitializeScheduler(); KeInitializeScheduler();
// hal->Screen.Clear(0x000000); hal->Screen.Clear(0x000000);
DbgPrint("KernelMain: Loading system DLLs...\n"); DbgPrint("KernelMain: Loading system DLLs...\n");
LoadAndRegisterDll("syscall.dll"); LoadAndRegisterDll("syscall.dll");
LoadAndRegisterDll("win32.dll"); LoadAndRegisterDll("win32.dll");
LoadAndRegisterDll("bink2w64.dll");
// Now that they are in memory and registered, we link them together // Now that they are in memory and registered, we link them together
// (e.g., win32.dll resolving its imports against syscall.dll) // (e.g., win32.dll resolving its imports against syscall.dll)
@@ -395,7 +420,20 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
// //
// User-Mode Launch // 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 // Ignite the System
@@ -403,18 +441,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
DbgPrint("KernelMain: Starting scheduler... Handing over control.\n"); DbgPrint("KernelMain: Starting scheduler... Handing over control.\n");
_enable(); _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("Memory Status:\n");
DbgPrint(" -> Free (PMM): %.2f MB\n", freeMB); DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f));
DbgPrint(" -> Kernel Pool: %.2f MB\n", kernelMB); DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f));
DbgPrint(" -> User Heap: %.2f MB\n", userMB); DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
while (1) { while (1) {
//DbgPrint("KernelMain: Idle loop. Free memory: %.2f MB.\n", KeYield(); // nothing to do
//(float)(MmFreeFrameCount() * PAGE_SIZE) / (1024.0f * 1024.0f));
__halt(); // 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); KSTATUS status = LdrResolveImports((uintptr_t)mem);
if (status != STATUS_SUCCESS) return status; if (status != STATUS_SUCCESS) return status;
*baseOut = (uintptr_t)mem + opt->AddressOfEntryPoint; *baseOut = (uintptr_t)mem;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
+108 -32
View File
@@ -1,37 +1,113 @@
#pragma once #pragma once
// syscall_nr.h - system call numbers // syscall_nr.h - system call numbers
// Shared between kernel (ke_syscall.c) and user mode (syscall.dll). // 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 // Process & Thread
#define SYS_SPAWN 2 // ---------------------------------------------------------------------------
#define SYS_SLEEP 3 #define SYS_EXIT_PROCESS 0 // (int exitCode) -> void
#define SYS_YIELD 4 #define SYS_EXIT_THREAD 1 // (int exitCode) -> void
#define SYS_ALLOC 5 #define SYS_CREATE_THREAD 2 // (uint64_t entry, uint64_t arg, uint32_t priority, uint32_t* tidOut) -> KSTATUS
#define SYS_FREE 6 #define SYS_GET_CURRENT_TID 3 // () -> uint32_t
#define SYS_CREATE_THREAD 7 #define SYS_GET_CURRENT_PID 4 // () -> uint32_t
#define SYS_OPEN_HANDLE 8 #define SYS_YIELD 5 // () -> void
#define SYS_CLOSE_HANDLE 9 #define SYS_SLEEP 6 // (uint32_t ms) -> void
#define SYS_DBG_PRINT 10 #define SYS_SET_THREAD_PRIORITY 7 // (uint32_t priority) -> KSTATUS
#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
#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
+158 -28
View File
@@ -1,47 +1,177 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <string.h>
void BitBlt(int x, int y, int w, int h, const uint32_t* pixels); // ---------------------------------------------------------------------------
void* HeapAlloc(void* heap, unsigned long flags, size_t size); // Win32 imports
int HeapFree(void* heap, unsigned long flags, void* mem); // ---------------------------------------------------------------------------
_Noreturn void ExitProcess(unsigned int code); _Noreturn void ExitProcess(unsigned int code);
_Noreturn void ExitThread(unsigned long code);
void OutputDebugStringA(const char* s); void OutputDebugStringA(const char* s);
int main() { typedef void* HANDLE;
OutputDebugStringA("Hello from the shell!\n"); typedef unsigned long DWORD;
int width = 300; typedef int BOOL;
int height = 300; #define INVALID_HANDLE_VALUE ((HANDLE)0)
#define INFINITE 0xFFFFFFFFUL
#define STD_INPUT_HANDLE ((DWORD)-10)
// Allocate space for uint32_t (4 bytes per pixel) HANDLE GetStdHandle(DWORD nStdHandle);
uint32_t* image = (uint32_t *)HeapAlloc(NULL, 0, width * height * sizeof(uint32_t)); BOOL ReadConsoleA(HANDLE hConsoleInput, void* lpBuffer,
DWORD nNumberOfCharsToRead, DWORD* lpNumberOfCharsRead,
void* pInputControl);
int tileSize = 8; typedef struct {
DWORD cb;
char* lpReserved;
char* lpDesktop;
char* lpTitle;
DWORD dwX, dwY, dwXSize, dwYSize;
DWORD dwXCountChars, dwYCountChars;
DWORD dwFillAttribute, dwFlags;
unsigned short wShowWindow, cbReserved2;
unsigned char* lpReserved2;
HANDLE hStdInput, hStdOutput, hStdError;
} STARTUPINFOA;
for (int y = 0; y < height; y++) { typedef struct {
for (int x = 0; x < width; x++) { HANDLE hProcess;
uint32_t color1 = 0xFFFFFFFF; // White HANDLE hThread;
uint32_t color2 = 0xFF000000; // Black DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
int tileX = x / tileSize; BOOL CreateProcessA(const char* lpApp, char* lpCmdLine,
int tileY = y / tileSize; void* lpPA, void* lpTA, BOOL bInherit,
DWORD dwFlags, void* lpEnv, const char* lpDir,
STARTUPINFOA* lpSI, PROCESS_INFORMATION* lpPI);
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMs);
BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode);
BOOL CloseHandle(HANDLE hObject);
if ((tileX + tileY) % 2 == 0) { // ---------------------------------------------------------------------------
image[y * width + x] = color1; // Output helpers
} else { // ---------------------------------------------------------------------------
image[y * width + x] = color2; static void print(const char* s) {
} OutputDebugStringA(s);
}
static void printUInt(uint32_t n) {
char buf[12];
int i = 0;
if (n == 0) { print("0"); return; }
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
char rev[12];
int j = 0;
while (i > 0) rev[j++] = buf[--i];
rev[j] = '\0';
print(rev);
}
// ---------------------------------------------------------------------------
// Line editor
// Reads one character at a time via ReadConsoleA, echoes it, handles
// backspace, returns on Enter.
// ---------------------------------------------------------------------------
#define MAX_CMD 256
static void readLine(HANDLE hIn, char* buf, int len) {
int pos = 0;
while (1) {
char c;
DWORD nRead;
ReadConsoleA(hIn, &c, 1, &nRead, 0);
if (!nRead) continue;
if (c == '\r' || c == '\n') {
print("\n");
break;
}
if ((c == '\b' || c == 127) && pos > 0) {
pos--;
print("\b \b");
continue;
}
if (c < 0x20 || c == 127)
continue;
if (pos < len - 1) {
buf[pos++] = c;
char echo[2] = { c, '\0' };
print(echo);
} }
} }
buf[pos] = '\0';
}
// Pass the 32-bit buffer directly to BitBlt // ---------------------------------------------------------------------------
BitBlt(600, 400, width, height, image); // Trim leading/trailing spaces in-place; returns pointer into buf.
// ---------------------------------------------------------------------------
static char* trim(char* s) {
while (*s == ' ') s++;
int len = (int)strlen(s);
while (len > 0 && s[len - 1] == ' ') len--;
s[len] = '\0';
return s;
}
// for (size_t i = 0; i < 1000000000; i++) { // ---------------------------------------------------------------------------
// // burn! // Run one command. Returns 1 if the shell should exit.
// } // ---------------------------------------------------------------------------
static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0')
return 0;
OutputDebugStringA("Goodbye from the shell!\n"); if (strcmp(cmd, "exit") == 0)
return 1;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
unsigned char* p = (unsigned char*)&si;
for (size_t i = 0; i < sizeof(si); i++) p[i] = 0;
si.cb = (DWORD)sizeof(si);
if (!CreateProcessA(cmd, 0, 0, 0, 0, 0, 0, 0, &si, &pi)) {
print("shell: could not launch '");
print(cmd);
print("'\n");
return 0;
}
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode = 0;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
if (exitCode != 0) {
print("shell: '");
print(cmd);
print("' exited with code ");
printUInt(exitCode);
print("\n");
}
return 0;
}
// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------
int main() {
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
char lineBuf[MAX_CMD];
print("shell: ready\n");
while (1) {
print("> ");
readLine(hIn, lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf)))
break;
}
print("shell: goodbye\n");
ExitProcess(0); ExitProcess(0);
} }
+73 -31
View File
@@ -1,32 +1,74 @@
; syscall_nr.inc - assembled from syscall_nr.h for use in .asm files ; syscall_nr.inc - assembled from syscall_nr.h for use in .asm files
SYS_EXIT_THREAD EQU 0 ; Keep in sync with syscall_nr.h and ke_syscall.c.
SYS_EXIT_PROCESS EQU 1
SYS_SPAWN EQU 2 ; Process & Thread
SYS_SLEEP EQU 3 SYS_EXIT_PROCESS EQU 0
SYS_YIELD EQU 4 SYS_EXIT_THREAD EQU 1
SYS_ALLOC EQU 5 SYS_CREATE_THREAD EQU 2
SYS_FREE EQU 6 SYS_GET_CURRENT_TID EQU 3
SYS_CREATE_THREAD EQU 7 SYS_GET_CURRENT_PID EQU 4
SYS_OPEN_HANDLE EQU 8 SYS_YIELD EQU 5
SYS_CLOSE_HANDLE EQU 9 SYS_SLEEP EQU 6
SYS_DBG_PRINT EQU 10 SYS_SET_THREAD_PRIORITY EQU 7
SYS_MUTEX_CREATE EQU 11
SYS_MUTEX_ACQUIRE EQU 12 ; Memory
SYS_MUTEX_RELEASE EQU 13 SYS_ALLOC EQU 8
SYS_SEM_CREATE EQU 14 SYS_FREE EQU 9
SYS_SEM_WAIT EQU 15 SYS_REALLOC EQU 10
SYS_SEM_SIGNAL EQU 16
SYS_EVENT_CREATE EQU 17 ; Mutex
SYS_EVENT_SET EQU 18 SYS_MUTEX_CREATE EQU 11
SYS_EVENT_WAIT EQU 19 SYS_MUTEX_ACQUIRE EQU 12
SYS_QUEUE_CREATE EQU 20 SYS_MUTEX_TRY_ACQUIRE EQU 13
SYS_QUEUE_SEND EQU 21 SYS_MUTEX_RELEASE EQU 14
SYS_QUEUE_RECV EQU 22
SYS_SCREEN_BLIT EQU 23 ; Semaphore
SYS_GET_TICK EQU 24 SYS_SEM_CREATE EQU 15
SYS_OPEN EQU 25 SYS_SEM_WAIT EQU 16
SYS_READ EQU 26 SYS_SEM_TRY_WAIT EQU 17
SYS_FCLOSE EQU 27 SYS_SEM_SIGNAL EQU 18
SYS_FSEEK EQU 28
SYS_FSIZE EQU 29 ; Event
SYS_MAX EQU 30 SYS_EVENT_CREATE EQU 19
SYS_EVENT_SIGNAL EQU 20
SYS_EVENT_CLEAR EQU 21
SYS_EVENT_WAIT EQU 22
; Queue
SYS_QUEUE_CREATE EQU 23
SYS_QUEUE_SEND EQU 24
SYS_QUEUE_RECV EQU 25
; Handle (unified - one close for every object type)
SYS_CLOSE_HANDLE EQU 26
; File I/O
SYS_OPEN_FILE EQU 27
SYS_READ_FILE EQU 28
SYS_SEEK_FILE EQU 29
SYS_GET_FILE_SIZE EQU 30
; Screen
SYS_SCREEN_BLIT EQU 31
SYS_SCREEN_CLEAR EQU 32
SYS_SCREEN_GET_SIZE EQU 33
SYS_SCREEN_PUT_CHAR EQU 34
; Keyboard
SYS_KB_POLL EQU 35
SYS_KB_READ_CHAR EQU 36
SYS_KB_READ_SCANCODE EQU 37
; Timer
SYS_GET_TICK EQU 38
; Debug
SYS_DEBUG_PRINT EQU 39
; Process launch & lifecycle
SYS_CREATE_PROCESS EQU 40
SYS_WAIT_FOR_PROCESS EQU 41
SYS_GET_PROCESS_EXIT_CODE EQU 42
; Sentinel
SYS_MAX EQU 43
+125 -158
View File
@@ -1,180 +1,147 @@
; syscall_stubs.asm - user-mode INT 0x2E stubs exported by syscall.dll (MASM64) ; syscall_stubs.asm - user-mode INT 0x40 stubs exported by syscall.dll (MASM64)
; Microsoft x64 ABI: args in RCX, RDX, R8, R9; args 5+6 in R10, R11. ;
; Calling convention (kernel side):
; RAX = syscall number
; RCX = arg 0
; RDX = arg 1
; R8 = arg 2
; R9 = arg 3
; R10 = arg 4 (our extension - loaded from the stack by SYSCALL_STUB6)
; R11 = arg 5 (our extension - loaded from the stack by SYSCALL_STUB6)
;
; User-mode callers follow standard MS x64: args 1-4 in RCX/RDX/R8/R9,
; args 5+ on the stack above the home area ([rsp+28h], [rsp+30h], ...).
;
; SYSCALL_STUB2 - 4 or fewer args: pure register passing, no stack read.
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
;
; Handle lifecycle:
; SysCloseHandle is the single close stub for every object type.
; There is no SysCloseFile - files are closed through the unified path,
; exactly like mutexes, semaphores, events, and queues.
INCLUDE syscall_nr.inc ; SYS_* constants INCLUDE syscall_nr.inc
.CODE .CODE
SysExitThread PROC ; ---------------------------------------------------------------------------
mov rax, SYS_EXIT_THREAD SYSCALL_STUB2 MACRO name, nr
int 40h PUBLIC Sys&name
Sys&name PROC EXPORT
mov rax, nr
int 40h
ret ret
SysExitThread ENDP Sys&name ENDP
ENDM
SysExitProcess PROC ; ---------------------------------------------------------------------------
mov rax, SYS_EXIT_PROCESS SYSCALL_STUB6 MACRO name, nr
int 40h PUBLIC Sys&name
Sys&name PROC EXPORT
mov r10, [rsp+28h] ; 5th argument
mov r11, [rsp+30h] ; 6th argument
mov rax, nr
int 40h
ret ret
SysExitProcess ENDP Sys&name ENDP
ENDM
SysYield PROC ; ---------------------------------------------------------------------------
mov rax, SYS_YIELD ; Process & Thread
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 ExitProcess, SYS_EXIT_PROCESS
SysYield ENDP SYSCALL_STUB2 ExitThread, SYS_EXIT_THREAD
SYSCALL_STUB2 CreateThread, SYS_CREATE_THREAD
SYSCALL_STUB2 GetCurrentTid, SYS_GET_CURRENT_TID
SYSCALL_STUB2 GetCurrentPid, SYS_GET_CURRENT_PID
SYSCALL_STUB2 Yield, SYS_YIELD
SYSCALL_STUB2 Sleep, SYS_SLEEP
SYSCALL_STUB2 SetThreadPriority, SYS_SET_THREAD_PRIORITY
SysSleep PROC ; RCX = milliseconds ; ---------------------------------------------------------------------------
mov rax, SYS_SLEEP ; Memory
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 Alloc, SYS_ALLOC
SysSleep ENDP SYSCALL_STUB2 Free, SYS_FREE
SYSCALL_STUB2 Realloc, SYS_REALLOC
SysAlloc PROC ; RCX = size -> RAX = ptr ; ---------------------------------------------------------------------------
mov rax, SYS_ALLOC ; Mutex
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 MutexCreate, SYS_MUTEX_CREATE
SysAlloc ENDP SYSCALL_STUB2 MutexAcquire, SYS_MUTEX_ACQUIRE
SYSCALL_STUB2 MutexTryAcquire, SYS_MUTEX_TRY_ACQUIRE
SYSCALL_STUB2 MutexRelease, SYS_MUTEX_RELEASE
SysFree PROC ; RCX = ptr, RDX = size ; ---------------------------------------------------------------------------
mov rax, SYS_FREE ; Semaphore
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 SemCreate, SYS_SEM_CREATE
SysFree ENDP SYSCALL_STUB2 SemWait, SYS_SEM_WAIT
SYSCALL_STUB2 SemTryWait, SYS_SEM_TRY_WAIT
SYSCALL_STUB2 SemSignal, SYS_SEM_SIGNAL
SysCreateThread PROC ; RCX=entry, RDX=arg, R8=priority -> RAX=handle ; ---------------------------------------------------------------------------
mov rax, SYS_CREATE_THREAD ; Event
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 EventCreate, SYS_EVENT_CREATE
SysCreateThread ENDP SYSCALL_STUB2 EventSignal, SYS_EVENT_SIGNAL
SYSCALL_STUB2 EventClear, SYS_EVENT_CLEAR
SYSCALL_STUB2 EventWait, SYS_EVENT_WAIT
SysCloseHandle PROC ; RCX = handle ; ---------------------------------------------------------------------------
mov rax, SYS_CLOSE_HANDLE ; Queue
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 QueueCreate, SYS_QUEUE_CREATE
SysCloseHandle ENDP SYSCALL_STUB2 QueueSend, SYS_QUEUE_SEND
SYSCALL_STUB2 QueueRecv, SYS_QUEUE_RECV
SysDbgPrint PROC ; RCX = string ptr ; ---------------------------------------------------------------------------
mov rax, SYS_DBG_PRINT ; Handle (unified - use for files, mutexes, semaphores, events, queues)
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 CloseHandle, SYS_CLOSE_HANDLE
SysDbgPrint ENDP
SysMutexCreate PROC ; -> RAX = handle ; ---------------------------------------------------------------------------
mov rax, SYS_MUTEX_CREATE ; File I/O
int 40h ; (To close: SysCloseHandle(handle) - no dedicated SysCloseFile)
ret ; ---------------------------------------------------------------------------
SysMutexCreate ENDP SYSCALL_STUB2 OpenFile, SYS_OPEN_FILE
SYSCALL_STUB2 ReadFile, SYS_READ_FILE
SYSCALL_STUB2 SeekFile, SYS_SEEK_FILE
SYSCALL_STUB2 GetFileSize, SYS_GET_FILE_SIZE
SysMutexAcquire PROC ; RCX = handle ; ---------------------------------------------------------------------------
mov rax, SYS_MUTEX_ACQUIRE ; Screen
int 40h ; SysScreenBlit has 5 args (x, y, w, h, pixels) - needs SYSCALL_STUB6.
ret ; SysScreenPutChar has 5 args (c, x, y, fg, bg) - needs SYSCALL_STUB6.
SysMutexAcquire ENDP ; ---------------------------------------------------------------------------
SYSCALL_STUB6 ScreenBlit, SYS_SCREEN_BLIT
SYSCALL_STUB2 ScreenClear, SYS_SCREEN_CLEAR
SYSCALL_STUB2 ScreenGetSize, SYS_SCREEN_GET_SIZE
SYSCALL_STUB6 ScreenPutChar, SYS_SCREEN_PUT_CHAR
SysMutexRelease PROC ; RCX = handle ; ---------------------------------------------------------------------------
mov rax, SYS_MUTEX_RELEASE ; Keyboard
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 KbPoll, SYS_KB_POLL
SysMutexRelease ENDP SYSCALL_STUB2 KbReadChar, SYS_KB_READ_CHAR
SYSCALL_STUB2 KbReadScancode, SYS_KB_READ_SCANCODE
SysSemCreate PROC ; RCX=initial, RDX=max -> RAX=handle ; ---------------------------------------------------------------------------
mov rax, SYS_SEM_CREATE ; Timer
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 GetTick, SYS_GET_TICK
SysSemCreate ENDP
SysSemWait PROC ; RCX = handle ; ---------------------------------------------------------------------------
mov rax, SYS_SEM_WAIT ; Debug
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 DebugPrint, SYS_DEBUG_PRINT
SysSemWait ENDP
SysSemSignal PROC ; RCX = handle ; ---------------------------------------------------------------------------
mov rax, SYS_SEM_SIGNAL ; Process launch & lifecycle
int 40h ; ---------------------------------------------------------------------------
ret SYSCALL_STUB2 CreateProcess, SYS_CREATE_PROCESS
SysSemSignal ENDP SYSCALL_STUB2 WaitForProcess, SYS_WAIT_FOR_PROCESS
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
SysEventCreate PROC ; RCX=manualReset, RDX=initialState -> RAX=handle
mov rax, SYS_EVENT_CREATE
int 40h
ret
SysEventCreate ENDP
SysEventSet PROC ; RCX = handle
mov rax, SYS_EVENT_SET
int 40h
ret
SysEventSet ENDP
SysEventWait PROC ; RCX = handle
mov rax, SYS_EVENT_WAIT
int 40h
ret
SysEventWait ENDP
SysQueueCreate PROC ; -> RAX = handle
mov rax, SYS_QUEUE_CREATE
int 40h
ret
SysQueueCreate ENDP
SysQueueSend PROC ; RCX=handle, RDX=*msg, R8=blocking
mov rax, SYS_QUEUE_SEND
int 40h
ret
SysQueueSend ENDP
SysQueueRecv PROC ; RCX=handle, RDX=*msg, R8=blocking
mov rax, SYS_QUEUE_RECV
int 40h
ret
SysQueueRecv ENDP
SysScreenBlit PROC
; RCX=x, RDX=y, R8=w, R9=h
; The 5th arg (pixels) is on the stack above the 32-byte shadow space and return address.
mov r10, [rsp + 40]
mov rax, SYS_SCREEN_BLIT
int 40h
ret
SysScreenBlit ENDP
SysGetTick PROC ; -> RAX = milliseconds
mov rax, SYS_GET_TICK
int 40h
ret
SysGetTick ENDP
SysOpen PROC ; RCX = *path -> RAX = handle (0 = failure)
mov rax, SYS_OPEN
int 40h
ret
SysOpen ENDP
SysRead PROC ; RCX = handle, RDX = *buf, R8 = size -> RAX = bytes read
mov rax, SYS_READ
int 40h
ret
SysRead ENDP
SysFClose PROC ; RCX = handle
mov rax, SYS_FCLOSE
int 40h
ret
SysFClose ENDP
SysFSeek PROC ; RCX = handle, RDX = offset
mov rax, SYS_FSEEK
int 40h
ret
SysFSeek ENDP
SysFSize PROC ; RCX = handle -> RAX = size in bytes
mov rax, SYS_FSIZE
int 40h
ret
SysFSize ENDP
END END
+1075
View File
File diff suppressed because it is too large. Load diff
Binary file not shown.
+2322
View File
File diff suppressed because it is too large. Load diff
+86
View File
@@ -0,0 +1,86 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#include "bink.h"
void OutputDebugStringA(const char* s);
_Noreturn void ExitProcess(unsigned int code);
void* HeapAlloc(void* heap, unsigned long flags, size_t size);
void BitBlt(int x, int y, int w, int h, const uint32_t* pixels);
void* CreateFileA(const char* name, unsigned long access,
unsigned long share, void* sa,
unsigned long disp, unsigned long flags,
void* templ);
unsigned long GetFileSize(void* handle, unsigned long* high);
int ReadFile(void* handle, void* buf, unsigned long toRead,
unsigned long* read, void* overlapped);
int main() {
OutputDebugStringA("video: starting\n");
// void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
// if (!f) {
// OutputDebugStringA("video: failed to open video.bk2\n");
// ExitProcess(1);
// }
// unsigned long s = GetFileSize(f, 0);
// void* buf = HeapAlloc(0, 0, s);
// if (!buf) {
// OutputDebugStringA("video: HeapAlloc failed\n");
// ExitProcess(1);
// }
// int r = ReadFile(f, buf, s, 0, 0);
// if (!r) {
// OutputDebugStringA("video: ReadFile failed\n");
// ExitProcess(1);
// }
// OutputDebugStringA("video: video.bk2 loaded\n");
// HBINK bink = BinkOpen(buf, BINKFROMMEMORY);
// if (!bink) {
// OutputDebugStringA("video: BinkOpen failed: ");
// OutputDebugStringA(BinkGetError());
// OutputDebugStringA("\n");
// ExitProcess(1);
// }
// uint32_t width = bink->Width;
// uint32_t height = bink->Height;
// uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
// if (!fb) {
// OutputDebugStringA("video: framebuffer alloc failed\n");
// ExitProcess(1);
// }
// while (1) {
// if (BinkWait(bink)) {
// _mm_pause();
// continue;
// }
// BinkDoFrame(bink);
// if (BinkShouldSkip(bink)) {
// BinkNextFrame(bink);
// BinkDoFrame(bink);
// continue;
// }
// BinkCopyToBuffer(bink, fb, width * 4, height, 0, 0, BINKSURFACE32);
// BitBlt(0, 0, width, height, fb);
// if (bink->FrameNum == bink->Frames)
// break;
// BinkNextFrame(bink);
// }
// BinkClose(bink);
for (int i = 0; i < 10000000; i++) _mm_pause();
OutputDebugStringA("video: done\n");
ExitProcess(0);
}
+992 -91
View File
File diff suppressed because it is too large. Load diff
+81 -2
View File
@@ -1,2 +1,81 @@
// ole32.c - stub; no COM at this stage. // ole32.c - Win32 COM / OLE stubs
static void stub(void) { } // No COM infrastructure at this stage; all functions fail gracefully.
#include <stdint.h>
#include <stddef.h>
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef long HRESULT;
typedef void* IUnknown;
#define S_OK ((HRESULT)0x00000000L)
#define E_NOTIMPL ((HRESULT)0x80004001L)
#define E_OUTOFMEMORY ((HRESULT)0x8007000EL)
#define COINIT_MULTITHREADED 0x0
// GUID - 16-byte COM interface identifier.
typedef struct {
unsigned int Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID;
// PROPVARIANT - variant type used by shell / media APIs.
typedef struct {
unsigned short vt;
unsigned short wReserved1, wReserved2, wReserved3;
union {
long long hVal;
double dblVal;
void* punkVal;
unsigned char bVal;
unsigned int ulVal;
// ... (only common fields; full union is ~16 bytes)
unsigned char padding[16];
};
} PROPVARIANT;
extern void SysFree(void* ptr, size_t size);
// ---------------------------------------------------------------------------
// COM initialisation
// ---------------------------------------------------------------------------
__declspec(dllexport) HRESULT CoInitializeEx(void* reserved, unsigned long coInit) {
(void)reserved; (void)coInit;
return S_OK; // Pretend success; no actual apartment is created.
}
__declspec(dllexport) void CoUninitialize(void) {
// No-op.
}
// ---------------------------------------------------------------------------
// Object creation
// ---------------------------------------------------------------------------
__declspec(dllexport) HRESULT CoCreateInstance(const GUID* clsid, IUnknown* outer,
unsigned long ctx, const GUID* iid,
void** ppv) {
(void)clsid; (void)outer; (void)ctx; (void)iid;
if (ppv) *ppv = (void*)0;
return E_NOTIMPL;
}
// ---------------------------------------------------------------------------
// Memory
// ---------------------------------------------------------------------------
__declspec(dllexport) void CoTaskMemFree(void* pv) {
if (pv) SysFree(pv, 0);
}
// ---------------------------------------------------------------------------
// PROPVARIANT
// ---------------------------------------------------------------------------
__declspec(dllexport) HRESULT PropVariantClear(PROPVARIANT* pvar) {
if (!pvar) return E_NOTIMPL;
// Zero the entire structure; no deep release needed for stub types.
unsigned char* p = (unsigned char*)pvar;
for (size_t i = 0; i < sizeof(*pvar); i++) p[i] = 0;
return S_OK;
}
+79 -8
View File
@@ -1,20 +1,91 @@
// user32.c - minimal user32 stubs (no window manager yet) // user32.c - Win32 user interface stubs (no window manager yet)
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
// Screen blit is the only graphical primitive available at this stage. // ---------------------------------------------------------------------------
// Syscall stubs (from syscall.dll)
// ---------------------------------------------------------------------------
extern void SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels); extern void SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
// syscall.dll
extern void SysYield(void); extern void SysYield(void);
extern void SysDebugPrint(const char* str);
extern uint32_t SysKbPoll(void);
void BitBlt(int x, int y, int w, int h, const uint32_t* pixels) { // ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef void* HWND;
#define IDOK 1
#define IDCANCEL 2
// ---------------------------------------------------------------------------
// Keyboard
// ---------------------------------------------------------------------------
// GetAsyncKeyState: returns non-zero if a key is available in the input
// buffer. We have no per-key state, so the vKey argument is ignored and
// the high bit (key-down) is set whenever SysKbPoll reports data waiting.
__declspec(dllexport) short GetAsyncKeyState(int vKey) {
(void)vKey;
// High bit (0x8000 / INT16_MIN) = key is down.
// Low bit = key pressed since last call; we have no per-key history to
// track, so it is left clear.
return SysKbPoll() ? (short)(-32768) : 0;
}
// ---------------------------------------------------------------------------
// Screen output
// ---------------------------------------------------------------------------
// BitBlt is the only graphical primitive available at this stage.
__declspec(dllexport) void BitBlt(int x, int y, int w, int h, const uint32_t* pixels) {
SysScreenBlit(x, y, w, h, pixels); SysScreenBlit(x, y, w, h, pixels);
} }
// Message loop stubs - always return 0. // ---------------------------------------------------------------------------
int GetMessage(void* msg, void* hwnd, unsigned int min, unsigned int max) { // Message loop (stubs - always return 0 / no messages)
// ---------------------------------------------------------------------------
__declspec(dllexport) int GetMessage(void* msg, HWND hwnd,
unsigned int min, unsigned int max) {
(void)msg; (void)hwnd; (void)min; (void)max; (void)msg; (void)hwnd; (void)min; (void)max;
SysYield(); SysYield();
return 0; return 0;
} }
int DispatchMessage(const void* msg) { (void)msg; return 0; }
__declspec(dllexport) int DispatchMessage(const void* msg) { (void)msg; return 0; }
// ---------------------------------------------------------------------------
// Window queries (stubs - no WM exists yet, all return NULL / 0)
// ---------------------------------------------------------------------------
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
(void)hwnd; (void)cmd; return (HWND)0;
}
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
(void)hwnd; (void)index; return 0;
}
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
(void)hwnd; (void)index; return 0;
}
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
(void)hwnd;
if (pid) *pid = 1;
return 1; // Thread ID
}
// ---------------------------------------------------------------------------
// Dialogs
// ---------------------------------------------------------------------------
__declspec(dllexport) int MessageBoxA(HWND hwnd, const char* text,
const char* caption, unsigned int type) {
(void)hwnd; (void)text; (void)caption; (void)type;
// No UI available, console
SysDebugPrint(text);
return IDOK;
}
+128 -2
View File
@@ -1,2 +1,128 @@
// winmm.c - stub; no multimedia at this stage. // winmm.c - Win32 multimedia: waveform audio, timer
static void stub(void) { } // Stubs only; no audio hardware access at this stage.
#include <stdint.h>
#include <stddef.h>
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
typedef unsigned int MMRESULT;
typedef void* HWAVEOUT;
#define MMSYSERR_NOERROR 0
#define MMSYSERR_NOTSUPPORTED 8
#define WAVERR_STILLPLAYING 33
// WAVEFORMATEX - standard PCM audio format descriptor.
typedef struct {
unsigned short wFormatTag;
unsigned short nChannels;
unsigned int nSamplesPerSec;
unsigned int nAvgBytesPerSec;
unsigned short nBlockAlign;
unsigned short wBitsPerSample;
unsigned short cbSize;
} WAVEFORMATEX;
// WAVEHDR - buffer header submitted to waveOutWrite.
typedef struct _WAVEHDR {
char* lpData;
unsigned int dwBufferLength;
unsigned int dwBytesRecorded;
uintptr_t dwUser;
unsigned int dwFlags;
unsigned int dwLoops;
struct _WAVEHDR* lpNext;
uintptr_t reserved;
} WAVEHDR;
// MMTIME - used by waveOutGetPosition.
typedef struct {
unsigned int wType;
union {
unsigned int ms;
unsigned int sample;
unsigned int cb;
unsigned int ticks;
} u;
} MMTIME;
// ---------------------------------------------------------------------------
// Timer
// ---------------------------------------------------------------------------
// timeGetTime is the lightweight alternative to GetTickCount; both return
// milliseconds since system start. We forward to the kernel tick counter.
extern uint64_t SysGetTick(void);
__declspec(dllexport) unsigned long timeGetTime(void) {
return (unsigned long)SysGetTick();
}
// ---------------------------------------------------------------------------
// Waveform audio (stubs - no audio hardware at this stage)
// ---------------------------------------------------------------------------
__declspec(dllexport) MMRESULT waveOutOpen(HWAVEOUT* phwo, unsigned int deviceId,
const WAVEFORMATEX* fmt, uintptr_t callback,
uintptr_t callbackInstance, unsigned long flags) {
(void)deviceId; (void)fmt; (void)callback; (void)callbackInstance; (void)flags;
if (phwo) *phwo = (void*)0;
return MMSYSERR_NOTSUPPORTED;
}
__declspec(dllexport) MMRESULT waveOutClose(HWAVEOUT hwo) {
(void)hwo; return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
unsigned int size) {
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
unsigned int size) {
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT waveOutWrite(HWAVEOUT hwo, WAVEHDR* hdr, unsigned int size) {
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOTSUPPORTED;
}
__declspec(dllexport) MMRESULT waveOutPause(HWAVEOUT hwo) {
(void)hwo; return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT waveOutRestart(HWAVEOUT hwo) {
(void)hwo; return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT waveOutReset(HWAVEOUT hwo) {
(void)hwo; return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT waveOutSetVolume(HWAVEOUT hwo, unsigned long volume) {
(void)hwo; (void)volume; return MMSYSERR_NOTSUPPORTED;
}
__declspec(dllexport) MMRESULT waveOutGetPosition(HWAVEOUT hwo, MMTIME* mmt,
unsigned int size) {
(void)hwo; (void)size;
if (mmt) { mmt->wType = 0; mmt->u.ms = 0; }
return MMSYSERR_NOERROR;
}
// ---------------------------------------------------------------------------
// Timer resolution control
// No hardware timer reprogramming at this stage; pretend success so callers
// that wrap their audio/game loop in timeBeginPeriod(1) don't bail out.
// ---------------------------------------------------------------------------
__declspec(dllexport) MMRESULT timeBeginPeriod(unsigned int uPeriod) {
(void)uPeriod;
return MMSYSERR_NOERROR;
}
__declspec(dllexport) MMRESULT timeEndPeriod(unsigned int uPeriod) {
(void)uPeriod;
return MMSYSERR_NOERROR;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

+15 -11
View File
@@ -1,26 +1,26 @@
# an x86-64 kernel # NT-Lite -- an x86-64 kernel
boots. runs processes. doesn't crash (usually). Boots. Runs processes. Mimics Windows to some extent.
## what is it? ## what is it?
a 64-bit operating system kernel written from scratch. A 64-bit operating system kernel written from scratch.
UEFI bootloader, physical and virtual memory managers, preemptive scheduler, HAL, VFS, PE loader, partial Win32 layer. UEFI bootloader, physical and virtual memory managers, preemptive scheduler, HAL, VFS, PE loader, partial Win32 layer.
it boots into a shell. It boots into a shell.
~20k lines of code. ~20k lines of C and assembly.
## features ## features
- UEFI bootloader with PE loading and base relocation - UEFI bootloader
- physical memory manager (frame stack, O(1) alloc/free) - physical memory manager
- virtual memory manager with kernel pool - virtual memory manager with kernel pool
- HAL (screen, keyboard, timer, disk, PCI, APIC) - HAL (screen, keyboard, timer, disk, PCI, APIC)
- preemptive scheduler with priority levels - preemptive scheduler with priority levels
- processes and threads - processes and threads
- VFS with FAT32 driver (read-only) - VFS
- drive-letter paths (`0:\apps\shell.exe`) - drive-letter paths (`0:\apps\shell.exe`)
- INI-based boot and driver configuration - INI-based boot and driver configuration
- partial Win32 layer (kernel32 and user32, far from finished) - partial Win32 layer (kernel32 and user32, far from finished but capable of running simple apps)
- PE import resolution and dynamic driver loading - PE import resolution and dynamic driver loading
## how 2 build ## how 2 build
@@ -62,10 +62,14 @@ a filesystem driver fills in an `IO_FILESYSTEM` vtable and calls `IoRegisterFile
drives 0–7. a drive only works if a filesystem registered on it. drives 0–7. a drive only works if a filesystem registered on it.
## screenshots
![shell.exe playing a video with bink 2](image.png)
## faq ## faq
Q: Why? Q: Why?
A: Why not. A: Why not?
Q: Does it run Doom? Q: Does it run Doom?
A: Not yet. A: Not yet.
@@ -74,7 +78,7 @@ Q: Is the Win32 layer real?
A: Real enough to load binaries that expect it. Not real enough to run Notepad. A: Real enough to load binaries that expect it. Not real enough to run Notepad.
Q: Is the scheduler preemptive? Q: Is the scheduler preemptive?
A: Yes. A: As much as it will ever be.
Q: Will it corrupt my data? Q: Will it corrupt my data?
A: How would it? It's read-only. A: How would it? It's read-only.