fix ps/2 mouse init, make Stall more precise, rename CurrentProcess to KeGetCurrentProcess, correct type about maneuvers, add file writing to win32

This commit is contained in:
maksw committed 2026-05-05 10:30:13 +02:00
1 parent 8c7564d5e1
commit a37f5b1581
14 files changed
+288 -75

No files matched your search

Binary file not shown.
Binary file not shown.
+2 -3
View File
@@ -609,7 +609,7 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
} }
if (EFI_ERROR(ResolveImports(core[0].physBase, core[1].physBase, core[1].virtBase))) { if (EFI_ERROR(ResolveImports(core[0].physBase, core[1].physBase, core[1].virtBase))) {
Print(L"Failed to resolve imports. System halted.\n"); Print(L"Failed to resolve imports.\n");
while(true); while(true);
} }
@@ -804,6 +804,5 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable
KernelMain(bi); KernelMain(bi);
// Never returns. // Never returns.
_disable(); __halt(); _disable(); while (true) __halt();
return EFI_SUCCESS;
} }
+17 -3
View File
@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <stdarg.h> #include <stdarg.h>
#include <intrin.h>
#include <printf.h> #include <printf.h>
// Declared in other hal_*.c files: // Declared in other hal_*.c files:
@@ -298,9 +299,8 @@ static void WalkAcpi(uintptr_t rsdpPhys) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// HalShutdown - ACPI S5 soft power-off. Does not return. // HalShutdown - ACPI S5 soft power-off. Does not return.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#include <intrin.h>
static void HalShutdown(void) { static void HalShutdown(void) {
// Silence! Stop the world!
_disable(); _disable();
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10) // SLP_EN (bit 13) | SLP_TYPx (bits 12:10)
@@ -310,7 +310,7 @@ static void HalShutdown(void) {
if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA); if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA);
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB); if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
// Firmware should have powered off by now. If it didn't, halt. // Die!
while (true) __halt(); while (true) __halt();
} }
@@ -360,6 +360,20 @@ __declspec(dllexport) KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
gApi.Version = HAL_API_VERSION; gApi.Version = HAL_API_VERSION;
gInitialised = 1; gInitialised = 1;
// This shouldn't be here but i have no other way.
// After i send 0xF4 and move the mouse the controller sends an interrupts that doesn't fire
// and because the CPU missed the IRQ 12 edge transition (cli) later when we expect it to fire, it doesn't
// leaving us with no mouse and no keyboard. The dreaded 8042 will hold the CLOCK line low forever.
// To combat this we just go about our day executing the rest of the init and just before the end
// of init we use a decaying poll to grab whatever the controller left for us ("Drain and Pray").
for (int i = 0; i < 1000; i++) {
if (__inbyte(0x64) & 0x01) {
__inbyte(0x60); // Consume
i = 0; // Reset timeout if we are still getting data
}
gApi.Timer.Stall(1); // Micro-stalls
}
HalDbgPrint("HAL: initialisation complete.\n"); HalDbgPrint("HAL: initialisation complete.\n");
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
+43 -6
View File
@@ -27,6 +27,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <intrin.h> #include <intrin.h>
#include <hal_api.h> #include <hal_api.h>
#include <hal_debug.h>
// -- PS/2 port addresses ------------------------------------------------------- // -- PS/2 port addresses -------------------------------------------------------
#define PS2_DATA 0x60u #define PS2_DATA 0x60u
@@ -74,10 +75,18 @@ static void Ps2WaitInput(void) {
// Wait until the 8042's output buffer has data (bit 0 of status = 1). // Wait until the 8042's output buffer has data (bit 0 of status = 1).
static void Ps2WaitOutput(void) { static void Ps2WaitOutput(void) {
int timeout = 100000; int timeout = 5000000;
while (!(__inbyte(PS2_STAT) & 0x01u) && --timeout) { /* spin */ } while (!(__inbyte(PS2_STAT) & 0x01u) && --timeout) { /* spin */ }
} }
// Reads from PS2_DATA until the output buffer is empty
static void Ps2FlushOutput(void) {
int timeout = 100000;
while ((__inbyte(PS2_STAT) & 0x01u) && --timeout) {
__inbyte(PS2_DATA);
}
}
// Route the next write to the auxiliary (mouse) device rather than the keyboard. // Route the next write to the auxiliary (mouse) device rather than the keyboard.
static void MouseSendByte(uint8_t byte) { static void MouseSendByte(uint8_t byte) {
Ps2WaitInput(); Ps2WaitInput();
@@ -192,27 +201,55 @@ void HalMouseInitialize(HAL_MOUSE* out, int screenW, int screenH) {
gMouseY = screenH / 2; gMouseY = screenH / 2;
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Step 1: Enable aux interrupts in the 8042 command byte. // Step 0: Deafen the controller and flush
// - Bit 1 (0x02): enable auxiliary interrupt (drives IRQ 12). // -----------------------------------------------------------------
// - Bit 5 (0x20): disables the aux clock when set; clear it. Ps2WaitInput();
__outbyte(PS2_CMD, 0xA7u); // Disable Aux port (ignores mouse)
Ps2WaitInput();
__outbyte(PS2_CMD, 0xADu); // Disable Main port (ignores keyboard)
// Now flush the buffer. Because the ports are disabled, we guarantee
// no new data can sneak in behind our backs.
Ps2FlushOutput();
// -----------------------------------------------------------------
// Step 1: Safely configure the 8042 Command Byte
// ----------------------------------------------------------------- // -----------------------------------------------------------------
Ps2WaitInput(); Ps2WaitInput();
__outbyte(PS2_CMD, 0x20u); // read current command byte __outbyte(PS2_CMD, 0x20u); // read current command byte
Ps2WaitOutput(); Ps2WaitOutput();
uint8_t cmd = __inbyte(PS2_DATA); uint8_t cmd = __inbyte(PS2_DATA);
cmd |= 0x02u; // enable aux interrupt cmd |= 0x02u; // enable aux interrupt
cmd &= ~0x20u; // clear "disable aux clock" cmd &= ~0x20u; // clear "disable aux clock"
Ps2WaitInput(); Ps2WaitInput();
__outbyte(PS2_CMD, 0x60u); // write command byte __outbyte(PS2_CMD, 0x60u); // write command byte
Ps2WaitInput(); Ps2WaitInput();
__outbyte(PS2_DATA, cmd); __outbyte(PS2_DATA, cmd);
// Re-enable both ports now that config is safely written
Ps2WaitInput();
__outbyte(PS2_CMD, 0xA8u); // Enable Aux port
Ps2WaitInput();
__outbyte(PS2_CMD, 0xAEu); // Enable Main port
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Step 2: Reset mouse (0xFF). // Step 2: Reset mouse (0xFF).
// Mouse replies: ACK (0xFA), self-test pass (0xAA), ID (0x00).
// ----------------------------------------------------------------- // -----------------------------------------------------------------
MouseSendByte(0xFFu); // reset MouseSendByte(0xFFu); // reset
MouseRecvByte(); // 0xFA - ACK
// The BIOS or an earlier phase might have left a movement packet
// partially in flight right as we re-enabled the port.
// We loop and discard anything that isn't the 0xFA ACK.
uint8_t ack;
int retries = 10;
do {
ack = MouseRecvByte();
} while (ack != 0xFAu && --retries > 0);
// Thanks to the increased Ps2WaitOutput timeout, we will actually
// wait long enough for the physical hardware self-test to finish here.
MouseRecvByte(); // 0xAA - BAT passed MouseRecvByte(); // 0xAA - BAT passed
MouseRecvByte(); // 0x00 - device ID MouseRecvByte(); // 0x00 - device ID
+44 -9
View File
@@ -20,6 +20,9 @@
extern HAL_API gApi; extern HAL_API gApi;
static volatile uint32_t* gLapic = NULL; static volatile uint32_t* gLapic = NULL;
static uint32_t gApicTicksPerUs = 0;
static uint64_t gHpetTicksPerUs = 0;
static volatile uint64_t* gHpet = NULL;
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off))) #define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
static volatile uint64_t gTicks = 0; static volatile uint64_t gTicks = 0;
@@ -37,17 +40,41 @@ static void TimerIsr(void) {
static uint64_t QueryTicks(void) { return gTicks; } static uint64_t QueryTicks(void) { return gTicks; }
static void Stall(uint32_t us) { static void Stall(uint32_t us) {
uint64_t start = gTicks; // Priority 1: Use HPET (Highest Precision)
uint64_t ms = (us + 999) / 1000; if (gHpet && gHpetTicksPerUs > 0) {
uint64_t start = gHpet[30]; // Offset 0xF0 is the Main Counter
uint64_t delta = (uint64_t)us * gHpetTicksPerUs;
while ((gHpet[30] - start) < delta) {
_mm_pause(); // Hint to CPU we are in a spin-loop
}
return;
}
// Spin up to 100ms worth of IO delays waiting for ticks to advance. // Priority 2: Use APIC Timer (High Precision, but handles wrap-around)
// If gTicks doesn't move (interrupts off), fall back to pure IO delay. if (gApicTicksPerUs > 0) {
uint32_t io_iters = (uint32_t)(ms * 1000); uint32_t total_ticks = us * gApicTicksPerUs;
for (uint32_t i = 0; i < io_iters; i++) { uint32_t last_val = LAPIC_REG(LAPIC_TIMER_CUR);
if ((gTicks - start) >= ms) return; // interrupt-driven path uint32_t elapsed = 0;
while (elapsed < total_ticks) {
uint32_t current = LAPIC_REG(LAPIC_TIMER_CUR);
if (current < last_val) {
elapsed += (last_val - current);
} else if (current > last_val) {
// Handled the reset/wrap at the end of QUANTUM_MS
uint32_t init_val = LAPIC_REG(LAPIC_TIMER_INIT);
elapsed += (last_val + (init_val - current));
}
last_val = current;
_mm_pause();
}
return;
}
// Priority 3: Fallback to Port 0x80
for (uint32_t i = 0; i < us; i++) {
__outbyte(0x80, 0); __outbyte(0x80, 0);
} }
// If we exhausted the IO delay loop we've waited long enough anyway.
} }
// Returns HPET ticks/second, or 0 if HPET is unavailable. // Returns HPET ticks/second, or 0 if HPET is unavailable.
@@ -62,7 +89,7 @@ static uint64_t HpetInit(uintptr_t hpetPhys) {
if (!periodFs) return 0; if (!periodFs) return 0;
// Enable the HPET main counter (General Configuration, offset 0x10, bit 0) // Enable the HPET main counter (General Configuration, offset 0x10, bit 0)
hpet[2] |= 1; // offset 0x10 / 8 = index 2 hpet[2] |= 1; // offset 0x10 / 8 = index 2
return (uint64_t)1e15 / periodFs; // ticks per second return (uint64_t)1e15 / periodFs; // ticks per second
} }
@@ -129,6 +156,14 @@ KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *o
return STATUS_DEVICE_ERROR; return STATUS_DEVICE_ERROR;
} }
gApicTicksPerUs = ticksPerMs / 1000;
if (hpetPhys) {
gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys);
uint64_t hpetHz = HpetInit(hpetPhys);
gHpetTicksPerUs = hpetHz / 1000000;
}
HalDbgPrint("HAL: APIC timer: %u ticks/ms\n", ticksPerMs); HalDbgPrint("HAL: APIC timer: %u ticks/ms\n", ticksPerMs);
// Configure LAPIC timer in periodic mode // Configure LAPIC timer in periodic mode
+3 -3
View File
@@ -32,9 +32,9 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) {
KeReleaseSpinlock(&sl, flags); KeReleaseSpinlock(&sl, flags);
} }
// KePanic - Forces output even if debug was compiled out, then the macro halts. // KePanic
__declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) { __declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
// We are done, go away // Silence! Stop the world!
_disable(); _disable();
// Print a bright-red banner so the framebuffer is unmistakable. // Print a bright-red banner so the framebuffer is unmistakable.
@@ -50,7 +50,7 @@ __declspec(dllexport) _Noreturn void KePanic(const char *fmt, ...) {
vprintf(fmt, va); vprintf(fmt, va);
va_end(va); va_end(va);
// Halt it! // Die!
while (true) __halt(); while (true) __halt();
} }
+6 -14
View File
@@ -54,21 +54,13 @@ static const HAL_API* gHal;
void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; } void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; }
// ---------------------------------------------------------------------------
// Helper: get the current process, or NULL if unavailable.
// ---------------------------------------------------------------------------
static inline PROCESS* CurrentProcess(void) {
THREAD* t = KeGetCurrentThread();
return t ? t->Owner : NULL;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Helper: look up a handle and type-check it in one call. // Helper: look up a handle and type-check it in one call.
// Bumps the refcount on success; caller must ObDereferenceObject when done. // Bumps the refcount on success; caller must ObDereferenceObject when done.
// Returns NULL if the handle is invalid or the type does not match. // Returns NULL if the handle is invalid or the type does not match.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static OBJECT* HandleToObject(uint32_t handle, OBJECT_TYPE expected) { static OBJECT* HandleToObject(uint32_t handle, OBJECT_TYPE expected) {
PROCESS* proc = CurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (!proc) return NULL; if (!proc) return NULL;
OBJECT* obj = KeLookupHandle(proc, handle); // bumps refcount OBJECT* obj = KeLookupHandle(proc, handle); // bumps refcount
if (!obj) return NULL; if (!obj) return NULL;
@@ -85,7 +77,7 @@ static OBJECT* HandleToObject(uint32_t handle, OBJECT_TYPE expected) {
// On failure, drops the reference it would have stored. // On failure, drops the reference it would have stored.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static KSTATUS AllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) { static KSTATUS AllocHandleForCurrent(OBJECT* obj, uint32_t* handleOut) {
PROCESS* proc = CurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE; if (!proc) return STATUS_FAILURE;
KSTATUS s = KeAllocateHandle(proc, obj, handleOut); KSTATUS s = KeAllocateHandle(proc, obj, handleOut);
if (!KSUCCESS(s)) ObDereferenceObject(obj); if (!KSUCCESS(s)) ObDereferenceObject(obj);
@@ -126,7 +118,7 @@ static uint64_t SyscallExitThread(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2, static uint64_t SyscallCreateThread(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) { uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a4; (void)a5; (void)a4; (void)a5;
PROCESS* proc = CurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE; if (!proc) return STATUS_FAILURE;
if (a2 >= PriorityCount) return STATUS_INVALID_PARAMETER; if (a2 >= PriorityCount) return STATUS_INVALID_PARAMETER;
@@ -154,7 +146,7 @@ static uint64_t SyscallGetCurrentTid(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallGetCurrentPid(uint64_t a0, uint64_t a1, uint64_t a2, static uint64_t SyscallGetCurrentPid(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) { uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; (void)a0; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* p = CurrentProcess(); PROCESS* p = KeGetCurrentProcess();
return p ? p->ProcessId : 0; return p ? p->ProcessId : 0;
} }
@@ -448,7 +440,7 @@ static uint64_t SyscallQueueRecv(uint64_t a0, uint64_t a1, uint64_t a2,
static uint64_t SyscallCloseHandle(uint64_t a0, uint64_t a1, uint64_t a2, static uint64_t SyscallCloseHandle(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) { uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5; (void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = CurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_FAILURE; if (!proc) return STATUS_FAILURE;
KeCloseHandle(proc, (uint32_t)a0); KeCloseHandle(proc, (uint32_t)a0);
return STATUS_SUCCESS; return STATUS_SUCCESS;
@@ -733,7 +725,7 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; } if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
// Give the caller a handle to the new process so they can wait on it. // Give the caller a handle to the new process so they can wait on it.
PROCESS* caller = CurrentProcess(); PROCESS* caller = KeGetCurrentProcess();
if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; } if (!caller) { ObDereferenceObject(&proc->Header); return STATUS_FAILURE; }
uint32_t handle; uint32_t handle;
+10 -6
View File
@@ -113,7 +113,7 @@ static void InitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
continue; continue;
} }
// memcpy manouvers // memcpy maneuvers
memcpy(path, "drivers\\", 8); memcpy(path, "drivers\\", 8);
memcpy(path + 8, runtimeKeys[i], len + 1); memcpy(path + 8, runtimeKeys[i], len + 1);
@@ -329,7 +329,7 @@ void SystemInitThread(void* arg) {
DbgPrint("SystemInitThread: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws); DbgPrint("SystemInitThread: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws);
} }
} }
MmFreePool(args); MmFreePool(args);
} }
@@ -340,7 +340,10 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
// //
// Physical & Virtual Memory initialization // Physical & Virtual Memory initialization
// //
MmInitializePhysical(&bootInfo->MemoryMap, bootInfo->PhysicalMapBase); if (!MmInitializePhysical(&bootInfo->MemoryMap, bootInfo->PhysicalMapBase)) {
_disable(); while (true) __halt(); // there's nothing we can do
}
PHYSICAL_ADDRESS pml4 = (PHYSICAL_ADDRESS)__readcr3(); PHYSICAL_ADDRESS pml4 = (PHYSICAL_ADDRESS)__readcr3();
MmInitializeVirtual(pml4, 4 * 1024 * 1024 /* 4 MB initial pool */); MmInitializeVirtual(pml4, 4 * 1024 * 1024 /* 4 MB initial pool */);
@@ -368,11 +371,12 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
.RsdpPhysical = bootInfo->RsdpPhysical, .RsdpPhysical = bootInfo->RsdpPhysical,
.DebugEnabled = bootInfo->DebugEnabled .DebugEnabled = bootInfo->DebugEnabled
}; };
KSTATUS s = HalInitialize(&halParams); KSTATUS s = HalInitialize(&halParams);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
_disable(); _disable(); while (true) __halt(); // there's nothing we can do.
while (true) __halt(); // there's nothing we can do.
} }
const HAL_API* hal = HalGetApi(); const HAL_API* hal = HalGetApi();
gKmainHal = hal; gKmainHal = hal;
@@ -416,7 +420,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
DbgPrint("KernelMain: Handing over to scheduler...\n"); DbgPrint("KernelMain: Handing over to scheduler...\n");
// Ignite the system // Ignite the system!
_enable(); _enable();
// The Idle Loop: The CPU rests here whenever all other threads are waiting on events. // The Idle Loop: The CPU rests here whenever all other threads are waiting on events.
+4 -5
View File
@@ -6,9 +6,6 @@
// bootloader at PhysicalWindowBase (always 0xFFFF880000000000). // bootloader at PhysicalWindowBase (always 0xFFFF880000000000).
// //
// Lock: a single spinlock guards Push/Pop. O(1) for both operations. // Lock: a single spinlock guards Push/Pop. O(1) for both operations.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <mm_phys.h> #include <mm_phys.h>
#include <ke_spinlock.h> #include <ke_spinlock.h>
@@ -47,7 +44,7 @@ static void PushFrame(PHYSICAL_ADDRESS frame) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Initialisation // Initialisation
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
void MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) { bool MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) {
gPhysBase = physicalWindowBase; gPhysBase = physicalWindowBase;
// --- Phase 0: Count total usable frames so we can size the stack // --- Phase 0: Count total usable frames so we can size the stack
@@ -71,7 +68,7 @@ void MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) {
} }
if (!stackPhys) { if (!stackPhys) {
while (true) { __halt(); } return false;
} }
gFrameStack.Stack = (PHYSICAL_ADDRESS *)PhysToVirt(stackPhys); gFrameStack.Stack = (PHYSICAL_ADDRESS *)PhysToVirt(stackPhys);
@@ -99,6 +96,8 @@ void MmInitializePhysical(const MEMORY_MAP* map, uint64_t physicalWindowBase) {
} }
} }
} }
return true;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+2 -1
View File
@@ -3,6 +3,7 @@
// Stack allocator over 4 KB frames, seeded from MEMORY_MAP. // Stack allocator over 4 KB frames, seeded from MEMORY_MAP.
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#include <memory_map.h> #include <memory_map.h>
#define PAGE_SIZE 4096ULL #define PAGE_SIZE 4096ULL
@@ -11,7 +12,7 @@
typedef uint64_t PHYSICAL_ADDRESS; typedef uint64_t PHYSICAL_ADDRESS;
// Called once by KernelMain with the MEMORY_MAP from BOOT_INFO. // Called once by KernelMain with the MEMORY_MAP from BOOT_INFO.
void MmInitializePhysical(const MEMORY_MAP* map, bool MmInitializePhysical(const MEMORY_MAP* map,
uint64_t physicalWindowBase); uint64_t physicalWindowBase);
// Allocate / free a single 4 KB frame. // Allocate / free a single 4 KB frame.
+64 -5
View File
@@ -21,7 +21,9 @@ typedef int BOOL;
// File I/O constants // File I/O constants
#define GENERIC_READ 0x80000000L #define GENERIC_READ 0x80000000L
#define GENERIC_WRITE 0x40000000L
#define OPEN_EXISTING 3 #define OPEN_EXISTING 3
#define CREATE_ALWAYS 2
#define FILE_SHARE_READ 0x00000001 #define FILE_SHARE_READ 0x00000001
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010
@@ -75,9 +77,9 @@ typedef struct _WIN32_FIND_DATAA {
} WIN32_FIND_DATAA; } WIN32_FIND_DATAA;
BOOL CreateProcessA(const char* lpApp, char* lpCmdLine, BOOL CreateProcessA(const char* lpApp, char* lpCmdLine,
void* lpPA, void* lpTA, BOOL bInherit, void* lpPA, void* lpTA, BOOL bInherit,
DWORD dwFlags, void* lpEnv, const char* lpDir, DWORD dwFlags, void* lpEnv, const char* lpDir,
STARTUPINFOA* lpSI, PROCESS_INFORMATION* lpPI); STARTUPINFOA* lpSI, PROCESS_INFORMATION* lpPI);
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMs); DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMs);
BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode); BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode);
BOOL CloseHandle(HANDLE hObject); BOOL CloseHandle(HANDLE hObject);
@@ -89,10 +91,12 @@ BOOL FindClose(HANDLE hFindFile);
BOOL SetCurrentDirectoryA(const char* lpPathName); BOOL SetCurrentDirectoryA(const char* lpPathName);
BOOL DeleteFileA(const char* lpFileName); BOOL DeleteFileA(const char* lpFileName);
HANDLE CreateFileA(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, HANDLE CreateFileA(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
void* lpSecurityAttributes, DWORD dwCreationDisposition, void* lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
BOOL ReadFile(HANDLE hFile, void* lpBuffer, DWORD nNumberOfBytesToRead, BOOL ReadFile(HANDLE hFile, void* lpBuffer, DWORD nNumberOfBytesToRead,
DWORD* lpNumberOfBytesRead, void* lpOverlapped); DWORD* lpNumberOfBytesRead, void* lpOverlapped);
BOOL WriteFile(HANDLE hFile, const void* lpBuffer, DWORD nNumberOfBytesToWrite,
DWORD* lpNumberOfBytesWritten, void* lpOverlapped);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Global handles // Global handles
@@ -172,6 +176,51 @@ static char* trim(char* s) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Built-in commands // Built-in commands
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void cmd_help() {
print("Available commands:\n"
" help - Show this list\n"
" dir - List files in current directory\n"
" cd <path> - Change directory\n"
" type <file> - Display file contents\n"
" del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n"
" exit - Close the shell\n");
}
static void cmd_write(const char* args) {
char filename[MAX_PATH];
int i = 0;
// Parse filename (up to first space)
while (args[i] != ' ' && args[i] != '\0' && i < MAX_PATH - 1) {
filename[i] = args[i];
i++;
}
filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') {
print("write: usage: write <filename> <text>\n");
return;
}
const char* text = args + i + 1; // Content starts after the space
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) {
print("write: could not create file\n");
return;
}
DWORD written;
if (!WriteFile(hFile, text, (DWORD)strlen(text), &written, 0)) {
print("write: error writing to file\n");
} else {
print("File written successfully.\n");
}
CloseHandle(hFile);
}
static void cmd_dir() { static void cmd_dir() {
WIN32_FIND_DATAA fd; WIN32_FIND_DATAA fd;
HANDLE hFind = FindFirstFileA("*", &fd); HANDLE hFind = FindFirstFileA("*", &fd);
@@ -226,6 +275,11 @@ static int runCommand(const char* cmd) {
return 1; return 1;
} }
if (strcmp(cmd, "help") == 0) {
cmd_help();
return 0;
}
if (strcmp(cmd, "dir") == 0) { if (strcmp(cmd, "dir") == 0) {
cmd_dir(); cmd_dir();
return 0; return 0;
@@ -256,6 +310,11 @@ static int runCommand(const char* cmd) {
return 0; return 0;
} }
if (strncmp(cmd, "write ", 6) == 0) {
cmd_write(cmd + 6);
return 0;
}
// --- External Process Execution --- // --- External Process Execution ---
STARTUPINFOA si; STARTUPINFOA si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
+16 -12
View File
@@ -102,17 +102,17 @@ int main() {
CloseHandle(h); CloseHandle(h);
HBINK bink = BinkOpen(buf, BINKFROMMEMORY); HBINK b = BinkOpen(buf, BINKFROMMEMORY);
if (!bink) { if (!b) {
WriteConsoleA(h, "video: BinkOpen failed: ", 24, 0, 0); WriteConsoleA(h, "video: BinkOpen failed: ", 24, 0, 0);
const char* err = BinkGetError(); const char* err = BinkGetError();
WriteConsoleA(h, err, strlen(err), 0, 0); WriteConsoleA(h, err, (unsigned int)strlen(err), 0, 0);
WriteConsoleA(h, "\n", 1, 0, 0); WriteConsoleA(h, "\n", 1, 0, 0);
ExitProcess(1); ExitProcess(1);
} }
uint32_t width = bink->Width; uint32_t width = b->Width;
uint32_t height = bink->Height; uint32_t height = b->Height;
uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t)); uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
if (!fb) { if (!fb) {
@@ -131,19 +131,20 @@ int main() {
bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biCompression = BI_RGB;
while (1) { while (1) {
if (BinkWait(bink)) { if (BinkWait(b)) {
_mm_pause(); _mm_pause();
continue; continue;
} }
BinkDoFrame(bink); BinkDoFrame(b);
if (BinkShouldSkip(bink)) { if (BinkShouldSkip(b)) {
BinkNextFrame(bink); BinkNextFrame(b);
BinkDoFrame(b);
continue; continue;
} }
BinkCopyToBuffer(bink, fb, width * 4, height, 0, 0, BinkCopyToBuffer(b, fb, width * 4, height, 0, 0,
BINKSURFACE32); BINKSURFACE32);
// Welcome to the GDI pipeline! // Welcome to the GDI pipeline!
@@ -163,10 +164,13 @@ int main() {
EndPaint((HWND)0, &ps); EndPaint((HWND)0, &ps);
BinkNextFrame(bink); if (b->FrameNum == b->Frames)
break;
BinkNextFrame(b);
} }
WriteConsoleA(h, "shell: goodbye\n", 15, 0, 0); WriteConsoleA(h, "shell: goodbye\n", 15, 0, 0);
return 0; ExitProcess(0);
} }
+77 -8
View File
@@ -23,6 +23,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <intrin.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Syscall stubs (from syscall.dll, assembled from syscall_stubs.asm) // Syscall stubs (from syscall.dll, assembled from syscall_stubs.asm)
@@ -155,7 +156,7 @@ __declspec(dllexport) void OutputDebugStringA(const char* str) {
// Timer // Timer
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
__declspec(dllexport) DWORD GetTickCount(void) { __declspec(dllexport) DWORD GetTickCount(void) {
return (DWORD)SysGetTick(); return (DWORD)SysGetTick() * 10;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -1041,35 +1042,103 @@ __declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (v
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Timing & Performance Counters // Timing & Performance Counters
// We report a 1 MHz counter so QPC / QPF is consistent with GetTickCount. //
// We use the CPU's Time Stamp Counter (TSC) for QueryPerformanceCounter so
// that callers like Bink get a truly high-resolution, continuously-advancing
// clock. The TSC frequency is measured once on first use.
//
// Calibration strategy:
// 1. Try CPUID leaf 0x15 (TSC / Crystal-clock ratio). If the crystal
// frequency (ECX) is non-zero, compute TSC Hz = crystal_hz * EBX / EAX
// with no blocking at all.
// 2. Otherwise fall back to measuring TSC ticks over a known wall-clock
// interval supplied by SysGetTick(). SysGetTick() returns raw LAPIC
// interrupt counts; each unit represents QUANTUM_MS = 10 ms. We wait
// for two consecutive tick-boundary crossings and divide.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#define QUANTUM_MS 10 // must match hal_timer.c QUANTUM_MS
typedef union { typedef union {
struct { DWORD LowPart; long HighPart; } u; struct { DWORD LowPart; long HighPart; } u;
long long QuadPart; long long QuadPart;
} LARGE_INTEGER; } LARGE_INTEGER;
#define QPC_FREQUENCY 1000000LL // 1 MHz static volatile long long gTscFreq = 0; // TSC ticks per second; 0 = uninitialised
// Attempt to read TSC frequency from CPUID leaf 0x15.
// Returns 0 if the leaf is absent or the crystal frequency is not enumerated.
static long long TscFreqFromCpuid(void) {
int info[4];
__cpuid(info, 0);
if ((unsigned)info[0] < 0x15) return 0;
__cpuid(info, 0x15);
unsigned denom = (unsigned)info[0]; // EAX – TSC/crystal denominator
unsigned numer = (unsigned)info[1]; // EBX – TSC/crystal numerator
unsigned crystal_hz = (unsigned)info[2]; // ECX – crystal clock in Hz
if (!denom || !numer || !crystal_hz) return 0;
return (long long)crystal_hz * numer / denom;
}
// Calibrate TSC frequency by timing over two LAPIC-tick boundaries.
// Each SysGetTick() unit = QUANTUM_MS ms; we measure across N_TICKS units
// for a total gate time of N_TICKS * QUANTUM_MS ms.
static long long TscFreqCalibrate(void) {
const uint64_t N_TICKS = 10; // 10 * 10 ms = 100 ms gate time
// Synchronise to the leading edge of a tick.
uint64_t t = SysGetTick();
while (SysGetTick() == t) { _mm_pause(); }
uint64_t tsc0 = __rdtsc();
uint64_t t0 = SysGetTick();
// Wait for N_TICKS more ticks.
while ((SysGetTick() - t0) < N_TICKS) { _mm_pause(); }
uint64_t tsc1 = __rdtsc();
uint64_t ticks_got = SysGetTick() - t0; // actual ticks elapsed (>= N_TICKS)
// ticks_got LAPIC ticks = ticks_got * QUANTUM_MS ms = ticks_got * QUANTUM_MS / 1000 s
// TSC Hz = (tsc1-tsc0) / (ticks_got * QUANTUM_MS / 1000)
// = (tsc1-tsc0) * 1000 / (ticks_got * QUANTUM_MS)
return (long long)((tsc1 - tsc0) * 1000ULL / (ticks_got * (uint64_t)QUANTUM_MS));
}
static long long TscFreqGet(void) {
if (gTscFreq) return gTscFreq;
long long f = TscFreqFromCpuid();
if (!f) f = TscFreqCalibrate();
gTscFreq = f;
return f;
}
__declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) { __declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency) {
if (lpFrequency) lpFrequency->QuadPart = QPC_FREQUENCY; if (lpFrequency) lpFrequency->QuadPart = TscFreqGet();
return TRUE; return TRUE;
} }
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) { __declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
// SysGetTick returns milliseconds; scale to microseconds for 1 MHz QPC. // Ensure frequency is measured before the first counter read so that the
// baseline TSC and the frequency come from the same calibration window.
TscFreqGet();
if (lpPerformanceCount) if (lpPerformanceCount)
lpPerformanceCount->QuadPart = (long long)SysGetTick() * 1000LL; lpPerformanceCount->QuadPart = (long long)__rdtsc();
return TRUE; return TRUE;
} }
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) { __declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
// Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC. // Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC.
// FILETIME = 100-nanosecond intervals since 1601-01-01. // FILETIME = 100-nanosecond intervals since 1601-01-01.
// 2024-01-01 offset from 1601 in 100ns units: 133186080000000000 // 2024-01-01 offset: 133186080000000000 * 100 ns
if (!lpSystemTimeAsFileTime) return; if (!lpSystemTimeAsFileTime) return;
// SysGetTick() units = QUANTUM_MS ms = QUANTUM_MS * 10000 * 100-ns intervals.
unsigned long long ft = 133186080000000000ULL unsigned long long ft = 133186080000000000ULL
+ (unsigned long long)SysGetTick() * 10000ULL; + SysGetTick() * ((unsigned long long)QUANTUM_MS * 10000ULL);
unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime; unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime;
for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8)); for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8));
} }