save fpu state properly-ish, add a guard to doom_malloc_wrapper, fix QueryPerformance* and GetSystemTimeAsFileTime

This commit is contained in:
maksw committed 2026-05-15 14:42:39 +02:00
1 parent def649b995
commit ddb5d5f5d8
8 files changed
+77 -55

No files matched your search

Binary file not shown.
+4 -3
View File
@@ -27,11 +27,12 @@ static void doom_print_wrapper(const char* msg)
// ---------------------------------------------------------------------------
// doom_malloc_fn / doom_free_fn
// Heap handle 0, same convention as video.c.
// ---------------------------------------------------------------------------
static void* doom_malloc_wrapper(int size)
{
return HeapAlloc(0, HEAP_ZERO_MEMORY, size);
void* p = HeapAlloc(0, HEAP_ZERO_MEMORY, size);
if (!p) ExitProcess(GetLastError());
return p;
}
static void doom_free_wrapper(void* ptr)
@@ -126,7 +127,7 @@ static void doom_exit_wrapper(int code)
static char* doom_getenv_wrapper(const char* name)
{
if (strcmp(name, "HOME") == 0) {
static char home[] = "fs0:\\";
static char home[] = "c:";
doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n");
return home;
}
+1
View File
@@ -53,6 +53,7 @@ void KeInitializeScheduler(void) {
__stosq((unsigned __int64 *)ctx, 0, sizeof(KCONTEXT) / 8);
ctx->Rip = (uint64_t)IdleLoop;
idle->SavedContext = ctx;
KeInitThreadFpuContext(idle);
gIdleThread = idle;
gCurrentThread = idle;
+7
View File
@@ -9,6 +9,7 @@
THREAD_SavedContext EQU 88
THREAD_TssRsp0 EQU 104
THREAD_Teb EQU 112
THREAD_FpuContext EQU 160
TSS_RSP0_OFFSET EQU 4
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
@@ -20,6 +21,9 @@ EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
; RCX = from, RDX = to (Microsoft x64)
; -------------------------------------------------------------------------
KeSwitchContext PROC
mov rax, [rcx + THREAD_FpuContext]
fxsave64 [rax]
; Save non-volatile registers onto the current (outgoing) kernel stack.
push rbx
push rbp
@@ -50,6 +54,9 @@ KeSwitchContext PROC
mov r8, gTss
mov qword ptr [r8 + TSS_RSP0_OFFSET], rax
mov rax, [rdx + THREAD_FpuContext]
fxrstor64 [rax]
; Restore non-volatile registers of the incoming thread.
pop r15
pop r14
+23 -2
View File
@@ -7,6 +7,9 @@
#include <mm_virt.h>
#include <ob_object.h>
#define FPU_FCW_DEFAULT 0x037Fu
#define FPU_MXCSR_DEFAULT 0x1F80u
static uint32_t gNextThreadId = 1;
extern void KeThreadEntry(void);
@@ -80,8 +83,8 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
// 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
teb[1] = userStackTop; // StackBase
teb[2] = (uintptr_t)ustack; // StackLimit
thread->Teb = teb;
@@ -124,6 +127,7 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
owner->ThreadCount++;
KeReleaseSpinlock(&owner->HandleLock, flags);
KeInitThreadFpuContext(thread);
KeReadyThread(thread);
*out = thread;
return STATUS_SUCCESS;
@@ -211,6 +215,7 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
if (out) *out = thread;
KeInitThreadFpuContext(thread);
KeReadyThread(thread);
return STATUS_SUCCESS;
}
@@ -233,3 +238,19 @@ void KeUnblockThread(THREAD* thread) {
KeReadyThread(thread);
}
void KeInitThreadFpuContext(THREAD* thread)
{
uintptr_t raw =
(uintptr_t)thread->FpuStorage;
uintptr_t aligned =
(raw + 15) & ~(uintptr_t)15;
thread->FpuContext = (void*)aligned;
memset(thread->FpuContext, 0, 512);
*(uint16_t*)((uint8_t*)thread->FpuContext + 0x00) = 0x037F;
*(uint32_t*)((uint8_t*)thread->FpuContext + 0x18) = 0x1F80;
}
+5
View File
@@ -73,6 +73,9 @@ typedef struct THREAD {
// Allocated in KeCreateThread, signaled in KeExitThread before teardown.
// Waiters block on this via SYS_WAIT_THREAD -> KeWaitForEvent.
EVENT* ExitEvent;
void* FpuContext;
uint8_t FpuStorage[528];
} THREAD;
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
@@ -94,3 +97,5 @@ void KeUnblockThread(THREAD* thread);
// Assembly stub - implemented in ke_switch.asm.
void KeSwitchContext(THREAD* from, THREAD* to);
void KeInitThreadFpuContext(THREAD* thread);
+5 -3
View File
@@ -131,9 +131,11 @@ static int runCommand(const char* cmd) {
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
STARTUPINFOA si = { sizeof(si) }; PROCESS_INFORMATION pi;
char cmdBuf[MAX_CMD]; strncpy(cmdBuf, cmd, MAX_CMD-1);
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
PROCESS_INFORMATION pi;
char cmdBuf[MAX_CMD];
strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
} else {
+32 -47
View File
@@ -1139,69 +1139,51 @@ __declspec(dllexport) void WakeAllConditionVariable(CONDITION_VARIABLE* cv) { (v
// ---------------------------------------------------------------------------
// Timing & Performance Counters
//
// 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 {
struct { DWORD LowPart; long HighPart; } u;
long long QuadPart;
} LARGE_INTEGER;
static volatile long long gTscFreq = 0; // TSC ticks per second; 0 = uninitialised
static volatile long long gTscFreq = 0;
// Attempt to read TSC frequency from CPUID leaf 0x15.
// Returns 0 if the leaf is absent or the crystal frequency is not enumerated.
// Attempt to read TSC frequency from CPUID leaf 0x15 (Modern Intel/AMD).
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
unsigned denom = (unsigned)info[0];
unsigned numer = (unsigned)info[1];
unsigned crystal_hz = (unsigned)info[2];
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.
// Calibrate TSC against SysGetTick (1 unit = 1 microsecond).
static long long TscFreqCalibrate(void) {
const uint64_t N_TICKS = 10; // 10 * 10 ms = 100 ms gate time
// We'll wait for 100ms (100,000 microseconds) to get a stable sample.
const uint64_t CALIBRATION_US = 100000;
// 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();
uint64_t tsc0 = __rdtsc();
// Wait for N_TICKS more ticks.
while ((SysGetTick() - t0) < N_TICKS) { _mm_pause(); }
// Simple busy wait for the duration
while ((SysGetTick() - t0) < CALIBRATION_US) {
_mm_pause();
}
uint64_t t1 = SysGetTick();
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));
uint64_t delta_tsc = tsc1 - tsc0;
uint64_t delta_us = t1 - t0;
// Hz = (Delta TSC / Delta Microseconds) * 1,000,000
return (long long)(delta_tsc * 1000000ULL / delta_us);
}
static long long TscFreqGet(void) {
@@ -1220,8 +1202,7 @@ __declspec(dllexport) BOOL QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency)
}
__declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount) {
// Ensure frequency is measured before the first counter read so that the
// baseline TSC and the frequency come from the same calibration window.
// Ensure calibration happens before the first read.
TscFreqGet();
if (lpPerformanceCount)
lpPerformanceCount->QuadPart = (long long)__rdtsc();
@@ -1229,15 +1210,19 @@ __declspec(dllexport) BOOL QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceC
}
__declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime) {
// Return a fixed FILETIME representing 2024-01-01 00:00:00 UTC.
// FILETIME = 100-nanosecond intervals since 1601-01-01.
// 2024-01-01 offset: 133186080000000000 * 100 ns
if (!lpSystemTimeAsFileTime) return;
// SysGetTick() units = QUANTUM_MS ms = QUANTUM_MS * 10000 * 100-ns intervals.
unsigned long long ft = 133186080000000000ULL
+ SysGetTick() * ((unsigned long long)QUANTUM_MS * 10000ULL);
unsigned char* p = (unsigned char*)lpSystemTimeAsFileTime;
for (int i = 0; i < 8; i++) p[i] = (unsigned char)(ft >> (i * 8));
// Baseline: 2024-01-01 00:00:00 UTC in 100-ns intervals
const unsigned long long BASELINE_FT = 133485408000000000ULL;
// SysGetTick() is in microseconds.
// 1 microsecond = 10 units of 100-nanoseconds.
unsigned long long now_us = SysGetTick();
unsigned long long ft = BASELINE_FT + (now_us * 10ULL);
// Copy the 64-bit value into the output buffer.
// We use memcpy to safely handle potential alignment issues with the void pointer.
memcpy(lpSystemTimeAsFileTime, &ft, sizeof(ft));
}
// ---------------------------------------------------------------------------