add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.

This commit is contained in:
maksw committed 2026-06-20 13:17:39 +02:00
1 parent 61ec2442eb
commit 2c4a68a75f
114 files changed
+18069 -2620

No files matched your search

+47 -11
View File
@@ -88,7 +88,8 @@ static int doom_read_wrapper(void* handle, void* buf, int count)
if (!handle || count <= 0) return 0;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
size_t read = 0;
SysReadFile(f->Handle, buf, (size_t)count, &read);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(f->Handle, buf, (size_t)count, &read);
SysReadFile(&req);
f->Pos += read;
if (f->Pos > f->Size) f->Size = f->Pos;
return (int)read;
@@ -99,7 +100,8 @@ static int doom_write_wrapper(void* handle, const void* buf, int count)
if (!handle || count <= 0) return 0;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
size_t written = 0;
SysWriteFile(f->Handle, buf, (size_t)count, &written);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(f->Handle, buf, (size_t)count, &written);
SysWriteFile(&req);
f->Pos += written;
if (f->Pos > f->Size) f->Size = f->Pos;
return (int)written;
@@ -262,7 +264,7 @@ static void poll_input(void)
}
// ---------------------------------------------------------------------------
// Render — Performs lightweight software upscale and blits to the screen
// Render — writes directly into a mapped kernel surface and presents it.
// ---------------------------------------------------------------------------
#define DOOM_W 320
#define DOOM_H 200
@@ -270,32 +272,65 @@ static void poll_input(void)
#define DISP_W (DOOM_W * SCALE)
#define DISP_H (DOOM_H * SCALE)
static uint32_t g_blit_buf[DISP_W * DISP_H];
static uint32_t g_surface_handle;
static uint32_t* g_surface_pixels;
static uint32_t g_surface_pitch_pixels;
static void init_surface(void)
{
uint32_t s = SysCreateSurface(DISP_W, DISP_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &g_surface_handle);
if (s != SYS_STATUS_SUCCESS || !g_surface_handle) {
doom_print_wrapper("doom: SysCreateSurface failed\n");
SysExitProcess(1);
}
void* base = 0;
uint32_t pitchBytes = 0;
s = SysMapSurface(g_surface_handle, &base, &pitchBytes);
if (s != SYS_STATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) {
doom_print_wrapper("doom: SysMapSurface failed\n");
SysExitProcess(1);
}
g_surface_pixels = (uint32_t*)base;
g_surface_pitch_pixels = pitchBytes / sizeof(uint32_t);
}
static void render_frame(const uint8_t* rgba)
{
const uint32_t* src = (const uint32_t*)rgba;
// Software upscale loop mapping 320x200 buffer directly to scaled window buffer
uint32_t* dstBase = g_surface_pixels;
uint32_t dstPitch = g_surface_pitch_pixels;
if (!dstBase || dstPitch < DISP_W) {
doom_print_wrapper("doom: surface not initialized\n");
SysExitProcess(1);
}
for (int y = 0; y < DOOM_H; y++) {
for (int x = 0; x < DOOM_W; x++) {
uint32_t p = src[y * DOOM_W + x];
// RGBA -> BGRA conversion (standard for linear x86/64 framebuffers)
// RGBA -> BGRA/XRGB8888 conversion for the current display path.
uint32_t bgra = (p & 0xFF00FF00u)
| ((p & 0x00FF0000u) >> 16)
| ((p & 0x000000FFu) << 16);
// Populate SCALE * SCALE pixel block
uint32_t* block = dstBase + (size_t)(y * SCALE) * dstPitch + (size_t)(x * SCALE);
for (int sy = 0; sy < SCALE; sy++) {
for (int sx = 0; sx < SCALE; sx++) {
g_blit_buf[(y * SCALE + sy) * DISP_W + (x * SCALE + sx)] = bgra;
block[(size_t)sy * dstPitch + sx] = bgra;
}
}
}
}
SysBlitScreen(0, 0, DISP_W, DISP_H, g_blit_buf);
DISPLAY_RECT rect = { 0, 0, DISP_W, DISP_H };
uint32_t code = SysPresentSurface(g_surface_handle, &rect, 1);
if (code != SYS_STATUS_SUCCESS) {
SysExitProcess(code);
}
}
static int build_argv_from_command_line(const char* cmdline, char* storage, size_t storageSize,
@@ -373,6 +408,7 @@ int main(void)
char argvStorage[512];
const char* argv[32];
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
init_surface();
doom_init(argc, argv, 0);
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: entering game loop\n") }; SysWriteConsole(&req); } while (0);
+7 -3
View File
@@ -380,12 +380,14 @@ static void save_file(void) {
for (int i = 0; i < gLineCount; i++) {
size_t written = 0;
if (gLines[i].len) {
st = SysWriteFile(h, gLines[i].text, (size_t)gLines[i].len, &written);
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(h, gLines[i].text, (size_t)gLines[i].len, &written);
st = SysWriteFile(&writeReq);
if (st != 0) { SysCloseHandle(h); msg_set("save failed: write error"); return; }
}
if (i + 1 < gLineCount) {
const char nl = '\n';
st = SysWriteFile(h, &nl, 1, &written);
SYS_FILE_IO_REQUEST newlineReq = SysMakeFileIoRequest(h, &nl, 1, &written);
st = SysWriteFile(&newlineReq);
if (st != 0) { SysCloseHandle(h); msg_set("save failed: write error"); return; }
}
}
@@ -418,7 +420,9 @@ static void load_file(void) {
char buf[512];
size_t got = 0;
bool truncated = false;
while (SysReadFile(h, buf, sizeof(buf), &got) == 0 && got > 0) {
for (;;) {
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(h, buf, sizeof(buf), &got);
if (SysReadFile(&readReq) != 0 || got == 0) break;
for (size_t i = 0; i < got; i++) {
char c = buf[i];
if (c == '\r') continue;
+71
View File
@@ -0,0 +1,71 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <ntlite/syscall.h>
static int u64_to_string(uint64_t n, char* buf) {
int i = 0;
if (n == 0) {
buf[i++] = '0';
buf[i] = '\0';
return i;
}
char rev[24];
int r_idx = 0;
while (n > 0) {
rev[r_idx++] = (char)('0' + (n % 10));
n /= 10;
}
while (r_idx > 0) {
buf[i++] = rev[--r_idx];
}
buf[i] = '\0';
return i;
}
int main(void) {
SYS_CONSOLE_INFO console;
SYS_MEMORY_STATUS mem;
char metricsBuf[128];
while (true) {
if (SysGetConsoleInfo(&console) == SYS_STATUS_SUCCESS &&
SysGetMemoryStatus(&mem) == SYS_STATUS_SUCCESS) {
// If the system isn't reporting columns correctly, default to a safe standard width
int columns = (console.Columns > 0) ? (int)console.Columns : 80;
char freeStr[24], poolStr[24], heapStr[24];
u64_to_string(mem.FreePhysicalBytes / 1024u, freeStr);
u64_to_string(mem.KernelPoolBytes / 1024u, poolStr);
u64_to_string(mem.UserHeapBytes / 1024u, heapStr);
metricsBuf[0] = '\0';
strcat(metricsBuf, "[ Free: ");
strcat(metricsBuf, freeStr);
strcat(metricsBuf, "K | Pool: ");
strcat(metricsBuf, poolStr);
strcat(metricsBuf, "K | Heap: ");
strcat(metricsBuf, heapStr);
strcat(metricsBuf, "K ]");
size_t textLen = strlen(metricsBuf);
int targetX = columns - (int)textLen;
int targetY = 0;
if (targetX >= 0) {
size_t written = 0;
// Loop through the string and use the console's dedicated text filling API
// This utilizes the console's native font configuration instead of drawing raw screen bits
for (size_t i = 0; i < textLen; i++) {
SysFillConsoleCharacter(metricsBuf[i], targetX + (int)i, targetY, 1, &written);
}
}
}
SysSleep(500);
}
return 0;
}
File diff suppressed because it is too large. Load diff
+2
View File
@@ -147,6 +147,8 @@ static int runCommand(const char* cmd) {
WaitForSingleObject(pi.hProcess, INFINITE);
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
CloseHandle(pi.hThread);
if (pi.hProcess && pi.hProcess != INVALID_HANDLE_VALUE)
CloseHandle(pi.hProcess);
FlushConsoleInputBuffer(hIn);
} else {
print("shell: launch failed\n");
+209 -95
View File
@@ -1,110 +1,231 @@
// User application entry points for video.
// Native NTLite Bink video player.
//
// This application intentionally uses the native syscall ABI directly. Do not
// include <windows.h> here; Win32 compatibility belongs in the Win32 personality
// layer, not in native apps.
#include <windows.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#include "bink.h"
// Implements the command Line Second Token operation.
static const char* commandLineSecondToken(const char* cmdline, char* out, size_t outSize) {
if (!out || outSize == 0) return "video.bk2";
strcpy(out, "video.bk2");
if (!cmdline) return out;
#define VIDEO_DEFAULT_PATH "video.bk2"
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return out;
static void print_span(const char* s, size_t len) {
if (!s || !len) return;
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(s, len) };
SysWriteConsole(&req);
}
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"') cmdline++;
if (*cmdline == '"') cmdline++;
static void print(const char* s) {
if (!s) return;
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i) {
char c = buf[--i];
print_span(&c, 1);
}
}
static void fail_status(const char* what, uint32_t status) {
print("video: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
for (;;) SysExitProcess(1);
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static SYS_STRING_VIEW command_line_view(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC)
return SYS_SV_NULL;
return peb->CommandLine;
}
static int sv_is_space(char c) {
return c == ' ' || c == '\t';
}
static SYS_STRING_VIEW skip_one_token(SYS_STRING_VIEW s) {
size_t i = 0;
while (i < s.Length && sv_is_space(s.Data[i]))
i++;
if (i < s.Length && s.Data[i] == '"') {
i++;
while (i < s.Length && s.Data[i] != '"')
i++;
if (i < s.Length && s.Data[i] == '"')
i++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t') cmdline++;
while (i < s.Length && !sv_is_space(s.Data[i]))
i++;
}
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
if (*cmdline == '\0') return out;
while (i < s.Length && sv_is_space(s.Data[i]))
i++;
size_t pos = 0;
if (*cmdline == '"') {
cmdline++;
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
out[pos++] = *cmdline++;
return SysSvFromParts(s.Data + i, s.Length - i);
}
static SYS_STRING_VIEW command_line_second_token(SYS_STRING_VIEW cmdline) {
SYS_STRING_VIEW rest = skip_one_token(cmdline);
if (!rest.Data || rest.Length == 0)
return SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
size_t begin = 0;
size_t end = 0;
if (rest.Data[0] == '"') {
begin = 1;
end = begin;
while (end < rest.Length && rest.Data[end] != '"')
end++;
} else {
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
out[pos++] = *cmdline++;
begin = 0;
end = 0;
while (end < rest.Length && !sv_is_space(rest.Data[end]))
end++;
}
out[pos] = '\0';
if (end <= begin)
return SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
return SysSvFromParts(rest.Data + begin, end - begin);
}
static const char* path_to_cstr(SYS_STRING_VIEW path, char* out, size_t outSize) {
if (!out || outSize == 0)
return VIDEO_DEFAULT_PATH;
if (!path.Data || path.Length == 0)
path = SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
size_t n = path.Length;
if (n + 1 > outSize)
n = outSize - 1;
memcpy(out, path.Data, n);
out[n] = '\0';
return out;
}
// Runs the requested operation.
static uint32_t open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { path, handleOut };
return SysOpenFile(&req);
}
int main(void) {
const char* cmdline = GetCommandLineA();
char videoPath[260];
const char* path = commandLineSecondToken(cmdline, videoPath, sizeof(videoPath));
SYS_STRING_VIEW cmdline = command_line_view();
SYS_STRING_VIEW pathView = command_line_second_token(cmdline);
void* h = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 0, 0);
WriteConsoleA(h, "\n", 1, 0, 0);
void* f = CreateFileA(path, 0, 0, 0, 3, 0, 0);
if (!f || f == (void*)(unsigned long long)0xFFFFFFFF) { // fuckass
WriteConsoleA(h, "video: failed to open input file\n", 33, 0, 0);
ExitProcess(1);
print("video: starting\n");
if (cmdline.Data && cmdline.Length) {
print_span(cmdline.Data, cmdline.Length);
print("\n");
}
unsigned long s = GetFileSize(f, 0);
if (!s || s == 0xFFFFFFFF) {
WriteConsoleA(h, "video: GetFileSize failed!\n", 27, 0, 0);
ExitProcess(1);
}
void* buf = HeapAlloc(0, 0, s);
if (!buf) {
WriteConsoleA(h, "video: HeapAlloc failed\n", 24, 0, 0);
ExitProcess(1);
uint32_t file = 0;
uint32_t status = open_file_view(pathView, &file);
if (status != SYS_STATUS_SUCCESS) {
char pathBuf[260];
print("video: failed to open input file: ");
print(path_to_cstr(pathView, pathBuf, sizeof(pathBuf)));
print("\n");
fail_status("SysOpenFile", status);
}
int r = ReadFile(f, buf, s, 0, 0);
if (!r) {
WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0);
ExitProcess(1);
uint64_t fileSize = SysGetFileSize(file);
if (fileSize == 0) {
SysCloseHandle(file);
print("video: input file is empty\n");
exit_now(1);
}
WriteConsoleA(h, "video: input file loaded\n", 25, 0, 0);
CloseHandle(h);
HBINK b = BinkOpen(buf, BINKFROMMEMORY);
uint32_t mapping = 0;
status = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, fileSize, &mapping);
if (status != SYS_STATUS_SUCCESS || !mapping) {
SysCloseHandle(file);
fail_status("SysCreateFileMapping", status);
}
void* buf = 0;
status = SysMapFile(mapping, 0, (size_t)fileSize,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &buf);
if (status != SYS_STATUS_SUCCESS || !buf) {
SysCloseHandle(mapping);
SysCloseHandle(file);
fail_status("SysMapFile", status);
}
// The mapped VAD owns the file reference now; keep only the mapped pointer
// alive while Bink is open. This avoids a second whole-file heap allocation
// and lets the VM fault file pages in on demand.
SysCloseHandle(mapping);
SysCloseHandle(file);
print("video: input file mapped\n");
HBINK b = BinkOpen((const char*)buf, BINKFROMMEMORY);
if (!b) {
WriteConsoleA(h, "video: BinkOpen failed: ", 24, 0, 0);
print("video: BinkOpen failed: ");
const char* err = BinkGetError();
WriteConsoleA(h, err, (unsigned int)strlen(err), 0, 0);
WriteConsoleA(h, "\n", 1, 0, 0);
ExitProcess(1);
print(err ? err : "unknown");
print("\n");
SysFreeVm(buf);
exit_now(1);
}
uint32_t width = b->Width;
uint32_t width = b->Width;
uint32_t height = b->Height;
uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
if (!fb) {
WriteConsoleA(h, "video: framebuffer alloc failed\n", 32, 0, 0);
ExitProcess(1);
if (!width || !height) {
print("video: invalid video dimensions\n");
BinkClose(b);
SysFreeVm(buf);
exit_now(1);
}
// Prepare the Win32 BITMAPINFO structure.
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(bmi));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -(LONG)height; // Negative means top-down drawing!
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
uint32_t surface = 0;
status = SysCreateSurface(width, height, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (status != SYS_STATUS_SUCCESS || !surface) {
BinkClose(b);
SysFreeVm(buf);
fail_status("SysCreateSurface", status);
}
void* surfaceBase = 0;
uint32_t pitchBytes = 0;
status = SysMapSurface(surface, &surfaceBase, &pitchBytes);
if (status != SYS_STATUS_SUCCESS || !surfaceBase || pitchBytes < width * sizeof(uint32_t)) {
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
fail_status("SysMapSurface", status);
}
print("video: native surface ready\n");
DISPLAY_RECT fullFrame = { 0, 0, (int32_t)width, (int32_t)height };
while (1) {
if (BinkWait(b)) {
@@ -120,33 +241,26 @@ int main(void) {
continue;
}
BinkCopyToBuffer(b, fb, width * 4, height, 0, 0,
BINKSURFACE32);
BinkCopyToBuffer(b, surfaceBase, pitchBytes, height, 0, 0, BINKSURFACE32);
// Welcome to the GDI pipeline!
PAINTSTRUCT ps;
// Using NULL/0 as the HWND since we don't have a real window handle yet.
HDC hdc = BeginPaint((HWND)0, &ps);
StretchDIBits(
hdc,
0, 0, width, height, // Destination rect
0, 0, width, height, // Source rect
fb,
&bmi,
DIB_RGB_COLORS,
SRCCOPY
);
EndPaint((HWND)0, &ps);
status = SysPresentSurface(surface, &fullFrame, 1);
if (status != SYS_STATUS_SUCCESS) {
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
fail_status("SysPresentSurface", status);
}
if (b->FrameNum == b->Frames)
break;
BinkNextFrame(b);
}
WriteConsoleA(h, "video: goodbye\n", 15, 0, 0);
ExitProcess(0);
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
print("video: goodbye\n");
exit_now(0);
}
+16 -1
View File
@@ -72,12 +72,27 @@ extern int errno; // defined in posix_fd.c; single global in v1
#define SEEK_CUR 1
#define SEEK_END 2
// mmap() protection and mapping flags.
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_FAILED ((void*)(intptr_t)-1)
#define MS_SYNC 0x04
#define MS_ASYNC 0x01
#define MS_INVALIDATE 0x02
// Scalar types.
typedef int32_t pid_t;
typedef int32_t off_t;
typedef uint32_t mode_t;
typedef int64_t ssize_t;
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
__declspec(dllexport) int munmap(void* addr, size_t length);
__declspec(dllexport) int msync(void* addr, size_t length, int flags);
// Sockets.
typedef uint16_t sa_family_t;
@@ -188,7 +203,7 @@ __declspec(dllexport) int getaddrinfo(const char* node, const char* service, con
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res);
// pthread types.
typedef struct { uint32_t Tid; } pthread_t;
typedef struct { uint32_t Handle; uint32_t Tid; } pthread_t;
typedef struct { uint32_t Handle; } pthread_mutex_t;
typedef void* pthread_attr_t; // unused in v1
typedef void* pthread_mutexattr_t;
+4 -2
View File
@@ -81,7 +81,8 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
return (ssize_t)count;
case FD_FILE: {
size_t got = 0;
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(e->KHandle, buf, count, &got);
uint32_t s = SysReadFile(&req);
if (s != 0 && got == 0) { errno = PosixStatusToErrno(s); return -1; }
return (ssize_t)got;
}
@@ -115,7 +116,8 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
return (ssize_t)count;
case FD_FILE: {
size_t written = 0;
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(e->KHandle, buf, count, &written);
uint32_t s = SysWriteFile(&req);
if (s != 0 && written == 0) { errno = PosixStatusToErrno(s); return -1; }
return (ssize_t)written;
}
@@ -0,0 +1,85 @@
// POSIX compatibility support for mmap-style file mappings.
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
#include "posix.h"
#include "posix_fd.h"
static uint32_t PosixProtToSysMapAccess(int prot, int flags) {
uint32_t access = 0;
if (prot & PROT_EXEC) access |= SYS_MAP_ACCESS_EXECUTE;
if (prot & PROT_WRITE) {
access |= (flags & MAP_SHARED) ? SYS_MAP_ACCESS_WRITE : SYS_MAP_ACCESS_COPY;
} else if (flags & MAP_PRIVATE) {
access |= SYS_MAP_ACCESS_COPY;
}
if (prot & PROT_READ) access |= SYS_MAP_ACCESS_READ;
if (!access) access = SYS_MAP_ACCESS_READ;
return access;
}
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
int flags, int fd, off_t offset) {
if (addr || length == 0 || offset < 0 || ((uint64_t)offset & 0xFFFu)) {
errno = EINVAL;
return MAP_FAILED;
}
if (!(flags & MAP_PRIVATE) && !(flags & MAP_SHARED)) {
errno = EINVAL;
return MAP_FAILED;
}
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e) return MAP_FAILED;
if (e->Type != FD_FILE) {
errno = EBADF;
return MAP_FAILED;
}
uint64_t maxSize = (uint64_t)offset + (uint64_t)length;
if (maxSize < (uint64_t)offset) {
errno = EINVAL;
return MAP_FAILED;
}
uint32_t protect = (prot & PROT_WRITE) ? SYS_VM_READWRITE : SYS_VM_READ;
if (prot & PROT_EXEC)
protect = (prot & PROT_WRITE) ? SYS_VM_EXECUTE_READWRITE : SYS_VM_EXECUTE_READ;
uint32_t mapping = 0;
uint32_t s = SysCreateFileMapping(e->KHandle, protect, SYS_MAP_FLAG_NONE,
maxSize, &mapping);
if (s != SYS_STATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
void* base = (void*)0;
s = SysMapFile(mapping, (uint64_t)offset, length,
PosixProtToSysMapAccess(prot, flags),
SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
return base;
}
__declspec(dllexport) int munmap(void* addr, size_t length) {
(void)length;
if (!addr) { errno = EINVAL; return -1; }
uint32_t s = SysFreeVm(addr);
if (s != SYS_STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
return 0;
}
__declspec(dllexport) int msync(void* addr, size_t length, int flags) {
(void)addr;
(void)length;
(void)flags;
return 0;
}
+1 -1
View File
@@ -126,7 +126,7 @@ __declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
uint32_t handle = ProcTableFind(pid);
if (!handle) { errno = ESRCH; return -1; }
SysWaitForProcess(handle);
SysWait(handle, SYS_WAIT_FLAG_NONE);
int exitCode = 0;
SysGetProcessExitCode(handle, &exitCode);
+20 -7
View File
@@ -84,28 +84,41 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
tr->Pid = pid;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
(uint64_t)tr, 1, &tid);
uint32_t handle = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)PthreadTrampoline,
(uintptr_t)tr,
1,
0,
&handle,
&tid
};
uint32_t s = SysCreateThread(&req);
if (s != 0) {
free(tr);
return EAGAIN;
}
// The trampoline allocates the retval slot itself after it knows its real
// TID, avoiding the creator/new-thread race.
if (t) t->Tid = tid;
if (t) {
t->Handle = handle;
t->Tid = tid;
} else {
SysCloseHandle(handle);
}
return 0;
}
// Implements the pthread join operation.
__declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
uint32_t pid = SysGetCurrentPid();
uint32_t s = SysWaitThread(t.Tid);
// STATUS_NOT_FOUND (0x22) means already dead - treat as success.
if (s != 0 && s != 0x0022) return ESRCH;
uint32_t s = SysWait(t.Handle, SYS_WAIT_FLAG_NONE);
if (s != 0) return ESRCH;
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
if (retval) *retval = slot ? slot->RetVal : (void*)0;
if (slot) slot->Used = false;
SysCloseHandle(t.Handle);
return 0;
}
@@ -138,7 +151,7 @@ __declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
// Implements the pthread mutex lock operation.
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
uint32_t s = SysAcquireMutex(m->Handle);
uint32_t s = SysWait(m->Handle, SYS_WAIT_FLAG_NONE);
return s == 0 ? 0 : EINVAL;
}
+7 -92
View File
@@ -63,101 +63,16 @@ static int* srcX_map = NULL;
static int currentDestCapacity = 0;
static int currentXMapCapacity = 0;
__declspec(dllexport) int StretchDIBits(
HDC hdc,
int xDest, int yDest, int DestWidth, int DestHeight,
__declspec(dllexport) int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight,
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
const void *lpBits, const BITMAPINFO *lpbmi,
UINT iUsage, DWORD rop)
{
(void)hdc; (void)lpbmi; (void)iUsage; (void)rop;
(void)hdc; (void)xDest; (void)yDest; (void)DestWidth; (void)DestHeight;
(void)xSrc; (void)ySrc; (void)SrcWidth; (void)SrcHeight;
(void)lpBits; (void)lpbmi; (void)iUsage; (void)rop;
// Safety check to prevent division by zero.
if (DestWidth <= 0 || DestHeight <= 0 || SrcWidth <= 0 || SrcHeight <= 0) {
return 0;
}
// Handle trivial case (1:1 copy) to bypass allocation and scaling overhead.
if (DestWidth == SrcWidth && DestHeight == SrcHeight && xSrc == 0 && ySrc == 0) {
SysBlitScreen(xDest, yDest, DestWidth, DestHeight, (const uint32_t*)lpBits);
return DestHeight;
}
const uint32_t* srcPixels = (const uint32_t*)lpBits;
// Only reallocate if the new size is LARGER than what we already have.
int requiredPixels = DestWidth * DestHeight;
if (requiredPixels > currentDestCapacity) {
free(destBuffer);
destBuffer = (uint32_t*)malloc(requiredPixels * sizeof(uint32_t));
currentDestCapacity = requiredPixels;
}
if (DestWidth > currentXMapCapacity) {
free(srcX_map);
srcX_map = (int*)malloc(DestWidth * sizeof(int));
currentXMapCapacity = DestWidth;
}
for (int dx = 0; dx < DestWidth; ++dx) {
// Nearest neighbor coordinate mapping.
srcX_map[dx] = xSrc + (dx * SrcWidth) / DestWidth;
// Clamp to safe bounds.
if (srcX_map[dx] < 0) srcX_map[dx] = 0;
if (srcX_map[dx] >= SrcWidth) srcX_map[dx] = SrcWidth - 1;
}
// Process the image row by row.
for (int dy = 0; dy < DestHeight; ++dy) {
// Map destination Y to source Y.
int sy = ySrc + (dy * SrcHeight) / DestHeight;
if (sy < 0) sy = 0;
if (sy >= SrcHeight) sy = SrcHeight - 1;
// Assumes stride == SrcWidth (tightly packed 32bpp).
const uint32_t* srcRow = srcPixels + (sy * SrcWidth);
uint32_t* dstRow = destBuffer + (dy * DestWidth);
int dx = 0;
#if defined(__SSE2__) || defined(_M_X64)
// Process 4 pixels (16 bytes) per loop iteration using SSE2.
for (; dx <= DestWidth - 4; dx += 4) {
// Gather 4 source pixels using our precomputed X map.
// Note: _mm_set_epi32 takes arguments in reverse order (3, 2, 1, 0).
__m128i pixels = _mm_set_epi32(
srcRow[srcX_map[dx + 3]],
srcRow[srcX_map[dx + 2]],
srcRow[srcX_map[dx + 1]],
srcRow[srcX_map[dx + 0]]
);
// Issue a single 128-bit unaligned store.
_mm_storeu_si128((__m128i*)&dstRow[dx], pixels);
}
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
// Process 4 pixels per loop iteration using ARM NEON.
for (; dx <= DestWidth - 4; dx += 4) {
// Initialize a vector and populate lanes (avoids stack-memory hop).
uint32x4_t pixels = vdupq_n_u32(0);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 0]], pixels, 0);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 1]], pixels, 1);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 2]], pixels, 2);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 3]], pixels, 3);
// Issue a single 128-bit store.
vst1q_u32(&dstRow[dx], pixels);
}
#endif
// Epilogue: Handle remaining pixels if DestWidth isn't a multiple of 4.
for (; dx < DestWidth; ++dx) {
dstRow[dx] = srcRow[srcX_map[dx]];
}
}
// Direct mapping to kernel screen blit using the stretched buffer.
SysBlitScreen(xDest, yDest, DestWidth, DestHeight, destBuffer);
return DestHeight;
// There is no implicit display blit ABI. Native graphics clients own a
// surface and present it explicitly.
return 0;
}
+321 -97
View File
@@ -27,6 +27,8 @@
#include <intrin.h>
#include <ntlite/syscall.h>
extern size_t _malloc_usable_size(const void* ptr);
// Types.
typedef void* HANDLE;
typedef unsigned long DWORD;
@@ -58,6 +60,7 @@ typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
#define MEM_RELEASE 0x00008000UL
#define MEM_FREE 0x00010000UL
#define MEM_PRIVATE 0x00020000UL
#define MEM_MAPPED 0x00040000UL
#define PAGE_NOACCESS 0x01UL
#define PAGE_READONLY 0x02UL
#define PAGE_READWRITE 0x04UL
@@ -65,6 +68,13 @@ typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
#define PAGE_EXECUTE 0x10UL
#define PAGE_EXECUTE_READ 0x20UL
#define PAGE_EXECUTE_READWRITE 0x40UL
#define SEC_COMMIT 0x08000000UL
#define FILE_MAP_COPY 0x00000001UL
#define FILE_MAP_WRITE 0x00000002UL
#define FILE_MAP_READ 0x00000004UL
#define FILE_MAP_EXECUTE 0x00000020UL
#define FILE_MAP_ALL_ACCESS 0x000F001FUL
typedef struct _MEMORY_BASIC_INFORMATION {
void* BaseAddress;
@@ -90,6 +100,26 @@ static uint32_t WinProtectToSysVm(DWORD protect) {
}
}
static uint32_t WinFileMappingProtectToSysVm(DWORD protect) {
// Strip section attributes; the low byte carries PAGE_* protection.
DWORD pageProtect = protect & 0xFFu;
if (pageProtect == PAGE_WRITECOPY)
return SYS_VM_READ; // MapViewOfFile(FILE_MAP_COPY) asks for the private writable view.
return WinProtectToSysVm(pageProtect ? pageProtect : PAGE_READONLY);
}
static uint32_t WinFileMapAccessToSys(DWORD desiredAccess) {
uint32_t access = 0;
if (desiredAccess & FILE_MAP_EXECUTE) access |= SYS_MAP_ACCESS_EXECUTE;
if (desiredAccess & FILE_MAP_COPY) access |= SYS_MAP_ACCESS_COPY;
if (desiredAccess & FILE_MAP_WRITE) access |= SYS_MAP_ACCESS_WRITE;
if (desiredAccess & FILE_MAP_READ) access |= SYS_MAP_ACCESS_READ;
if ((desiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS)
access |= SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_COPY;
if (!access) access = SYS_MAP_ACCESS_READ;
return access;
}
// Implements the protect Vm To Win operation.
static DWORD SysProtectVmToWin(uint32_t protect) {
switch (protect) {
@@ -153,24 +183,9 @@ typedef struct _WIN32_FIND_DATAA {
static inline HANDLE KH_TO_HANDLE(uint32_t kh) { return (HANDLE)(uintptr_t)kh; }
static inline uint32_t HANDLE_TO_KH(HANDLE h) { return (uint32_t)(uintptr_t)h; }
// Thread IDs are not kernel handles. Keep Win32 thread pseudo-handles out of
// the low numeric handle namespace so CloseHandle/WaitForSingleObject cannot
// accidentally operate on an unrelated file/process handle with the same value.
#define WIN32_THREAD_HANDLE_TAG ((uintptr_t)1ULL << 63)
#define WIN32_MUTEX_SENTINEL ((uintptr_t)0x4D555458UL) // 'MUTX'
#define WIN32_SEMA_SENTINEL ((uintptr_t)0x53454D41UL) // 'SEMA'
static inline HANDLE TID_TO_THREAD_HANDLE(uint32_t tid) {
return (HANDLE)(WIN32_THREAD_HANDLE_TAG | (uintptr_t)tid);
}
static inline BOOL IS_THREAD_HANDLE(HANDLE h) {
return h && h != INVALID_HANDLE_VALUE && (((uintptr_t)h & WIN32_THREAD_HANDLE_TAG) != 0);
}
static inline uint32_t THREAD_HANDLE_TO_TID(HANDLE h) {
return (uint32_t)((uintptr_t)h & 0xFFFFFFFFu);
}
static inline BOOL IS_NOOP_SYNC_HANDLE(HANDLE h) {
static inline BOOL IS_VALID_KERNEL_HANDLE(HANDLE h) {
uintptr_t v = (uintptr_t)h;
return v == WIN32_MUTEX_SENTINEL || v == WIN32_SEMA_SENTINEL;
return v != 0 && v != (uintptr_t)INVALID_HANDLE_VALUE;
}
// Path translator.
@@ -268,11 +283,14 @@ static void K32DebugPrintA(const char* s) {
}
#define WIN32_TLS_SLOTS 64
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
typedef struct WIN32_THREAD_ENV {
DWORD LastError;
DWORD Reserved0;
void* TlsSlots[WIN32_TLS_SLOTS];
void* FlsSlots[FLS_MAX_SLOTS];
} WIN32_THREAD_ENV;
static WIN32_THREAD_ENV gFallbackThreadEnv;
@@ -418,11 +436,27 @@ __declspec(dllexport) uint64_t GetTickCount64(void) {
}
// Memory (heap argument is ignored - single user-mode heap).
#define HEAP_ZERO_MEMORY 0x00000008UL
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010UL
__declspec(dllexport) void* HeapAlloc(void* heap, DWORD flags, size_t size) {
(void)heap; (void)flags;
(void)heap;
if (!size) return NULL;
void* ptr = malloc(size);
if (!ptr) gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
if (!ptr) {
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return NULL;
}
// Bink and the Microsoft CRT routinely allocate internal state with
// HEAP_ZERO_MEMORY. Ignoring this flag only works accidentally while the
// heap keeps receiving fresh demand-zero pages; under low memory / swap the
// allocator reuses dirty freed blocks and stale pitch/pointer fields can
// corrupt the decoder.
if (flags & HEAP_ZERO_MEMORY)
memset(ptr, 0, size);
return ptr;
}
@@ -436,9 +470,28 @@ __declspec(dllexport) BOOL HeapFree(void* heap, DWORD flags, void* ptr) {
__declspec(dllexport) void* HeapReAlloc(void* heap, DWORD flags,
void* ptr, size_t size) {
(void)heap; (void)flags;
(void)heap;
if (flags & HEAP_REALLOC_IN_PLACE_ONLY) {
// The user heap does not expose an in-place grow primitive. Preserve
// Windows semantics by failing instead of silently moving the block.
size_t oldSize = _malloc_usable_size(ptr);
if (oldSize != (size_t)-1 && oldSize >= size)
return ptr;
gLastError = 8; // ERROR_NOT_ENOUGH_MEMORY
return NULL;
}
size_t oldSize = ptr ? _malloc_usable_size(ptr) : 0;
void* newptr = realloc(ptr, size);
if (!newptr && size) gLastError = 8;
if (!newptr && size) {
gLastError = 8;
return NULL;
}
if (newptr && (flags & HEAP_ZERO_MEMORY) && oldSize != (size_t)-1 && size > oldSize)
memset((unsigned char*)newptr + oldSize, 0, size - oldSize);
return newptr;
}
@@ -508,10 +561,88 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
lpBuffer->RegionSize = info.PageCount * 4096u;
lpBuffer->State = MEM_COMMIT;
lpBuffer->Protect = protect;
lpBuffer->Type = MEM_PRIVATE;
lpBuffer->Type = (info.Type == 7u) ? MEM_MAPPED : MEM_PRIVATE; // 7 == MmVadMappedFile
return sizeof(MEMORY_BASIC_INFORMATION);
}
__declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
void* lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
const char* lpName) {
(void)lpFileMappingAttributes;
(void)lpName;
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
gLastError = 6; // ERROR_INVALID_HANDLE
return NULL;
}
uint64_t maximumSize = ((uint64_t)dwMaximumSizeHigh << 32) | (uint64_t)dwMaximumSizeLow;
uint32_t kh = 0;
uint32_t s = SysCreateFileMapping(HANDLE_TO_KH(hFile),
WinFileMappingProtectToSysVm(flProtect),
SYS_MAP_FLAG_NONE,
maximumSize,
&kh);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return KH_TO_HANDLE(kh);
}
__declspec(dllexport) HANDLE CreateFileMappingW(HANDLE hFile,
void* lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
const unsigned short* lpName) {
(void)lpName;
return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect,
dwMaximumSizeHigh, dwMaximumSizeLow, NULL);
}
__declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
size_t dwNumberOfBytesToMap) {
if (!hFileMappingObject || hFileMappingObject == INVALID_HANDLE_VALUE) {
gLastError = 6;
return NULL;
}
uint64_t offset = ((uint64_t)dwFileOffsetHigh << 32) | (uint64_t)dwFileOffsetLow;
void* base = NULL;
uint32_t s = SysMapFile(HANDLE_TO_KH(hFileMappingObject),
offset,
dwNumberOfBytesToMap,
WinFileMapAccessToSys(dwDesiredAccess),
SYS_MAP_FLAG_NONE,
&base);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return base;
}
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
uint32_t s = SysFreeVm((void*)lpBaseAddress);
if (s != SYS_STATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
__declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwNumberOfBytesToFlush) {
(void)dwNumberOfBytesToFlush;
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
// Native flush is file-handle based for now; unmap/close also harvests dirty
// PTEs, so FlushViewOfFile remains a successful cache-coherency hint.
return TRUE;
}
// --- Win32 API ---.
// GetAsyncKeyState implementation over the canonical input key namespace.
@@ -720,7 +851,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return 0xFFFFFFFFUL; }
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
@@ -731,25 +862,27 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
DWORD* bytesRead, void* overlapped) {
(void)overlapped;
if (bytesRead) *bytesRead = 0;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) {
gLastError = 6;
return FALSE;
}
size_t got = 0;
uint32_t s = SysReadFile(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toRead, &got);
uint32_t s = SysReadFile(&req);
if (bytesRead) *bytesRead = (DWORD)got;
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but consuming stale stack/heap bytes after them makes
// the failure mode look like random game-data corruption.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
if (s != 0) {
gLastError = 1;
return FALSE;
}
// Harden callers that accidentally look past *bytesRead. EOF/short reads
// are legal for files, but do not touch the caller's remaining buffer after
// a failed read because that can fault in megabytes under low memory.
if (got < (size_t)toRead)
memset((unsigned char*)buf + got, 0, (size_t)toRead - got);
return TRUE;
}
@@ -758,9 +891,10 @@ __declspec(dllexport) BOOL ReadFile(HANDLE hFile, void* buf, DWORD toRead,
__declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrite,
DWORD* bytesWritten, void* overlapped) {
(void)overlapped;
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile) || !buf) { gLastError = 6; return FALSE; }
if (!hFile || hFile == INVALID_HANDLE_VALUE || !buf) { gLastError = 6; return FALSE; }
size_t written = 0;
uint32_t s = SysWriteFile(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(HANDLE_TO_KH(hFile), buf, (size_t)toWrite, &written);
uint32_t s = SysWriteFile(&req);
if (bytesWritten) *bytesWritten = (DWORD)written;
if (s != 0) { gLastError = 29; return FALSE; } // ERROR_WRITE_FAULT
return TRUE;
@@ -770,7 +904,7 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit
// Returns new position low DWORD, or 0xFFFFFFFF on error.
__declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
long* distHigh, DWORD method) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) {
gLastError = 6;
return 0xFFFFFFFFUL;
}
@@ -813,7 +947,7 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
// SetEndOfFile - truncate or extend the file to the current seek position.
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE || IS_THREAD_HANDLE(hFile)) { gLastError = 6; return FALSE; }
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
@@ -834,14 +968,10 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
return TRUE;
}
// CloseHandle - unified close for files, mutexes, semaphores, events, queues.
// CloseHandle - unified close for files, threads, mutexes, semaphores, events, queues, and processes.
__declspec(dllexport) BOOL CloseHandle(HANDLE hObject) {
if (!hObject || hObject == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
// Our thread/sentinel sync handles are not entries in the kernel handle table.
// There is nothing to close; the thread object is reaped by the scheduler.
if (IS_THREAD_HANDLE(hObject) || IS_NOOP_SYNC_HANDLE(hObject)) return TRUE;
SysCloseHandle(HANDLE_TO_KH(hObject));
return TRUE;
}
@@ -870,8 +1000,11 @@ __declspec(dllexport) BOOL HeapDestroy(HANDLE hHeap) {
// Implements the heap Size operation.
__declspec(dllexport) size_t HeapSize(HANDLE heap, DWORD flags, const void* ptr) {
(void)heap; (void)flags; (void)ptr;
return (size_t)-1; // Win32 returns (SIZE_T)-1 on error / unsupported
(void)heap; (void)flags;
size_t size = _malloc_usable_size(ptr);
if (size == (size_t)-1)
gLastError = 87; // ERROR_INVALID_PARAMETER
return size;
}
__declspec(dllexport) BOOL HeapSetInformation(HANDLE HeapHandle,
@@ -1050,9 +1183,29 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
// Implements the terminate Process operation.
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
(void)hProcess;
SysExitProcess((int)uExitCode);
while (true) ;
if (hProcess == GetCurrentProcess()) {
SysExitProcess((int)uExitCode);
while (true) ;
}
if (!hProcess) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
uint32_t pid = 0;
uint32_t s = SysGetProcessPid(HANDLE_TO_KH(hProcess), &pid);
if (s != 0 || pid == 0) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
s = SysKillProcess(pid, (int)uExitCode);
if (s != 0) {
gLastError = 5; // ERROR_ACCESS_DENIED / generic failure for now
return FALSE;
}
return TRUE;
}
// STARTUPINFOA - only cb matters for our stub.
@@ -1099,35 +1252,42 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
DWORD* lpThreadId) {
(void)lpThreadAttributes; (void)dwStackSize; (void)dwCreationFlags;
uint32_t tid = 0;
uint32_t s = SysCreateThread((uint64_t)(uintptr_t)lpStartAddress,
(uint64_t)(uintptr_t)lpParameter,
0, &tid);
uint32_t kh = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)lpStartAddress,
(uintptr_t)lpParameter,
0,
0,
&kh,
&tid
};
uint32_t s = SysCreateThread(&req);
if (s != 0) { gLastError = 11; return INVALID_HANDLE_VALUE; }
if (lpThreadId) *lpThreadId = (DWORD)tid;
return TID_TO_THREAD_HANDLE(tid);
return KH_TO_HANDLE(kh);
}
// WaitForSingleObject - blocks on process handles via SysWaitForProcess;
// thread and other handle types are not yet waitable so return immediately.
// WaitForSingleObject - blocks on native waitable handles. A zero timeout is
// only a probe for mutexes/semaphores until native timeout waits exist.
__declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
(void)dwMilliseconds;
if (!hHandle || hHandle == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
if (IS_THREAD_HANDLE(hHandle)) {
uint32_t tid = THREAD_HANDLE_TO_TID(hHandle);
uint32_t s = SysWaitThread(tid);
if (s != 0 && s != 0x0022) { gLastError = 6; return 0xFFFFFFFFUL; }
return WAIT_OBJECT_0;
}
if (IS_NOOP_SYNC_HANDLE(hHandle)) return WAIT_OBJECT_0;
if (!IS_VALID_KERNEL_HANDLE(hHandle)) { gLastError = 6; return 0xFFFFFFFFUL; }
uint32_t kh = HANDLE_TO_KH(hHandle);
if (kh) {
uint32_t s = SysWaitForProcess(kh);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
if (dwMilliseconds == 0) {
uint32_t s = SysTryAcquireMutex(kh);
if (s == 0) return WAIT_OBJECT_0;
s = SysTryWaitSem(kh);
if (s == 0) return WAIT_OBJECT_0;
return WAIT_TIMEOUT;
}
return WAIT_OBJECT_0;
uint32_t s = SysWait(kh, SYS_WAIT_FLAG_NONE);
if (s == 0) return WAIT_OBJECT_0;
gLastError = 6;
return 0xFFFFFFFFUL;
}
@@ -1250,7 +1410,7 @@ __declspec(dllexport) BOOL CreateProcessA(
__declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode) {
if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh || IS_THREAD_HANDLE(hProcess)) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
@@ -1791,57 +1951,123 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)bInitialOwner; (void)lpName;
return (HANDLE)WIN32_MUTEX_SENTINEL;
(void)lpMutexAttributes; (void)lpName;
uint32_t kh = 0;
uint32_t s = SysCreateMutex(&kh);
if (s != 0 || !kh) { gLastError = 8; return NULL; }
if (bInitialOwner) {
s = SysWait(kh, SYS_WAIT_FLAG_NONE);
if (s != 0) {
SysCloseHandle(kh);
gLastError = 1;
return NULL;
}
}
return KH_TO_HANDLE(kh);
}
// Releases mutex.
__declspec(dllexport) BOOL ReleaseMutex(HANDLE hMutex) {
(void)hMutex;
if (!IS_VALID_KERNEL_HANDLE(hMutex)) { gLastError = 6; return FALSE; }
uint32_t s = SysReleaseMutex(HANDLE_TO_KH(hMutex));
if (s != 0) { gLastError = 6; return FALSE; }
return TRUE;
}
__declspec(dllexport) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes,
long lInitialCount, long lMaximumCount,
const char* lpName) {
(void)lpSemaphoreAttributes; (void)lInitialCount;
(void)lMaximumCount; (void)lpName;
return (HANDLE)WIN32_SEMA_SENTINEL;
(void)lpSemaphoreAttributes; (void)lpName;
uint32_t kh = 0;
uint32_t s = SysCreateSem((int32_t)lInitialCount, (int32_t)lMaximumCount, &kh);
if (s != 0 || !kh) { gLastError = 8; return NULL; }
return KH_TO_HANDLE(kh);
}
__declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCount,
long* lpPreviousCount) {
(void)hSemaphore; (void)lReleaseCount;
if (!IS_VALID_KERNEL_HANDLE(hSemaphore) || lReleaseCount < 0) { gLastError = 6; return FALSE; }
if (lpPreviousCount) *lpPreviousCount = 0;
for (long i = 0; i < lReleaseCount; ++i) {
uint32_t s = SysSignalSem(HANDLE_TO_KH(hSemaphore));
if (s != 0) { gLastError = 6; return FALSE; }
}
return TRUE;
}
// Critical Sections & Condition Variables
// Single-threaded: all locks are no-ops.
// Minimal opaque types; callers allocate these from their Windows headers -
// we just need the pointer to be accepted by our stubs.
// Critical Sections & Condition Variables.
// Match the size of Win64 RTL_CRITICAL_SECTION (5 pointer-sized fields) while
// using the storage as a small recursive spin lock. Bink uses these from its
// worker/copy paths, so the old no-op implementation allowed internal state
// races as soon as CreateThread started working.
typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION;
typedef struct { void* _ptr; } CONDITION_VARIABLE;
#define K32_CS_LOCK 0
#define K32_CS_OWNER 1
#define K32_CS_RECURSION 2
static volatile long* K32CsLockWord(CRITICAL_SECTION* cs) {
return (volatile long*)&cs->_opaque[K32_CS_LOCK];
}
static DWORD K32CurrentTidOrOne(void) {
DWORD tid = (DWORD)SysGetCurrentTid();
return tid ? tid : 1;
}
// Initializes critical Section.
__declspec(dllexport) void InitializeCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS;
if (!lpCS) return;
memset(lpCS, 0, sizeof(*lpCS));
}
__declspec(dllexport) BOOL InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION* lpCS,
DWORD dwSpinCount) {
(void)lpCS; (void)dwSpinCount;
(void)dwSpinCount;
InitializeCriticalSection(lpCS);
return TRUE;
}
// Releases critical Section.
__declspec(dllexport) void DeleteCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
// Implements the enter Critical Section operation.
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void EnterCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
lpCS->_opaque[K32_CS_RECURSION]++;
return;
}
while (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
_mm_pause();
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
}
// Implements the leave Critical Section operation.
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) { (void)lpCS; }
__declspec(dllexport) void LeaveCriticalSection(CRITICAL_SECTION* lpCS) {
if (!lpCS) return;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] != tid || lpCS->_opaque[K32_CS_RECURSION] == 0)
return;
lpCS->_opaque[K32_CS_RECURSION]--;
if (lpCS->_opaque[K32_CS_RECURSION] == 0) {
lpCS->_opaque[K32_CS_OWNER] = 0;
_ReadWriteBarrier();
_InterlockedExchange(K32CsLockWord(lpCS), 0);
}
}
// Implements the try Enter Critical Section operation.
__declspec(dllexport) BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCS) {
(void)lpCS; return TRUE;
if (!lpCS) return FALSE;
DWORD tid = K32CurrentTidOrOne();
if ((DWORD)lpCS->_opaque[K32_CS_OWNER] == tid) {
lpCS->_opaque[K32_CS_RECURSION]++;
return TRUE;
}
if (_InterlockedCompareExchange(K32CsLockWord(lpCS), 1, 0) != 0)
return FALSE;
lpCS->_opaque[K32_CS_OWNER] = tid;
lpCS->_opaque[K32_CS_RECURSION] = 1;
return TRUE;
}
// Initializes condition Variable.
@@ -2357,14 +2583,12 @@ __declspec(dllexport) int GetLocaleInfoA(unsigned int Locale, DWORD LCType,
return 0;
}
// Fiber Local Storage (FLS)
// We implement a minimal 64-slot static table so callers that use FLS for
// thread-local-ish state don't immediately fail.
// Fiber Local Storage (FLS).
// Allocation state is process-global, but the values themselves are per-thread,
// matching the contract that Bink and the CRT expect when worker threads are
// enabled. The old single global value array let unrelated threads overwrite
// each other's decoder-local state.
#define FLS_MAX_SLOTS 64
#define FLS_OUT_OF_INDEXES 0xFFFFFFFFUL
static void* gFlsSlots[FLS_MAX_SLOTS];
static int gFlsUsed[FLS_MAX_SLOTS]; // 0 = free, 1 = allocated
// Implements the fls Alloc operation.
@@ -2372,8 +2596,8 @@ __declspec(dllexport) DWORD FlsAlloc(void* lpCallback) {
(void)lpCallback;
for (int i = 0; i < FLS_MAX_SLOTS; i++) {
if (!gFlsUsed[i]) {
gFlsUsed[i] = 1;
gFlsSlots[i] = NULL;
gFlsUsed[i] = 1;
K32ThreadEnv()->FlsSlots[i] = NULL;
return (DWORD)i;
}
}
@@ -2387,7 +2611,7 @@ __declspec(dllexport) void* FlsGetValue(DWORD dwFlsIndex) {
gLastError = 87; // ERROR_INVALID_PARAMETER
return NULL;
}
return gFlsSlots[dwFlsIndex];
return K32ThreadEnv()->FlsSlots[dwFlsIndex];
}
// Implements the fls Set Value operation.
@@ -2396,7 +2620,7 @@ __declspec(dllexport) BOOL FlsSetValue(DWORD dwFlsIndex, void* lpFlsData) {
gLastError = 87;
return FALSE;
}
gFlsSlots[dwFlsIndex] = lpFlsData;
K32ThreadEnv()->FlsSlots[dwFlsIndex] = lpFlsData;
return TRUE;
}
@@ -2406,8 +2630,8 @@ __declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
gLastError = 87;
return FALSE;
}
gFlsUsed[dwFlsIndex] = 0;
gFlsSlots[dwFlsIndex] = NULL;
gFlsUsed[dwFlsIndex] = 0;
K32ThreadEnv()->FlsSlots[dwFlsIndex] = NULL;
return TRUE;
}
+23
View File
@@ -210,6 +210,29 @@ void* _calloc(size_t count, size_t size) {
return ptr;
}
// Returns the usable payload size of an allocation made by this heap.
size_t _malloc_usable_size(const void* ptr) {
if (!ptr) return (size_t)-1;
HeapLock();
HEAP_SEGMENT* seg = SegmentForPointer((void*)ptr);
if (!seg) {
HeapUnlock();
return (size_t)-1;
}
HEAP_BLOCK* block = PayloadBlock((void*)ptr);
if (block->Magic != USER_HEAP_MAGIC || block->Free) {
HeapUnlock();
return (size_t)-1;
}
size_t size = block->Size;
HeapUnlock();
return size;
}
// Implements the realloc operation.
void* _realloc(void* ptr, size_t newSize) {
if (!ptr) return _malloc(newSize);
+5
View File
@@ -12,6 +12,7 @@ void* _malloc(size_t size);
void _free(void* ptr);
void* _calloc(size_t count, size_t size);
void* _realloc(void* ptr, size_t size);
size_t _malloc_usable_size(const void* ptr);
HEAP_EXPORT void* malloc(size_t size) {
return _malloc(size);
@@ -28,3 +29,7 @@ HEAP_EXPORT void* calloc(size_t count, size_t size) {
HEAP_EXPORT void* realloc(void* ptr, size_t size) {
return _realloc(ptr, size);
}
HEAP_EXPORT size_t malloc_usable_size(const void* ptr) {
return _malloc_usable_size(ptr);
}
+49 -38
View File
@@ -3,13 +3,13 @@
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_WAIT_FOR_PROCESS EQU 3
SYS_WAIT_SINGLE EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_WAIT_THREAD EQU 9
SYS_RETIRED_WAIT_THREAD EQU 9
SYS_GET_CURRENT_TID EQU 10
SYS_GET_CURRENT_PID EQU 11
SYS_SET_THREAD_PRIORITY EQU 12
@@ -23,17 +23,17 @@ SYS_PROTECT_VM EQU 19
SYS_QUERY_VM EQU 20
SYS_CLOSE_HANDLE EQU 21
SYS_CREATE_MUTEX EQU 22
SYS_ACQUIRE_MUTEX EQU 23
SYS_RETIRED_ACQUIRE_MUTEX EQU 23
SYS_TRY_ACQUIRE_MUTEX EQU 24
SYS_RELEASE_MUTEX EQU 25
SYS_CREATE_SEM EQU 26
SYS_WAIT_SEM EQU 27
SYS_RETIRED_WAIT_SEM EQU 27
SYS_TRY_WAIT_SEM EQU 28
SYS_SIGNAL_SEM EQU 29
SYS_CREATE_EVENT EQU 30
SYS_SIGNAL_EVENT EQU 31
SYS_CLEAR_EVENT EQU 32
SYS_WAIT_EVENT EQU 33
SYS_RETIRED_WAIT_EVENT EQU 33
SYS_CREATE_QUEUE EQU 34
SYS_SEND_QUEUE EQU 35
SYS_RECV_QUEUE EQU 36
@@ -66,36 +66,47 @@ SYS_SET_CONSOLE_CURSOR EQU 62
SYS_FILL_CONSOLE_CHARACTER EQU 63
SYS_FILL_CONSOLE_ATTRIBUTE EQU 64
SYS_PUT_SCREEN_CHAR EQU 65
SYS_BLIT_SCREEN EQU 66
SYS_CLEAR_SCREEN EQU 67
SYS_GET_SCREEN_SIZE EQU 68
SYS_QUERY_DISPLAY_INFO EQU 69
SYS_POLL_KEYBOARD EQU 70
SYS_IS_KEY_DOWN EQU 71
SYS_READ_KEYBOARD_CHAR EQU 72
SYS_READ_KEY EQU 73
SYS_GET_KEY_MODIFIERS EQU 74
SYS_POLL_MOUSE EQU 75
SYS_READ_MOUSE EQU 76
SYS_GET_MOUSE_POS EQU 77
SYS_GET_TICK EQU 78
SYS_PRINT_DEBUG EQU 79
SYS_QUERY_TRAP_FRAME EQU 80
SYS_CREATE_SOCKET EQU 81
SYS_BIND_SOCKET EQU 82
SYS_LISTEN_SOCKET EQU 83
SYS_ACCEPT_SOCKET EQU 84
SYS_CONNECT_SOCKET EQU 85
SYS_SEND_SOCKET EQU 86
SYS_RECV_SOCKET EQU 87
SYS_SHUTDOWN_SOCKET EQU 88
SYS_SEND_TO_SOCKET EQU 89
SYS_RECV_FROM_SOCKET EQU 90
SYS_RESOLVE_HOST EQU 91
SYS_SET_SOCKET_OPTION EQU 92
SYS_GET_SOCKET_OPTION EQU 93
SYS_POLL EQU 94
SYS_GET_MEMORY_STATUS EQU 95
SYS_QUERY_KERNEL_STATS EQU 96
SYS_GET_SYSTEM_TIME EQU 97
SYS_MAX EQU 98
SYS_CLEAR_SCREEN EQU 66
SYS_GET_SCREEN_SIZE EQU 67
SYS_QUERY_DISPLAY_INFO EQU 68
SYS_POLL_KEYBOARD EQU 69
SYS_IS_KEY_DOWN EQU 70
SYS_READ_KEYBOARD_CHAR EQU 71
SYS_READ_KEY EQU 72
SYS_GET_KEY_MODIFIERS EQU 73
SYS_POLL_MOUSE EQU 74
SYS_READ_MOUSE EQU 75
SYS_GET_MOUSE_POS EQU 76
SYS_GET_TICK EQU 77
SYS_PRINT_DEBUG EQU 78
SYS_QUERY_TRAP_FRAME EQU 79
SYS_CREATE_SOCKET EQU 80
SYS_BIND_SOCKET EQU 81
SYS_LISTEN_SOCKET EQU 82
SYS_ACCEPT_SOCKET EQU 83
SYS_CONNECT_SOCKET EQU 84
SYS_SEND_SOCKET EQU 85
SYS_RECV_SOCKET EQU 86
SYS_SHUTDOWN_SOCKET EQU 87
SYS_SEND_TO_SOCKET EQU 88
SYS_RECV_FROM_SOCKET EQU 89
SYS_RESOLVE_HOST EQU 90
SYS_SET_SOCKET_OPTION EQU 91
SYS_GET_SOCKET_OPTION EQU 92
SYS_POLL EQU 93
SYS_GET_MEMORY_STATUS EQU 94
SYS_QUERY_KERNEL_STATS EQU 95
SYS_GET_SYSTEM_TIME EQU 96
SYS_CREATE_FILE_MAPPING EQU 97
SYS_MAP_FILE EQU 98
SYS_CREATE_SURFACE EQU 99
SYS_MAP_SURFACE EQU 100
SYS_QUERY_SURFACE EQU 101
SYS_PRESENT_SURFACE EQU 102
SYS_GET_THREAD_LIST EQU 103
SYS_READ_INPUT_EVENT EQU 104
SYS_PEEK_INPUT_EVENT EQU 105
SYS_WAIT_MANY EQU 106
SYS_OPEN_INPUT_QUEUE EQU 107
SYS_FLUSH_FILE EQU 108
SYS_MAX EQU 109
+36 -7
View File
@@ -12,7 +12,8 @@
; 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_STUB2/3 - 4 or fewer args: pure register passing, no stack read.
; SYSCALL_STUB5 - exactly 5 args: loads [rsp+28h] -> R10 only.
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
;
; Handle lifecycle:
@@ -44,6 +45,27 @@ Sys&name PROC EXPORT
Sys&name ENDP
ENDM
; ---------------------------------------------------------------------------
SYSCALL_STUB3 MACRO name, nr
PUBLIC Sys&name
Sys&name PROC EXPORT
mov rax, nr
int 40h
ret
Sys&name ENDP
ENDM
; ---------------------------------------------------------------------------
SYSCALL_STUB5 MACRO name, nr
PUBLIC Sys&name
Sys&name PROC EXPORT
mov r10, [rsp+28h] ; 5th argument
mov rax, nr
int 40h
ret
Sys&name ENDP
ENDM
; ---------------------------------------------------------------------------
SYSCALL_STUB6 MACRO name, nr
PUBLIC Sys&name
@@ -62,14 +84,16 @@ ENDM
SYSCALL_STUB2 ExitProcess, SYS_EXIT_PROCESS
SYSCALL_STUB2 ExitThread, SYS_EXIT_THREAD
SYSCALL_STUB2 CreateProcess, SYS_CREATE_PROCESS
SYSCALL_STUB2 WaitForProcess, SYS_WAIT_FOR_PROCESS
SYSCALL_STUB3 WaitSingle, SYS_WAIT_SINGLE
SYSCALL_STUB2 WaitMany, SYS_WAIT_MANY
SYSCALL_STUB2 OpenInputQueue, SYS_OPEN_INPUT_QUEUE
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
SYSCALL_STUB2 KillProcess, SYS_KILL_PROCESS
SYSCALL_STUB2 GetProcessList, SYS_GET_PROCESS_LIST
SYSCALL_STUB2 GetProcessPid, SYS_GET_PROCESS_PID
SYSCALL_STUB2 GetThreadList, SYS_GET_THREAD_LIST
SYSCALL_STUB2 CreateThread, SYS_CREATE_THREAD
SYSCALL_STUB2 WaitThread, SYS_WAIT_THREAD
SYSCALL_STUB2 GetCurrentTid, SYS_GET_CURRENT_TID
SYSCALL_STUB2 GetCurrentPid, SYS_GET_CURRENT_PID
SYSCALL_STUB2 SetThreadPriority, SYS_SET_THREAD_PRIORITY
@@ -120,19 +144,16 @@ SYSCALL_STUB2 CloseHandle, SYS_CLOSE_HANDLE
; Synchronization objects
; ---------------------------------------------------------------------------
SYSCALL_STUB2 CreateMutex, SYS_CREATE_MUTEX
SYSCALL_STUB2 AcquireMutex, SYS_ACQUIRE_MUTEX
SYSCALL_STUB2 TryAcquireMutex, SYS_TRY_ACQUIRE_MUTEX
SYSCALL_STUB2 ReleaseMutex, SYS_RELEASE_MUTEX
SYSCALL_STUB2 CreateSem, SYS_CREATE_SEM
SYSCALL_STUB2 WaitSem, SYS_WAIT_SEM
SYSCALL_STUB2 TryWaitSem, SYS_TRY_WAIT_SEM
SYSCALL_STUB2 SignalSem, SYS_SIGNAL_SEM
SYSCALL_STUB2 CreateEvent, SYS_CREATE_EVENT
SYSCALL_STUB2 SignalEvent, SYS_SIGNAL_EVENT
SYSCALL_STUB2 ClearEvent, SYS_CLEAR_EVENT
SYSCALL_STUB2 WaitEvent, SYS_WAIT_EVENT
; ---------------------------------------------------------------------------
; Queues and named queues
@@ -159,6 +180,7 @@ SYSCALL_STUB2 SeekFile, SYS_SEEK_FILE
SYSCALL_STUB2 GetFilePos, SYS_GET_FILE_POS
SYSCALL_STUB2 GetFileSize, SYS_GET_FILE_SIZE
SYSCALL_STUB2 TruncateFile, SYS_TRUNCATE_FILE
SYSCALL_STUB2 FlushFile, SYS_FLUSH_FILE
SYSCALL_STUB2 DeleteFile, SYS_DELETE_FILE
SYSCALL_STUB2 RenameFile, SYS_RENAME_FILE
@@ -178,7 +200,10 @@ SYSCALL_STUB2 SetConsoleCursor, SYS_SET_CONSOLE_CURSOR
SYSCALL_STUB6 FillConsoleCharacter, SYS_FILL_CONSOLE_CHARACTER
SYSCALL_STUB6 FillConsoleAttribute, SYS_FILL_CONSOLE_ATTRIBUTE
SYSCALL_STUB6 PutScreenChar, SYS_PUT_SCREEN_CHAR
SYSCALL_STUB6 BlitScreen, SYS_BLIT_SCREEN
SYSCALL_STUB5 CreateSurface, SYS_CREATE_SURFACE
SYSCALL_STUB2 MapSurface, SYS_MAP_SURFACE
SYSCALL_STUB2 QuerySurface, SYS_QUERY_SURFACE
SYSCALL_STUB2 PresentSurface, SYS_PRESENT_SURFACE
SYSCALL_STUB2 ClearScreen, SYS_CLEAR_SCREEN
SYSCALL_STUB2 GetScreenSize, SYS_GET_SCREEN_SIZE
SYSCALL_STUB2 QueryDisplayInfo, SYS_QUERY_DISPLAY_INFO
@@ -191,6 +216,8 @@ SYSCALL_STUB2 IsKeyDown, SYS_IS_KEY_DOWN
SYSCALL_STUB2 ReadKeyboardChar, SYS_READ_KEYBOARD_CHAR
SYSCALL_STUB2 ReadKey, SYS_READ_KEY
SYSCALL_STUB2 GetKeyModifiers, SYS_GET_KEY_MODIFIERS
SYSCALL_STUB2 ReadInputEvent, SYS_READ_INPUT_EVENT
SYSCALL_STUB2 PeekInputEvent, SYS_PEEK_INPUT_EVENT
SYSCALL_STUB2 PollMouse, SYS_POLL_MOUSE
SYSCALL_STUB2 ReadMouse, SYS_READ_MOUSE
@@ -224,5 +251,7 @@ SYSCALL_STUB2 Poll, SYS_POLL
SYSCALL_STUB2 GetMemoryStatus, SYS_GET_MEMORY_STATUS
SYSCALL_STUB2 QueryKernelStats, SYS_QUERY_KERNEL_STATS
SYSCALL_STUB2 GetSystemTime, SYS_GET_SYSTEM_TIME
SYSCALL_STUB5 CreateFileMapping, SYS_CREATE_FILE_MAPPING
SYSCALL_STUB6 MapFile, SYS_MAP_FILE
END
+2 -2
View File
@@ -76,9 +76,9 @@ void _start(void)
}
debug_print("[init] waiting\n");
s = SysWaitForProcess(h);
s = SysWait(h, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS)
debug_print("[init] WARNING: SysWaitForProcess returned error\n");
debug_print("[init] WARNING: SysWait returned error\n");
int exitCode = 0;
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
+36 -6
View File
@@ -15,10 +15,35 @@ static void print(const char* s) {
SysWriteConsole(&req);
}
// Opens file.
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
// Opens the first available executable payload for async I/O.
// Tests are usually launched from /apps, so a bare "system/init.exe"
// resolves as /apps/system/init.exe and fails. Prefer the test binary itself,
// then fall back to absolute/drive-qualified paths.
static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) {
static const char* paths[] = {
"async_apc.exe",
"/apps/async_apc.exe",
"1:/apps/async_apc.exe",
"0:/apps/async_apc.exe",
"/system/init.exe",
"0:/system/init.exe",
"1:/system/init.exe",
"system/init.exe",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*handleOut = h;
if (pathOut)
*pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
// Prints hex8.
@@ -74,7 +99,8 @@ int main(void) {
}
uint32_t file = 0;
s = open_file("system/init.exe", &file);
const char* openedPath = 0;
s = open_test_file(&file, &openedPath);
if (s != SYS_STATUS_SUCCESS) {
print("async_apc: open failed status=");
print_u64(s);
@@ -82,6 +108,10 @@ int main(void) {
SysExitProcess(1);
}
print("async_apc: using ");
print(openedPath ? openedPath : "<unknown>");
print("\n");
uint32_t event = 0;
s = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (s != SYS_STATUS_SUCCESS) {
@@ -125,7 +155,7 @@ int main(void) {
}
while (!state.Delivered) {
s = SysWaitEvent(event, 1);
s = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
print("async_apc: alertable wait returned ");
print_u64(s);
print("\n");
+36 -6
View File
@@ -14,10 +14,35 @@ static void print(const char* s) {
SysWriteConsole(&req);
}
// Opens file.
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
// Opens the first available executable payload for async I/O.
// Tests are usually launched from /apps, so a bare "system/init.exe"
// resolves as /apps/system/init.exe and fails. Prefer the test binary itself,
// then fall back to absolute/drive-qualified paths.
static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) {
static const char* paths[] = {
"async_cancel.exe",
"/apps/async_cancel.exe",
"1:/apps/async_cancel.exe",
"0:/apps/async_cancel.exe",
"/system/init.exe",
"0:/system/init.exe",
"1:/system/init.exe",
"system/init.exe",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*handleOut = h;
if (pathOut)
*pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
// Prints u64.
@@ -46,12 +71,17 @@ int main(void) {
uint32_t event = 0;
uint32_t request = 0;
uint32_t s = open_file("system/init.exe", &file);
const char* openedPath = 0;
uint32_t s = open_test_file(&file, &openedPath);
if (s != SYS_STATUS_SUCCESS) {
print("async_cancel: open failed status="); print_u64(s); print("\n");
SysExitProcess(1);
}
print("async_cancel: using ");
print(openedPath ? openedPath : "<unknown>");
print("\n");
s = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (s != SYS_STATUS_SUCCESS) {
print("async_cancel: event create failed status="); print_u64(s); print("\n");
@@ -90,7 +120,7 @@ int main(void) {
print("async_cancel: cancel returned "); print_u64(cs); print("\n");
while (iosb.Status == SYS_STATUS_PENDING) {
s = SysWaitEvent(event, 1);
s = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
print("async_cancel: wait returned "); print_u64(s); print("\n");
if (s != SYS_STATUS_SUCCESS && s != SYS_STATUS_KERNEL_APC && s != SYS_STATUS_USER_APC && s != SYS_STATUS_ALERTED)
break;
@@ -0,0 +1,227 @@
// Dirty file-cache writeback regression test.
// Verifies buffered writes dirty cache pages, explicit flush writes them back, and close flushes synchronously.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define PAGE_SIZE_BYTES 4096u
#define TEST_SIZE (PAGE_SIZE_BYTES * 4u)
#define REWRITE_SIZE (PAGE_SIZE_BYTES * 2u)
#define CLOSE_SIZE (PAGE_SIZE_BYTES * 3u)
static uint8_t gPatternA[TEST_SIZE];
static uint8_t gPatternB[TEST_SIZE];
static uint8_t gReadback[TEST_SIZE];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c[2] = { buf[i], 0 };
print(c);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("dirty_writeback: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("dirty_writeback: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
memset(out, 0, sizeof(*out));
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static void delete_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
}
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"dirty_writeback.tmp",
"/dirty_writeback.tmp",
"1:/dirty_writeback.tmp",
"0:/dirty_writeback.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
static uint32_t open_path(const char* path, uint32_t* fileOut) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &h };
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS)
*fileOut = h;
return s;
}
static void fill_patterns(void) {
for (size_t i = 0; i < sizeof(gPatternA); ++i) {
gPatternA[i] = (uint8_t)(0x11u + (uint8_t)(i * 13u + (i >> 6)));
gPatternB[i] = (uint8_t)(0x80u + (uint8_t)(i * 31u + (i >> 4)));
}
}
static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) {
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOut, const char* what) {
size_t got = 0;
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
static void checked_seek(uint32_t file, uint64_t offset, const char* what) {
uint32_t s = SysSeekFile(file, offset);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
}
static void expect_writeback_progress(const SYS_FILE_CACHE_STATS* before,
const SYS_FILE_CACHE_STATS* after,
const char* what) {
if (after->FlushFileFailures != before->FlushFileFailures)
fail("flush failure counter increased");
if (after->WritebackFailures != before->WritebackFailures)
fail("writeback failure counter increased");
if (after->WritebackBytes <= before->WritebackBytes) {
print("dirty_writeback: ");
print(what);
print(" did not write back bytes\n");
exit_now(1);
}
}
int main(void) {
print("dirty_writeback: start\n");
fill_patterns();
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
print("dirty_writeback: using ");
print(path ? path : "<unknown>");
print("\n");
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS afterWrite;
SYS_FILE_CACHE_STATS afterFlush;
SYS_FILE_CACHE_STATS beforeClose;
SYS_FILE_CACHE_STATS afterClose;
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
checked_write(file, gPatternA, sizeof(gPatternA), "write pattern A");
if (query_cache(&afterWrite) != SYS_STATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.DirtyMarks <= before.DirtyMarks) fail("write did not mark dirty cache pages");
if (afterWrite.DirtyPages <= before.DirtyPages) fail("write did not leave dirty pages resident");
checked_seek(file, 0, "seek readback before flush");
size_t got = 0;
checked_read(file, gReadback, sizeof(gReadback), &got, "read before flush");
if (got != sizeof(gPatternA)) fail("read before flush size mismatch");
if (memcmp(gReadback, gPatternA, sizeof(gPatternA)) != 0) fail("cached read before flush mismatch");
s = SysFlushFile(file);
if (s != SYS_STATUS_SUCCESS) fail_status("explicit flush", s);
if (query_cache(&afterFlush) != SYS_STATUS_SUCCESS) fail("post-flush stats query failed");
if (afterFlush.FlushFileCalls <= afterWrite.FlushFileCalls) fail("flush call counter did not increase");
expect_writeback_progress(&afterWrite, &afterFlush, "explicit flush");
checked_seek(file, 0, "seek rewrite");
checked_write(file, gPatternB, REWRITE_SIZE, "rewrite pattern B");
checked_seek(file, REWRITE_SIZE, "seek close-tail");
checked_write(file, gPatternA + REWRITE_SIZE, CLOSE_SIZE - REWRITE_SIZE, "write close tail");
if (query_cache(&beforeClose) != SYS_STATUS_SUCCESS) fail("pre-close stats query failed");
if (beforeClose.DirtyMarks <= afterFlush.DirtyMarks) fail("rewrite did not mark dirty pages");
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close", s);
if (query_cache(&afterClose) != SYS_STATUS_SUCCESS) fail("post-close stats query failed");
if (afterClose.FlushFileCalls <= beforeClose.FlushFileCalls) fail("close did not flush file");
expect_writeback_progress(&beforeClose, &afterClose, "close flush");
s = open_path(path, &file);
if (s != SYS_STATUS_SUCCESS) fail_status("reopen", s);
checked_read(file, gReadback, CLOSE_SIZE, &got, "read after reopen");
if (got != CLOSE_SIZE) fail("read after reopen size mismatch");
if (memcmp(gReadback, gPatternB, REWRITE_SIZE) != 0) fail("rewrite data not durable after close");
if (memcmp(gReadback + REWRITE_SIZE, gPatternA + REWRITE_SIZE,
CLOSE_SIZE - REWRITE_SIZE) != 0) fail("tail data not durable after close");
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close reopened", s);
delete_path(path);
print("dirty_writeback: dirty-marks+=");
print_u64(afterClose.DirtyMarks - before.DirtyMarks);
print(" writeback-bytes+=");
print_u64(afterClose.WritebackBytes - before.WritebackBytes);
print(" flushes+=");
print_u64(afterClose.FlushFileCalls - before.FlushFileCalls);
print("\n");
print("dirty_writeback: PASS\n");
exit_now(0);
return 0;
}
+196
View File
@@ -0,0 +1,196 @@
// User-mode regression test for mmap-style native file mappings.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_MAP_SIZE 8192u
static uint8_t gExpected[TEST_MAP_SIZE];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
static void exit_now(int code) {
// SysExitProcess should not return, but keep tests from falling through to
// the synthetic entry frame if a broken kernel build returns anyway.
for (;;)
SysExitProcess(code);
}
static void fail_status(const char* what, uint32_t status) {
print("mmap_smoke: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static void fail_msg(const char* msg) {
print("mmap_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
static uint32_t open_first_existing(uint32_t* fileOut, const char** pathOut) {
static const char* candidates[] = {
"mmap_smoke.exe",
"/apps/mmap_smoke.exe",
"0:/apps/mmap_smoke.exe",
"1:/apps/mmap_smoke.exe",
"/system/init.exe",
"0:/system/init.exe",
"1:/system/init.exe",
"system/init.exe",
};
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
uint32_t file = 0;
SYS_PATH_HANDLE_REQUEST openReq = { SysSvFromCstr(candidates[i]), &file };
uint32_t s = SysOpenFile(&openReq);
if (s == SYS_STATUS_SUCCESS) {
*fileOut = file;
if (pathOut)
*pathOut = candidates[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
int main(void) {
print("mmap_smoke: start\n");
uint32_t file = 0;
const char* openedPath = 0;
uint32_t s = open_first_existing(&file, &openedPath);
if (s != SYS_STATUS_SUCCESS)
fail_status("open test file", s);
print("mmap_smoke: using ");
print(openedPath ? openedPath : "<unknown>");
print("\n");
size_t got = 0;
memset(gExpected, 0xA5, sizeof(gExpected));
SYS_FILE_IO_REQUEST ioReq = SysMakeFileIoRequest(file, gExpected, sizeof(gExpected), &got);
s = SysReadFile(&ioReq);
if (s != SYS_STATUS_SUCCESS)
fail_status("read baseline", s);
if (got < 64)
fail_msg("baseline read was unexpectedly short");
print("mmap_smoke: baseline read OK\n");
uint32_t mapping = 0;
s = SysCreateFileMapping(file,
SYS_VM_READ,
SYS_MAP_FLAG_NONE,
sizeof(gExpected),
&mapping);
if (s != SYS_STATUS_SUCCESS)
fail_status("create file mapping", s);
print("mmap_smoke: mapping created\n");
// The mapping object must hold its own file reference. Closing the source
// handle before SysMapFile catches lifetime bugs in the object layer.
SysCloseHandle(file);
file = 0;
print("mmap_smoke: source handle closed\n");
void* base = 0;
s = SysMapFile(mapping,
0,
sizeof(gExpected),
SYS_MAP_ACCESS_READ,
SYS_MAP_FLAG_NONE,
&base);
SysCloseHandle(mapping);
mapping = 0;
if (s != SYS_STATUS_SUCCESS)
fail_status("map file view", s);
if (!base)
fail_msg("map returned null base");
print("mmap_smoke: view mapped\n");
volatile uint8_t* view = (volatile uint8_t*)base;
// Touch both pages before comparing. This ensures the test exercises
// demand-faulted file-backed VAD pages rather than only VAD creation.
(void)view[0];
print("mmap_smoke: first page fault OK\n");
(void)view[4096];
print("mmap_smoke: second page fault OK\n");
for (size_t i = 0; i < got; ++i) {
if (view[i] != gExpected[i]) {
print("mmap_smoke: byte mismatch at ");
print_u64(i);
print(" expected=");
print_u64(gExpected[i]);
print(" got=");
print_u64(view[i]);
print("\n");
SysFreeVm(base);
exit_now(1);
}
}
print("mmap_smoke: mapped bytes match\n");
// If the file is shorter than TEST_MAP_SIZE, the mapped tail must be
// zero-filled rather than exposing stale physical memory.
for (size_t i = got; i < sizeof(gExpected) && i < got + 64; ++i) {
if (view[i] != 0) {
print("mmap_smoke: nonzero EOF fill at ");
print_u64(i);
print(" got=");
print_u64(view[i]);
print("\n");
SysFreeVm(base);
exit_now(1);
}
}
print("mmap_smoke: EOF fill OK\n");
s = SysFreeVm(base);
if (s != SYS_STATUS_SUCCESS)
fail_status("unmap", s);
print("mmap_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,223 @@
// Page-cache write/truncate invalidation regression test.
// Verifies cached clean pages are not reused after file write or truncate.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_SIZE 8192u
#define TRUNC_SIZE 1024u
static uint8_t gOriginal[TEST_SIZE];
static uint8_t gReplacement[4096];
static uint8_t gReadback[TEST_SIZE];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c[2] = { buf[i], 0 };
print(c);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("pcache_invalidate: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("pcache_invalidate: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
memset(out, 0, sizeof(*out));
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static void delete_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
}
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"pcache_invalidate.tmp",
"/pcache_invalidate.tmp",
"1:/pcache_invalidate.tmp",
"0:/pcache_invalidate.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
static void fill_patterns(void) {
for (size_t i = 0; i < TEST_SIZE; ++i)
gOriginal[i] = (uint8_t)(0x30u + (uint8_t)(i * 17u + (i >> 7)));
for (size_t i = 0; i < sizeof(gReplacement); ++i)
gReplacement[i] = (uint8_t)(0xA0u + (uint8_t)(i * 29u + (i >> 5)));
}
static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) {
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOut, const char* what) {
size_t got = 0;
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
int main(void) {
print("pcache_invalidate: start\n");
fill_patterns();
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
print("pcache_invalidate: using ");
print(path ? path : "<unknown>");
print("\n");
checked_write(file, gOriginal, sizeof(gOriginal), "write original");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek fill", s);
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS afterFill;
SYS_FILE_CACHE_STATS afterSecond;
SYS_FILE_CACHE_STATS afterWrite;
SYS_FILE_CACHE_STATS afterRewriteRead;
SYS_FILE_CACHE_STATS afterTruncate;
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
size_t got = 0;
checked_read(file, gReadback, sizeof(gReadback), &got, "first read");
if (got != sizeof(gOriginal)) fail("first read size mismatch");
if (memcmp(gReadback, gOriginal, sizeof(gOriginal)) != 0) fail("first read data mismatch");
if (query_cache(&afterFill) != SYS_STATUS_SUCCESS) fail("fill stats query failed");
if (afterFill.Loads <= before.Loads) fail("initial read did not populate cache");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek second", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "second read");
if (got != sizeof(gOriginal)) fail("second read size mismatch");
if (memcmp(gReadback, gOriginal, sizeof(gOriginal)) != 0) fail("second read data mismatch");
if (query_cache(&afterSecond) != SYS_STATUS_SUCCESS) fail("second stats query failed");
// Do not require this second read to hit: the cache is intentionally
// bounded and pcache_pressure.exe may have left it at the soft limit, so
// the newly-populated temp pages are eligible for immediate pressure
// eviction. This test is about write/truncate freshness; mmap/read reuse
// is covered by pcache_mmap_read_reuse.exe.
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek rewrite", s);
checked_write(file, gReplacement, sizeof(gReplacement), "rewrite first page");
if (query_cache(&afterWrite) != SYS_STATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.Invalidations <= afterSecond.Invalidations) fail("write did not invalidate cached page");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, sizeof(gReplacement), &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping after rewrite", s);
void* mappedBase = 0;
s = SysMapFile(mapping, 0, sizeof(gReplacement), SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &mappedBase);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("map after rewrite", s);
if (!mappedBase) fail("map after rewrite returned null");
volatile uint8_t* mappedView = (volatile uint8_t*)mappedBase;
(void)mappedView[0];
(void)mappedView[sizeof(gReplacement) - 1];
if (memcmp((const void*)mappedBase, gReplacement, sizeof(gReplacement)) != 0) {
SysFreeVm(mappedBase);
fail("stale mmap data after write invalidation");
}
s = SysFreeVm(mappedBase);
if (s != SYS_STATUS_SUCCESS) fail_status("unmap after rewrite", s);
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek verify rewrite", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "read after rewrite");
if (got != sizeof(gOriginal)) fail("rewrite read size mismatch");
if (memcmp(gReadback, gReplacement, sizeof(gReplacement)) != 0) fail("stale data after write invalidation");
if (memcmp(gReadback + sizeof(gReplacement), gOriginal + sizeof(gReplacement),
sizeof(gOriginal) - sizeof(gReplacement)) != 0) fail("unwritten page changed after rewrite");
if (query_cache(&afterRewriteRead) != SYS_STATUS_SUCCESS) fail("rewrite-read stats query failed");
s = SysTruncateFile(file, TRUNC_SIZE);
if (s != SYS_STATUS_SUCCESS) fail_status("truncate", s);
if (SysGetFileSize(file) != TRUNC_SIZE) fail("truncate size mismatch");
if (query_cache(&afterTruncate) != SYS_STATUS_SUCCESS) fail("truncate stats query failed");
if (afterTruncate.Invalidations <= afterRewriteRead.Invalidations) fail("truncate did not invalidate cached tail");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek truncated", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "read truncated");
if (got != TRUNC_SIZE) fail("truncated read length mismatch");
if (memcmp(gReadback, gReplacement, TRUNC_SIZE) != 0) fail("truncated data mismatch");
SysCloseHandle(file);
delete_path(path);
print("pcache_invalidate: invalidations+=");
print_u64(afterTruncate.Invalidations - before.Invalidations);
print(" loads+=");
print_u64(afterTruncate.Loads - before.Loads);
print(" hits+=");
print_u64(afterTruncate.Hits - before.Hits);
print("\n");
print("pcache_invalidate: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,176 @@
// Page-cache mmap->read reuse regression test.
// Faults file pages through mmap first, then verifies SysReadFile reuses those pages.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_SIZE 8192u
static uint8_t gReadback[TEST_SIZE];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c[2] = { buf[i], 0 };
print(c);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("pcache_mmap_read_reuse: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("pcache_mmap_read_reuse: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
memset(out, 0, sizeof(*out));
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static uint32_t open_test_file(uint32_t* out, const char** pathOut) {
static const char* paths[] = {
"pcache_mmap_read_reuse.exe",
"/apps/pcache_mmap_read_reuse.exe",
"1:/apps/pcache_mmap_read_reuse.exe",
"0:/apps/pcache_mmap_read_reuse.exe",
"pcache_smoke.exe",
"/apps/pcache_smoke.exe",
"1:/apps/pcache_smoke.exe",
"0:/apps/pcache_smoke.exe",
"/system/init.exe",
"0:/system/init.exe",
"1:/system/init.exe",
"system/init.exe",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*out = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
int main(void) {
print("pcache_mmap_read_reuse: start\n");
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS afterFault;
SYS_FILE_CACHE_STATS afterRead;
uint32_t s = query_cache(&before);
if (s != SYS_STATUS_SUCCESS) fail_status("query initial stats", s);
uint32_t file = 0;
const char* path = 0;
s = open_test_file(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("open test file", s);
uint64_t fileSize = SysGetFileSize(file);
if (fileSize < 4096) fail("test file too small");
size_t mapSize = (fileSize < TEST_SIZE) ? (size_t)fileSize : TEST_SIZE;
print("pcache_mmap_read_reuse: using ");
print(path ? path : "<unknown>");
print(" size=");
print_u64(fileSize);
print("\n");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, mapSize, &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
void* base = 0;
s = SysMapFile(mapping, 0, mapSize, SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("map file", s);
if (!base) fail("map returned null");
volatile uint8_t* view = (volatile uint8_t*)base;
for (size_t i = 0; i < mapSize; i += 4096)
(void)view[i];
(void)view[mapSize - 1];
s = query_cache(&afterFault);
if (s != SYS_STATUS_SUCCESS) fail_status("query after mmap faults", s);
if (afterFault.Loads <= before.Loads) {
SysFreeVm(base);
fail("mmap faults did not populate file cache");
}
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) {
SysFreeVm(base);
fail_status("seek", s);
}
size_t got = 0;
memset(gReadback, 0xCC, sizeof(gReadback));
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(file, gReadback, mapSize, &got);
s = SysReadFile(&readReq);
if (s != SYS_STATUS_SUCCESS) {
SysFreeVm(base);
fail_status("read", s);
}
if (got != mapSize) {
SysFreeVm(base);
fail("read length mismatch");
}
if (memcmp(gReadback, (const void*)base, mapSize) != 0) {
SysFreeVm(base);
fail("read data differs from mapped data");
}
s = query_cache(&afterRead);
if (s != SYS_STATUS_SUCCESS) {
SysFreeVm(base);
fail_status("query after read", s);
}
if (afterRead.Hits <= afterFault.Hits) {
SysFreeVm(base);
fail("read did not reuse mmap-populated cache pages");
}
SysFreeVm(base);
SysCloseHandle(file);
print("pcache_mmap_read_reuse: loads+=");
print_u64(afterRead.Loads - before.Loads);
print(" hits+=");
print_u64(afterRead.Hits - before.Hits);
print("\n");
print("pcache_mmap_read_reuse: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,227 @@
// Multi-threaded file-cache/VAD fault regression test.
// Several threads map the same file mapping and fault/read pages concurrently.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define THREAD_COUNT 6u
#define ITERATIONS 16u
#define TEST_SIZE 32768u
static uint8_t gPattern[TEST_SIZE];
typedef struct THREAD_CTX {
uint32_t Index;
uint32_t Mapping;
volatile uint32_t Status;
volatile uint32_t IterationsDone;
} THREAD_CTX;
static THREAD_CTX gThreads[THREAD_COUNT];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c[2] = { buf[i], 0 };
print(c);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("pcache_multithread: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("pcache_multithread: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
memset(out, 0, sizeof(*out));
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static void delete_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
}
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"pcache_multithread.tmp",
"/pcache_multithread.tmp",
"1:/pcache_multithread.tmp",
"0:/pcache_multithread.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
static void fill_pattern(void) {
for (size_t i = 0; i < TEST_SIZE; ++i)
gPattern[i] = (uint8_t)(0x5Au ^ (uint8_t)(i * 13u) ^ (uint8_t)(i >> 3));
}
static uint32_t validate_mapping(const uint8_t* base) {
for (size_t i = 0; i < TEST_SIZE; ++i) {
if (base[i] != gPattern[i])
return 0x10000000u | (uint32_t)(i & 0x0FFFFFFFu);
}
return SYS_STATUS_SUCCESS;
}
static void thread_main(uintptr_t arg) {
THREAD_CTX* ctx = (THREAD_CTX*)arg;
if (!ctx) SysExitThread(1);
for (uint32_t iter = 0; iter < ITERATIONS; ++iter) {
void* base = 0;
uint32_t s = SysMapFile(ctx->Mapping, 0, TEST_SIZE,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
if (s != SYS_STATUS_SUCCESS || !base) {
ctx->Status = s ? s : SYS_STATUS_INVALID_PARAMETER;
SysExitThread(1);
}
volatile uint8_t* touch = (volatile uint8_t*)base;
for (size_t off = 0; off < TEST_SIZE; off += 4096)
(void)touch[off];
(void)touch[TEST_SIZE - 1];
s = validate_mapping((const uint8_t*)base);
uint32_t fs = SysFreeVm(base);
if (s != SYS_STATUS_SUCCESS) {
ctx->Status = s;
SysExitThread(1);
}
if (fs != SYS_STATUS_SUCCESS) {
ctx->Status = fs;
SysExitThread(1);
}
ctx->IterationsDone++;
}
ctx->Status = SYS_STATUS_SUCCESS;
SysExitThread(0);
}
int main(void) {
print("pcache_multithread: start\n");
fill_pattern();
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
size_t written = 0;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &written);
s = SysWriteFile(&writeReq);
if (s != SYS_STATUS_SUCCESS) fail_status("write pattern", s);
if (written != sizeof(gPattern)) fail("short write");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS after;
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
uint32_t handles[THREAD_COUNT];
uint32_t tids[THREAD_COUNT];
memset(handles, 0, sizeof(handles));
memset(tids, 0, sizeof(tids));
memset(gThreads, 0, sizeof(gThreads));
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
gThreads[i].Index = i;
gThreads[i].Mapping = mapping;
gThreads[i].Status = SYS_STATUS_PENDING;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)thread_main,
(uintptr_t)&gThreads[i],
1,
0,
&handles[i],
&tids[i]
};
s = SysCreateThread(&req);
if (s != SYS_STATUS_SUCCESS) fail_status("create thread", s);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
if (s != SYS_STATUS_SUCCESS) fail_status("wait thread", s);
SysCloseHandle(handles[i]);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
if (gThreads[i].Status != SYS_STATUS_SUCCESS) {
print("pcache_multithread: worker ");
print_u64(i);
print(" status=");
print_u64(gThreads[i].Status);
print(" iterations=");
print_u64(gThreads[i].IterationsDone);
print("\n");
exit_now(1);
}
if (gThreads[i].IterationsDone != ITERATIONS) fail("worker iteration count mismatch");
}
if (query_cache(&after) != SYS_STATUS_SUCCESS) fail("final stats query failed");
if (after.Loads <= before.Loads) fail("concurrent mmap did not load cache pages");
if (after.Hits <= before.Hits) fail("concurrent mmap did not reuse cache pages");
SysCloseHandle(mapping);
SysCloseHandle(file);
delete_path(path);
print("pcache_multithread: loads+=");
print_u64(after.Loads - before.Loads);
print(" hits+=");
print_u64(after.Hits - before.Hits);
print(" races+=");
print_u64(after.RaceDiscards - before.RaceDiscards);
print("\n");
print("pcache_multithread: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,169 @@
// Sequential file-cache pressure test.
// Reads a large read-only asset through SysReadFile and checks that the
// read-only page cache stays bounded by dropping clean pages.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define READ_CHUNK 4096u
#define MIN_PRESSURE_BYTES (3u * 1024u * 1024u)
#define RESIDENT_LIMIT_PAGES 640u
static uint8_t gBuf[READ_CHUNK];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c[2] = { buf[i], 0 };
print(c);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("pcache_pressure: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("pcache_pressure: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static uint32_t open_first_large(uint32_t* fileOut, const char** pathOut, uint64_t* sizeOut) {
static const char* candidates[] = {
"1:/apps/video.bk2",
"0:/apps/video.bk2",
"/apps/video.bk2",
"video.bk2",
"1:/apps/doom1.wad",
"0:/apps/doom1.wad",
"/apps/doom1.wad",
"doom1.wad",
};
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(candidates[i]), &h };
uint32_t s = SysOpenFile(&req);
if (s != SYS_STATUS_SUCCESS)
continue;
uint64_t size = SysGetFileSize(h);
if (size >= MIN_PRESSURE_BYTES) {
*fileOut = h;
*pathOut = candidates[i];
*sizeOut = size;
return SYS_STATUS_SUCCESS;
}
SysCloseHandle(h);
}
return SYS_STATUS_NOT_FOUND;
}
int main(void) {
print("pcache_pressure: start\n");
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS after;
if (query_cache(&before) != SYS_STATUS_SUCCESS)
fail("initial cache stats query failed");
uint32_t file = 0;
const char* path = 0;
uint64_t size = 0;
uint32_t s = open_first_large(&file, &path, &size);
if (s != SYS_STATUS_SUCCESS)
fail_status("open large asset", s);
print("pcache_pressure: using ");
print(path ? path : "<unknown>");
print(" size=");
print_u64(size);
print("\n");
uint64_t total = 0;
uint64_t target = size < (8u * 1024u * 1024u) ? size : (8u * 1024u * 1024u);
while (total < target) {
size_t got = 0;
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(file, gBuf, sizeof(gBuf), &got);
s = SysReadFile(&readReq);
if (s != SYS_STATUS_SUCCESS)
fail_status("read", s);
if (got == 0)
break;
total += got;
}
SysCloseHandle(file);
if (total < MIN_PRESSURE_BYTES)
fail("asset read was too short to pressure the cache");
if (query_cache(&after) != SYS_STATUS_SUCCESS)
fail("final cache stats query failed");
uint64_t loads = after.Loads - before.Loads;
uint64_t hits = after.Hits - before.Hits;
uint64_t drops = after.Drops - before.Drops;
uint64_t raReq = after.ReadAheadRequests - before.ReadAheadRequests;
uint64_t raLoad = after.ReadAheadPagesLoaded - before.ReadAheadPagesLoaded;
uint64_t raThrottle = after.ReadAheadPagesThrottled - before.ReadAheadPagesThrottled;
uint64_t batchReads = after.BatchReads - before.BatchReads;
uint64_t batchPages = after.BatchPagesLoaded - before.BatchPagesLoaded;
print("pcache_pressure: read bytes="); print_u64(total);
print(" loads+="); print_u64(loads);
print(" hits+="); print_u64(hits);
print(" drops+="); print_u64(drops);
print(" resident="); print_u64(after.ResidentPages);
print(" active="); print_u64(after.ActivePages);
print(" standby="); print_u64(after.StandbyPages);
print(" max="); print_u64(after.MaxResidentPages);
print(" readahead-req+="); print_u64(raReq);
print(" readahead-load+="); print_u64(raLoad);
print(" readahead-throttle+="); print_u64(raThrottle);
print(" batch-reads+="); print_u64(batchReads);
print(" batch-pages+="); print_u64(batchPages);
print("\n");
if (loads < 512)
fail("expected at least 512 cache page loads");
if (after.ResidentPages > RESIDENT_LIMIT_PAGES)
fail("resident page cache exceeded pressure bound");
if (loads > RESIDENT_LIMIT_PAGES && drops == 0)
fail("cache did not drop clean pages under pressure");
print("pcache_pressure: PASS\n");
exit_now(0);
return 0;
}
+156
View File
@@ -0,0 +1,156 @@
// Read-only page-cache smoke test.
// Verifies that ReadFile and file-backed mappings share the same cached pages.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_SIZE 8192u
static uint8_t gA[TEST_SIZE];
static uint8_t gB[TEST_SIZE];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("pcache_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t s) {
print("pcache_smoke: "); print(what); print(" failed status="); print_u64(s); print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
memset(out, 0, sizeof(*out));
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static uint32_t open_self(uint32_t* out) {
static const char* paths[] = {
"pcache_smoke.exe",
"/apps/pcache_smoke.exe",
"Y:/apps/pcache_smoke.exe",
"0:/apps/pcache_smoke.exe",
"1:/apps/pcache_smoke.exe",
"/apps/shell_native.exe",
"0:/apps/shell_native.exe",
"1:/apps/shell_native.exe",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*out = h;
print("pcache_smoke: using "); print(paths[i]); print("\n");
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
int main(void) {
print("pcache_smoke: start\n");
SYS_FILE_CACHE_STATS before, afterFirst, afterSecond, afterMap;
uint32_t s = query_cache(&before);
if (s != SYS_STATUS_SUCCESS) fail_status("query initial stats", s);
uint32_t file = 0;
s = open_self(&file);
if (s != SYS_STATUS_SUCCESS) fail_status("open test file", s);
size_t gotA = 0;
memset(gA, 0xA5, sizeof(gA));
SYS_FILE_IO_REQUEST readReqA = SysMakeFileIoRequest(file, gA, sizeof(gA), &gotA);
s = SysReadFile(&readReqA);
if (s != SYS_STATUS_SUCCESS) fail_status("first read", s);
if (gotA < 4096) fail("first read too short");
print("pcache_smoke: first read OK\n");
s = query_cache(&afterFirst);
if (s != SYS_STATUS_SUCCESS) fail_status("query after first read", s);
if (afterFirst.Loads <= before.Loads)
fail("first read did not load file-cache pages");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek", s);
size_t gotB = 0;
memset(gB, 0x5A, sizeof(gB));
SYS_FILE_IO_REQUEST readReqB = SysMakeFileIoRequest(file, gB, sizeof(gB), &gotB);
s = SysReadFile(&readReqB);
if (s != SYS_STATUS_SUCCESS) fail_status("second read", s);
if (gotB != gotA) fail("second read length mismatch");
if (memcmp(gA, gB, gotA) != 0) fail("second read data mismatch");
print("pcache_smoke: second read OK\n");
s = query_cache(&afterSecond);
if (s != SYS_STATUS_SUCCESS) fail_status("query after second read", s);
if (afterSecond.Hits <= afterFirst.Hits)
fail("second read did not hit file cache");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, sizeof(gA), &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
SysCloseHandle(file);
file = 0;
void* base = 0;
s = SysMapFile(mapping, 0, sizeof(gA), SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("map file", s);
if (!base) fail("map returned null");
volatile uint8_t* view = (volatile uint8_t*)base;
(void)view[0];
(void)view[4096];
print("pcache_smoke: mmap faults OK\n");
if (memcmp((const void*)base, gA, gotA) != 0) {
SysFreeVm(base);
fail("mapped data mismatch");
}
s = query_cache(&afterMap);
if (s != SYS_STATUS_SUCCESS) fail_status("query after map", s);
if (afterMap.Hits <= afterSecond.Hits)
fail("mmap faults did not hit already cached pages");
SysFreeVm(base);
print("pcache_smoke: stats loads="); print_u64(afterMap.Loads - before.Loads);
print(" hits="); print_u64(afterMap.Hits - before.Hits);
print(" resident="); print_u64(afterMap.ResidentPages); print("\n");
print("pcache_smoke: PASS\n");
exit_now(0);
return 0;
}
+216
View File
@@ -0,0 +1,216 @@
// Shared writable mmap regression test.
// Verifies that writable mapped-file views dirty the coherent file cache and flush through the normal file path.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define PAGE_SIZE_BYTES 4096u
#define TEST_SIZE (PAGE_SIZE_BYTES * 3u)
static uint8_t gInitial[TEST_SIZE];
static uint8_t gExpected[TEST_SIZE];
static uint8_t gReadback[TEST_SIZE];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c[2] = { buf[i], 0 };
print(c);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("shared_mmap: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("shared_mmap: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) {
memset(out, 0, sizeof(*out));
return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0);
}
static void delete_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
}
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"shared_mmap.tmp",
"/shared_mmap.tmp",
"1:/shared_mmap.tmp",
"0:/shared_mmap.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
}
static uint32_t open_path(const char* path, uint32_t* fileOut) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &h };
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS)
*fileOut = h;
return s;
}
static void checked_seek(uint32_t file, uint64_t offset, const char* what) {
uint32_t s = SysSeekFile(file, offset);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
}
static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) {
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOut, const char* what) {
size_t got = 0;
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
static void fill_initial(void) {
for (size_t i = 0; i < TEST_SIZE; ++i) {
gInitial[i] = (uint8_t)(0x20u + (uint8_t)(i * 7u + (i >> 5)));
gExpected[i] = gInitial[i];
}
}
int main(void) {
print("shared_mmap: start\n");
fill_initial();
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
print("shared_mmap: creating temporary file ");
print(path ? path : "<unknown>");
print("\n");
checked_write(file, gInitial, TEST_SIZE, "initial write");
s = SysFlushFile(file);
if (s != SYS_STATUS_SUCCESS) fail_status("initial flush", s);
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READWRITE, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
uint8_t* view = 0;
s = SysMapFile(mapping, 0, TEST_SIZE, SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_WRITE,
SYS_MAP_FLAG_NONE, (void**)&view);
if (s != SYS_STATUS_SUCCESS) fail_status("map shared write", s);
for (size_t i = 128; i < 128 + 3000; ++i) {
view[i] = (uint8_t)(0xA0u + (uint8_t)i);
gExpected[i] = view[i];
}
for (size_t i = PAGE_SIZE_BYTES + 17; i < PAGE_SIZE_BYTES + 17 + 2048; ++i) {
view[i] = (uint8_t)(0x50u + (uint8_t)(i >> 1));
gExpected[i] = view[i];
}
SYS_FILE_CACHE_STATS beforeFlush;
SYS_FILE_CACHE_STATS afterFlush;
if (query_cache(&beforeFlush) != SYS_STATUS_SUCCESS) fail("pre-flush stats failed");
s = SysFlushFile(file);
if (s != SYS_STATUS_SUCCESS) fail_status("flush mapped writes", s);
if (query_cache(&afterFlush) != SYS_STATUS_SUCCESS) fail("post-flush stats failed");
if (afterFlush.MappedDirtyPages <= beforeFlush.MappedDirtyPages)
fail("mapped dirty pages were not harvested");
if (afterFlush.WritebackBytes <= beforeFlush.WritebackBytes)
fail("mapped writes did not write back");
checked_seek(file, 0, "seek after mapped flush");
size_t got = 0;
checked_read(file, gReadback, TEST_SIZE, &got, "read after mapped flush");
if (got != TEST_SIZE) fail("read after mapped flush size mismatch");
if (memcmp(gReadback, gExpected, TEST_SIZE) != 0) fail("mapped flush data mismatch");
for (size_t i = PAGE_SIZE_BYTES * 2 + 3; i < PAGE_SIZE_BYTES * 2 + 900; ++i) {
view[i] = (uint8_t)(0xC0u ^ (uint8_t)i);
gExpected[i] = view[i];
}
s = SysFreeVm(view);
view = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("unmap", s);
s = SysCloseHandle(mapping);
mapping = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close mapping", s);
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close file", s);
s = open_path(path, &file);
if (s != SYS_STATUS_SUCCESS) fail_status("reopen", s);
checked_read(file, gReadback, TEST_SIZE, &got, "read after close");
if (got != TEST_SIZE) fail("read after close size mismatch");
if (memcmp(gReadback, gExpected, TEST_SIZE) != 0) fail("unmap/close data mismatch");
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close reopened", s);
delete_path(path);
print("shared_mmap: mapped-dirty-pages+=");
print_u64(afterFlush.MappedDirtyPages - beforeFlush.MappedDirtyPages);
print(" writeback-bytes+=");
print_u64(afterFlush.WritebackBytes - beforeFlush.WritebackBytes);
print("\n");
print("shared_mmap: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,105 @@
// Native surface validation test.
// Exercises invalid parameters and clipped dirty rectangles without depending on windowing.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define SURFACE_W 32u
#define SURFACE_H 24u
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("surface_invalid_rect: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("surface_invalid_rect: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("surface_invalid_rect: ");
print(what);
print(" returned ");
print_u64(got);
print(" expected ");
print_u64(expected);
print("\n");
exit_now(1);
}
int main(void) {
print("surface_invalid_rect: start\n");
uint32_t bad = 0;
uint32_t s = SysCreateSurface(0, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888, SYS_SURFACE_FLAG_NONE, &bad);
expect_status("zero width", s, SYS_STATUS_INVALID_PARAMETER);
s = SysCreateSurface(SURFACE_W, SURFACE_H, 0x9999u, SYS_SURFACE_FLAG_NONE, &bad);
expect_status("bad format", s, SYS_STATUS_NOT_SUPPORTED);
uint32_t surface = 0;
s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != SYS_STATUS_SUCCESS) fail_status("create surface", s);
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != SYS_STATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
s = SysPresentSurface(surface, 0, 1);
expect_status("null rect array with nonzero count", s, SYS_STATUS_INVALID_PARAMETER);
DISPLAY_RECT rects[SYS_SURFACE_MAX_RECTS + 1];
memset(rects, 0, sizeof(rects));
s = SysPresentSurface(surface, rects, SYS_SURFACE_MAX_RECTS + 1);
expect_status("too many rects", s, SYS_STATUS_INVALID_PARAMETER);
DISPLAY_RECT clipped = { -8, -8, 24, 24 };
s = SysPresentSurface(surface, &clipped, 1);
if (s != SYS_STATUS_SUCCESS) fail_status("present clipped rect", s);
DISPLAY_RECT empty = { 10000, 10000, 16, 16 };
s = SysPresentSurface(surface, &empty, 1);
if (s != SYS_STATUS_SUCCESS) fail_status("present empty clipped rect", s);
SysCloseHandle(surface);
print("surface_invalid_rect: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,157 @@
// Native surface multithreaded present test.
// Multiple user threads present one process-private surface to catch mapping and scratch-buffer races.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define SURFACE_W 96u
#define SURFACE_H 64u
#define THREAD_COUNT 4u
#define ITERATIONS 32u
typedef struct WORKER_STATE {
uint32_t Index;
uint32_t Surface;
uint8_t* Base;
uint32_t Pitch;
uint32_t Status;
uint32_t IterationsDone;
} WORKER_STATE;
static WORKER_STATE gWorkers[THREAD_COUNT];
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("surface_multithread_present: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("surface_multithread_present: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
static void worker_main(uintptr_t arg) {
WORKER_STATE* st = (WORKER_STATE*)arg;
if (!st) SysExitThread(1);
uint32_t bandHeight = SURFACE_H / THREAD_COUNT;
uint32_t y0 = st->Index * bandHeight;
uint32_t h = bandHeight;
if (st->Index == THREAD_COUNT - 1)
h = SURFACE_H - y0;
for (uint32_t iter = 0; iter < ITERATIONS; ++iter) {
for (uint32_t y = 0; y < h; ++y) {
uint32_t* row = (uint32_t*)(st->Base + (size_t)(y0 + y) * st->Pitch);
for (uint32_t x = 0; x < SURFACE_W; ++x)
row[x] = 0x00010101u * ((st->Index + 1u) * 32u + (iter & 31u));
}
DISPLAY_RECT rect = { 0, (int32_t)y0, (int32_t)SURFACE_W, (int32_t)h };
uint32_t s = SysPresentSurface(st->Surface, &rect, 1);
if (s != SYS_STATUS_SUCCESS) {
st->Status = s;
SysExitThread(1);
}
st->IterationsDone++;
SysYield();
}
st->Status = SYS_STATUS_SUCCESS;
SysExitThread(0);
}
int main(void) {
print("surface_multithread_present: start\n");
uint32_t surface = 0;
uint32_t s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != SYS_STATUS_SUCCESS) fail_status("create surface", s);
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != SYS_STATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
uint32_t handles[THREAD_COUNT];
uint32_t tids[THREAD_COUNT];
memset(handles, 0, sizeof(handles));
memset(tids, 0, sizeof(tids));
memset(gWorkers, 0, sizeof(gWorkers));
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
gWorkers[i].Index = i;
gWorkers[i].Surface = surface;
gWorkers[i].Base = (uint8_t*)base;
gWorkers[i].Pitch = pitch;
gWorkers[i].Status = SYS_STATUS_PENDING;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)worker_main,
(uintptr_t)&gWorkers[i],
1,
0,
&handles[i],
&tids[i]
};
s = SysCreateThread(&req);
if (s != SYS_STATUS_SUCCESS) fail_status("create thread", s);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
if (s != SYS_STATUS_SUCCESS) fail_status("wait thread", s);
SysCloseHandle(handles[i]);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
if (gWorkers[i].Status != SYS_STATUS_SUCCESS) {
print("surface_multithread_present: worker ");
print_u64(i);
print(" status=");
print_u64(gWorkers[i].Status);
print(" iterations=");
print_u64(gWorkers[i].IterationsDone);
print("\n");
exit_now(1);
}
if (gWorkers[i].IterationsDone != ITERATIONS) fail("worker iteration count mismatch");
}
SysCloseHandle(surface);
print("surface_multithread_present: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,93 @@
// Native surface smoke test.
// Verifies that process-private surface handles can be created, mapped, queried, presented, and closed.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define SURFACE_W 64u
#define SURFACE_H 48u
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) { print("0"); return; }
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
static void fail(const char* msg) {
print("surface_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
static void fail_status(const char* what, uint32_t status) {
print("surface_smoke: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
int main(void) {
print("surface_smoke: start\n");
uint32_t surface = 0;
uint32_t s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != SYS_STATUS_SUCCESS) fail_status("create surface", s);
if (!surface) fail("surface handle is zero");
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != SYS_STATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
SYS_SURFACE_DESC desc;
memset(&desc, 0, sizeof(desc));
s = SysQuerySurface(surface, &desc);
if (s != SYS_STATUS_SUCCESS) fail_status("query surface", s);
if (desc.Width != SURFACE_W || desc.Height != SURFACE_H) fail("query dimensions mismatch");
if (desc.PitchBytes != pitch || desc.Format != SYS_SURFACE_FORMAT_XRGB8888) fail("query metadata mismatch");
if (desc.Base != base) fail("query base mismatch");
uint8_t* rows = (uint8_t*)base;
for (uint32_t y = 0; y < SURFACE_H; ++y) {
uint32_t* row = (uint32_t*)(rows + (size_t)y * pitch);
for (uint32_t x = 0; x < SURFACE_W; ++x)
row[x] = 0x00202020u + ((x & 0xffu) << 16) + ((y & 0xffu) << 8);
}
DISPLAY_RECT rect = { 0, 0, (int32_t)SURFACE_W, (int32_t)SURFACE_H };
s = SysPresentSurface(surface, &rect, 1);
if (s != SYS_STATUS_SUCCESS) fail_status("present surface", s);
s = SysPresentSurface(surface, 0, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("present full surface", s);
SysCloseHandle(surface);
print("surface_smoke: PASS\n");
exit_now(0);
return 0;
}