replace the VHD/diskpart disk image pipeline with a directory-based vvfat root, we'll see when it breaks, fix /O2, add an all_tests, generalize the fat32 driver into a fat 16+32 driver, change policy on volume drivers, collapse KCPU's fields into a single atomic KE_CPU_STATE state machine, move scheduler ticking to per-CPU LAPIC timers, gate verbose SMP topology logging behind selftest diagnostics and add SMP pool-checkpoint validation around AP admission, add checked intrusive list helpers behind a new KE_DEBUG_LIST_CHECKS flag, add ObInitializeObjectHeader/ObTryReferenceObject, add waitable display clock objects and a SYS_OPEN_DISPLAY_CLOCK syscall for vsync-style waits, refactor kernel input into a per-queue KE_INPUT_CONTEXT with its own key/mouse ring buffers and DPC-driven key repeat, add KeInputQueuePeekEvent/ReadEvent/WaitKey and a SYS_QUERY_GLOBAL_INPUT_STATE syscall, split the kernel pool allocator out of mm_virt, new video files!

This commit is contained in:
maksw committed 2026-07-12 00:10:21 +02:00
1 parent 485a6bbfe7
commit 8b254f84f4
74 files changed
+4488 -2503

No files matched your search

+61 -6
View File
@@ -274,17 +274,25 @@ static const KeyMap g_keymap[] = {
static bool g_prev_key[KEYMAP_COUNT];
// Returns whether one NTLite input key is currently held.
static bool ntlite_key_down(INPUT_KEY key)
// Returns whether one key bit is set in a global input snapshot.
static bool ntlite_snapshot_key_down(const SYS_GLOBAL_INPUT_STATE* state, INPUT_KEY key)
{
bool down = false;
return SysIsKeyDown(key, &down) == NTLSTATUS_SUCCESS && down;
if (!state || key >= 256u) return false;
return (state->KeyDown[key >> 3] & (uint8_t)(1u << (key & 7u))) != 0;
}
static void poll_input(void)
{
SYS_GLOBAL_INPUT_STATE input;
memset(&input, 0, sizeof(input));
input.Size = sizeof(input);
input.Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE;
if (SysQueryGlobalInputState(&input) != NTLSTATUS_SUCCESS)
return;
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
bool cur_down = ntlite_key_down(g_keymap[i].inputKey);
bool cur_down = ntlite_snapshot_key_down(&input, g_keymap[i].inputKey);
bool prev_down = g_prev_key[i];
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
@@ -304,6 +312,7 @@ static void poll_input(void)
#define DISP_H (DOOM_H * SCALE)
static uint32_t g_surface_handle;
static uint32_t g_display_clock_handle;
static uint32_t* g_surface_pixels;
static uint32_t g_surface_pitch_pixels;
@@ -335,6 +344,22 @@ static void init_surface(void)
g_surface_pitch_pixels = pitchBytes / sizeof(uint32_t);
}
static void init_display_clock(void)
{
SYS_OPEN_DISPLAY_CLOCK_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = SYS_DISPLAY_CLOCK_FLAG_NONE;
req.PeriodMilliseconds = 1;
req.HandleOut = &g_display_clock_handle;
uint32_t s = SysOpenDisplayClock(&req);
if (s != NTLSTATUS_SUCCESS) {
g_display_clock_handle = 0;
doom_print_wrapper("doom: display clock unavailable, using sleep pacing\n");
}
}
static void render_frame(const uint8_t* rgba)
{
const uint32_t* src = (const uint32_t*)rgba;
@@ -409,6 +434,30 @@ static int build_argv_from_command_line(const char* cmdline, char* storage, size
return argc;
}
// ---------------------------------------------------------------------------
// Timing
// ---------------------------------------------------------------------------
static int doom_now_tic(void)
{
return I_GetTime();
}
// Waits until Doom has a new simulation tic to render.
static int wait_for_next_doom_tic(int lastTic)
{
for (;;) {
int now = doom_now_tic();
if (now != lastTic)
return now;
if (g_display_clock_handle) {
(void)SysWaitSingle(g_display_clock_handle, SYS_WAIT_FLAG_NONE, SYS_WAIT_TIMEOUT_INFINITE);
} else {
SysSleep(1);
}
}
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
@@ -447,12 +496,18 @@ int main(void)
const char* argv[32];
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
init_surface();
init_display_clock();
doom_init(argc, argv, 0);
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: entering game loop\n") }; SysWriteConsole(&req); } while (0);
// Core execution loop
// Core execution loop. Doom advances at TICRATE, so rendering duplicate
// frames as fast as possible only amplifies scheduler and input jitter.
int lastFrameTic = doom_now_tic();
while (1) {
int frameTic = wait_for_next_doom_tic(lastFrameTic);
lastFrameTic = frameTic;
poll_input();
doom_update();
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
Binary file not shown.
+142 -20
View File
@@ -1416,36 +1416,158 @@ static void cmd_write(const char* args) {
print("File written successfully.\n");
}
#define SHELL_DIR_INITIAL_CAPACITY 64u
// Returns an ASCII lower-case value for case-insensitive directory sorting.
static char shellAsciiLower(char c) {
if (c >= 'A' && c <= 'Z')
return (char)(c - 'A' + 'a');
return c;
}
// Compares two names using case-insensitive order with a stable case tie-breaker.
static int shellCompareNames(const char* a, const char* b) {
size_t i = 0;
if (!a) a = "";
if (!b) b = "";
for (;;) {
char ca = shellAsciiLower(a[i]);
char cb = shellAsciiLower(b[i]);
if (ca != cb)
return (ca < cb) ? -1 : 1;
if (ca == '\0')
break;
i++;
}
return strcmp(a, b);
}
// Compares two directory entries by display name.
static int shellCompareDirEntries(const SYS_DIR_ENTRY* a, const SYS_DIR_ENTRY* b) {
return shellCompareNames(a->Name, b->Name);
}
// Allocates a directory-entry array from VM so large listings do not consume stack.
static uint32_t shellAllocDirEntries(uint32_t capacity, SYS_DIR_ENTRY** entriesOut) {
void* base = 0;
size_t bytes = (size_t)capacity * sizeof(SYS_DIR_ENTRY);
size_t pages = (bytes + 4095u) / 4096u;
if (pages == 0) pages = 1;
SYS_ALLOC_VM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Protect = SYS_VM_READWRITE;
req.Flags = SYS_VM_FLAG_NONE;
req.PageCount = pages;
req.BaseOut = &base;
uint32_t status = SysAllocVm(&req);
if (status != 0 || !base) {
*entriesOut = 0;
return status ? status : 1;
}
*entriesOut = (SYS_DIR_ENTRY*)base;
return 0;
}
// Grows a directory-entry array while preserving already-read entries.
static uint32_t shellGrowDirEntries(SYS_DIR_ENTRY** entries, uint32_t* capacity, uint32_t count) {
uint32_t oldCapacity = *capacity;
uint32_t newCapacity = oldCapacity ? oldCapacity * 2u : SHELL_DIR_INITIAL_CAPACITY;
SYS_DIR_ENTRY* next = 0;
uint32_t status = shellAllocDirEntries(newCapacity, &next);
if (status != 0)
return status;
if (*entries && count)
memcpy(next, *entries, (size_t)count * sizeof(SYS_DIR_ENTRY));
if (*entries)
SysFreeVm(*entries);
*entries = next;
*capacity = newCapacity;
return 0;
}
// Sorts directory entries alphabetically.
static void shellSortDirEntries(SYS_DIR_ENTRY* entries, uint32_t count) {
for (uint32_t i = 1; i < count; i++) {
SYS_DIR_ENTRY current = entries[i];
uint32_t j = i;
while (j > 0 && shellCompareDirEntries(&current, &entries[j - 1]) < 0) {
entries[j] = entries[j - 1];
j--;
}
entries[j] = current;
}
}
// Prints one directory entry in cmd dir format.
static void shellPrintDirEntry(const SYS_DIR_ENTRY* ke) {
printDirTime(&ke->LastWriteTime);
if (ke->Attr & 0x10) {
print("<DIR> ");
print(ke->Name);
} else {
print(" ");
print(ke->Name);
print(" (");
printUInt64(ke->FileSize);
print(" bytes)");
}
print("\n");
}
// Implements the cmd dir operation.
static void cmd_dir(const char* path) {
uint32_t hDir;
const char* target = (path && *path != '\0') ? path : ".";
uint32_t st = open_dir(target, &hDir);
if (st != 0) {
if (st != 0) {
print("dir: failed to open directory, status=0x");
printHex32(st);
print("\n");
return;
}
SYS_DIR_ENTRY ke;
while (SysReadDir(hDir, &ke) == 0) {
printDirTime(&ke.LastWriteTime);
if (ke.Attr & 0x10) {
print("<DIR> ");
print(ke.Name);
} else {
print(" ");
print(ke.Name);
print(" (");
printUInt64(ke.FileSize);
print(" bytes)");
}
print("\n");
return;
}
SYS_DIR_ENTRY* entries = 0;
uint32_t count = 0;
uint32_t capacity = 0;
for (;;) {
SYS_DIR_ENTRY ke;
st = SysReadDir(hDir, &ke);
if (st != 0)
break;
if (count == capacity) {
st = shellGrowDirEntries(&entries, &capacity, count);
if (st != 0) {
print("dir: out of memory, status=0x");
printHex32(st);
print("\n");
SysCloseHandle(hDir);
if (entries) SysFreeVm(entries);
return;
}
}
entries[count++] = ke;
}
SysCloseHandle(hDir);
shellSortDirEntries(entries, count);
for (uint32_t i = 0; i < count; i++)
shellPrintDirEntry(&entries[i]);
if (entries)
SysFreeVm(entries);
}
// Implements the cmd type operation.
+77 -2
View File
@@ -23,6 +23,9 @@ extern int closedir(DIR* d);
extern int unlink(const char *pathname);
extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]);
extern int waitpid(int pid, int *status, int options);
extern void* malloc(size_t size);
extern void* realloc(void* ptr, size_t size);
extern void free(void* ptr);
extern void exit(int status);
extern void clearscreen(void);
extern void clearkeyboard(void);
@@ -121,17 +124,89 @@ static void cmd_write(const char* args) {
print("File written successfully.\n");
}
typedef struct SHELL_POSIX_DIR_ENTRY {
char Name[NAME_MAX + 1];
unsigned char Type;
} SHELL_POSIX_DIR_ENTRY;
// Returns an ASCII lower-case value for case-insensitive directory sorting.
static char shellAsciiLower(char c) {
if (c >= 'A' && c <= 'Z')
return (char)(c - 'A' + 'a');
return c;
}
// Compares two names using case-insensitive order with a stable case tie-breaker.
static int shellCompareNames(const char* a, const char* b) {
size_t i = 0;
if (!a) a = "";
if (!b) b = "";
for (;;) {
char ca = shellAsciiLower(a[i]);
char cb = shellAsciiLower(b[i]);
if (ca != cb)
return (ca < cb) ? -1 : 1;
if (ca == '\0')
break;
i++;
}
return strcmp(a, b);
}
// Sorts directory entries alphabetically.
static void shellSortDirEntries(SHELL_POSIX_DIR_ENTRY* entries, size_t count) {
for (size_t i = 1; i < count; i++) {
SHELL_POSIX_DIR_ENTRY current = entries[i];
size_t j = i;
while (j > 0 && shellCompareNames(current.Name, entries[j - 1].Name) < 0) {
entries[j] = entries[j - 1];
j--;
}
entries[j] = current;
}
}
// Implements the cmd dir operation.
static void cmd_dir(const char* path) {
const char* target = (path && *path != '\0') ? path : ".";
DIR* d = opendir(target);
if (!d) { print("dir: cannot access directory\n"); return; }
SHELL_POSIX_DIR_ENTRY* entries = NULL;
size_t count = 0;
size_t capacity = 0;
dirent* entry;
while ((entry = readdir(d)) != NULL) {
print(entry->d_type == 1 ? "<DIR> " : " ");
print(entry->d_name); print("\n");
if (count == capacity) {
size_t nextCapacity = capacity ? capacity * 2u : 64u;
SHELL_POSIX_DIR_ENTRY* next = (SHELL_POSIX_DIR_ENTRY*)realloc(entries, nextCapacity * sizeof(SHELL_POSIX_DIR_ENTRY));
if (!next) {
print("dir: out of memory\n");
free(entries);
closedir(d);
return;
}
entries = next;
capacity = nextCapacity;
}
strncpy(entries[count].Name, entry->d_name, NAME_MAX);
entries[count].Name[NAME_MAX] = '\0';
entries[count].Type = entry->d_type;
count++;
}
closedir(d);
shellSortDirEntries(entries, count);
for (size_t i = 0; i < count; i++) {
print(entries[i].Type == 1 ? "<DIR> " : " ");
print(entries[i].Name);
print("\n");
}
free(entries);
}
// Implements the cmd type operation.
+82 -3
View File
@@ -94,12 +94,56 @@ static void cmd_write(const char* args) {
print("File written successfully.\n");
}
typedef struct SHELL_WIN32_DIR_ENTRY {
char Name[MAX_PATH];
DWORD Attributes;
} SHELL_WIN32_DIR_ENTRY;
// Returns an ASCII lower-case value for case-insensitive directory sorting.
static char shellAsciiLower(char c) {
if (c >= 'A' && c <= 'Z')
return (char)(c - 'A' + 'a');
return c;
}
// Compares two names using case-insensitive order with a stable case tie-breaker.
static int shellCompareNames(const char* a, const char* b) {
size_t i = 0;
if (!a) a = "";
if (!b) b = "";
for (;;) {
char ca = shellAsciiLower(a[i]);
char cb = shellAsciiLower(b[i]);
if (ca != cb)
return (ca < cb) ? -1 : 1;
if (ca == '\0')
break;
i++;
}
return strcmp(a, b);
}
// Sorts directory entries alphabetically.
static void shellSortDirEntries(SHELL_WIN32_DIR_ENTRY* entries, size_t count) {
for (size_t i = 1; i < count; i++) {
SHELL_WIN32_DIR_ENTRY current = entries[i];
size_t j = i;
while (j > 0 && shellCompareNames(current.Name, entries[j - 1].Name) < 0) {
entries[j] = entries[j - 1];
j--;
}
entries[j] = current;
}
}
// Implements the cmd dir operation.
static void cmd_dir(const char* path) {
WIN32_FIND_DATAA fd;
char search_path[MAX_PATH];
const char* target = (path && *path != '\0') ? path : ".";
size_t len = strlen(target);
if (len + 3 > MAX_PATH) { print("dir: path too long\n"); return; }
strcpy(search_path, target);
@@ -107,11 +151,46 @@ static void cmd_dir(const char* path) {
HANDLE hFind = FindFirstFileA(search_path, &fd);
if (hFind == INVALID_HANDLE_VALUE) { print("dir: failed to read directory\n"); return; }
void* heap = GetProcessHeap();
SHELL_WIN32_DIR_ENTRY* entries = NULL;
size_t count = 0;
size_t capacity = 0;
do {
print(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
print(fd.cFileName); print("\n");
if (count == capacity) {
size_t nextCapacity = capacity ? capacity * 2u : 64u;
size_t bytes = nextCapacity * sizeof(SHELL_WIN32_DIR_ENTRY);
SHELL_WIN32_DIR_ENTRY* next;
if (entries)
next = (SHELL_WIN32_DIR_ENTRY*)HeapReAlloc(heap, 0, entries, bytes);
else
next = (SHELL_WIN32_DIR_ENTRY*)HeapAlloc(heap, 0, bytes);
if (!next) {
print("dir: out of memory\n");
if (entries) HeapFree(heap, 0, entries);
FindClose(hFind);
return;
}
entries = next;
capacity = nextCapacity;
}
strncpy(entries[count].Name, fd.cFileName, MAX_PATH - 1);
entries[count].Name[MAX_PATH - 1] = '\0';
entries[count].Attributes = fd.dwFileAttributes;
count++;
} while (FindNextFileA(hFind, &fd));
FindClose(hFind);
shellSortDirEntries(entries, count);
for (size_t i = 0; i < count; i++) {
print(entries[i].Attributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
print(entries[i].Name);
print("\n");
}
if (entries)
HeapFree(heap, 0, entries);
}
// Implements the cmd type operation.
Binary file not shown.
Binary file not shown.
+41 -8
View File
@@ -11,7 +11,9 @@
#include <ntlite/syscall.h>
#include "bink.h"
#define VIDEO_DEFAULT_PATH "video.bk2"
#define VIDEO_USAGE "usage: video <file.bk2>\n" \
" video --help\n" \
"Plays a Bink video file from disk.\n"
static void print_span(const char* s, size_t len) {
if (!s || !len) return;
@@ -88,10 +90,10 @@ static SYS_STRING_VIEW skip_one_token(SYS_STRING_VIEW s) {
return SysSvFromParts(s.Data + i, s.Length - i);
}
static SYS_STRING_VIEW command_line_second_token(SYS_STRING_VIEW cmdline) {
static SYS_STRING_VIEW command_line_first_argument(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);
return SYS_SV_NULL;
size_t begin = 0;
size_t end = 0;
@@ -108,17 +110,43 @@ static SYS_STRING_VIEW command_line_second_token(SYS_STRING_VIEW cmdline) {
}
if (end <= begin)
return SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
return SYS_SV_NULL;
return SysSvFromParts(rest.Data + begin, end - begin);
}
static int sv_equals_cstr(SYS_STRING_VIEW s, const char* cstr) {
size_t len = 0;
if (!s.Data || !cstr)
return 0;
while (cstr[len])
len++;
if (s.Length != len)
return 0;
return memcmp(s.Data, cstr, len) == 0;
}
static int video_argument_is_help(SYS_STRING_VIEW arg) {
return sv_equals_cstr(arg, "-h") ||
sv_equals_cstr(arg, "--help") ||
sv_equals_cstr(arg, "/?");
}
static void print_usage(void) {
print(VIDEO_USAGE);
}
static const char* path_to_cstr(SYS_STRING_VIEW path, char* out, size_t outSize) {
if (!out || outSize == 0)
return VIDEO_DEFAULT_PATH;
return "";
if (!path.Data || path.Length == 0)
path = SYS_SV_LITERAL(VIDEO_DEFAULT_PATH);
if (!path.Data || path.Length == 0) {
out[0] = '\0';
return out;
}
size_t n = path.Length;
if (n + 1 > outSize)
@@ -140,7 +168,12 @@ static uint32_t open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) {
int main(void) {
SYS_STRING_VIEW cmdline = command_line_view();
SYS_STRING_VIEW pathView = command_line_second_token(cmdline);
SYS_STRING_VIEW pathView = command_line_first_argument(cmdline);
if (!pathView.Data || pathView.Length == 0 || video_argument_is_help(pathView)) {
print_usage();
exit_now(0);
}
print("video: starting\n");
if (cmdline.Data && cmdline.Length) {
+53 -51
View File
@@ -85,61 +85,63 @@ SYS_SET_CWD EQU 72
SYS_REAL_PATH EQU 73
; Console, display, and surfaces.
SYS_WRITE_CONSOLE EQU 74
SYS_GET_CONSOLE_INFO EQU 75
SYS_SET_CONSOLE_CURSOR EQU 76
SYS_FILL_CONSOLE_CHARACTER EQU 77
SYS_FILL_CONSOLE_ATTRIBUTE EQU 78
SYS_PUT_SCREEN_CHAR EQU 79
SYS_CLEAR_SCREEN EQU 80
SYS_GET_SCREEN_SIZE EQU 81
SYS_QUERY_DISPLAY_INFO EQU 82
SYS_CREATE_SURFACE EQU 83
SYS_MAP_SURFACE EQU 84
SYS_QUERY_SURFACE EQU 85
SYS_PRESENT_SURFACE EQU 86
SYS_WRITE_CONSOLE EQU 74
SYS_GET_CONSOLE_INFO EQU 75
SYS_SET_CONSOLE_CURSOR EQU 76
SYS_FILL_CONSOLE_CHARACTER EQU 77
SYS_FILL_CONSOLE_ATTRIBUTE EQU 78
SYS_PUT_SCREEN_CHAR EQU 79
SYS_CLEAR_SCREEN EQU 80
SYS_GET_SCREEN_SIZE EQU 81
SYS_QUERY_DISPLAY_INFO EQU 82
SYS_OPEN_DISPLAY_CLOCK EQU 83
SYS_CREATE_SURFACE EQU 84
SYS_MAP_SURFACE EQU 85
SYS_QUERY_SURFACE EQU 86
SYS_PRESENT_SURFACE EQU 87
; Input.
SYS_POLL_KEYBOARD EQU 87
SYS_IS_KEY_DOWN EQU 88
SYS_READ_KEYBOARD_CHAR EQU 89
SYS_READ_KEY EQU 90
SYS_GET_KEY_MODIFIERS EQU 91
SYS_OPEN_INPUT_QUEUE EQU 92
SYS_READ_INPUT_EVENT EQU 93
SYS_PEEK_INPUT_EVENT EQU 94
SYS_PEEK_MOUSE EQU 95
SYS_READ_MOUSE EQU 96
SYS_GET_MOUSE_POS EQU 97
SYS_POLL_KEYBOARD EQU 88
SYS_IS_KEY_DOWN EQU 89
SYS_READ_KEYBOARD_CHAR EQU 90
SYS_READ_KEY EQU 91
SYS_GET_KEY_MODIFIERS EQU 92
SYS_QUERY_GLOBAL_INPUT_STATE EQU 93
SYS_OPEN_INPUT_QUEUE EQU 94
SYS_READ_INPUT_EVENT EQU 95
SYS_PEEK_INPUT_EVENT EQU 96
SYS_PEEK_MOUSE EQU 97
SYS_READ_MOUSE EQU 98
SYS_GET_MOUSE_POS EQU 99
; Time, debug, and system information.
SYS_CREATE_TIMER EQU 98
SYS_SET_TIMER EQU 99
SYS_CANCEL_TIMER EQU 100
SYS_GET_TICK EQU 101
SYS_GET_TICK_FREQUENCY EQU 102
SYS_GET_SYSTEM_TIME EQU 103
SYS_PRINT_DEBUG EQU 104
SYS_QUERY_TRAP_FRAME EQU 105
SYS_GET_MEMORY_STATUS EQU 106
SYS_QUERY_KERNEL_STATS EQU 107
SYS_CREATE_TIMER EQU 100
SYS_SET_TIMER EQU 101
SYS_CANCEL_TIMER EQU 102
SYS_GET_TICK EQU 103
SYS_GET_TICK_FREQUENCY EQU 104
SYS_GET_SYSTEM_TIME EQU 105
SYS_PRINT_DEBUG EQU 106
SYS_QUERY_TRAP_FRAME EQU 107
SYS_GET_MEMORY_STATUS EQU 108
SYS_QUERY_KERNEL_STATS EQU 109
; Native sockets and polling.
SYS_CREATE_SOCKET EQU 108
SYS_SOCKET_IO_ASYNC EQU 109
SYS_BIND_SOCKET EQU 110
SYS_LISTEN_SOCKET EQU 111
SYS_ACCEPT_SOCKET EQU 112
SYS_CONNECT_SOCKET EQU 113
SYS_SEND_SOCKET EQU 114
SYS_RECV_SOCKET EQU 115
SYS_SHUTDOWN_SOCKET EQU 116
SYS_SEND_TO_SOCKET EQU 117
SYS_RECV_FROM_SOCKET EQU 118
SYS_RESOLVE_HOST EQU 119
SYS_SET_SOCKET_OPTION EQU 120
SYS_GET_SOCKET_OPTION EQU 121
SYS_POLL EQU 122
SYS_UNPUBLISH_QUEUE EQU 123
SYS_CREATE_SOCKET EQU 110
SYS_SOCKET_IO_ASYNC EQU 111
SYS_BIND_SOCKET EQU 112
SYS_LISTEN_SOCKET EQU 113
SYS_ACCEPT_SOCKET EQU 114
SYS_CONNECT_SOCKET EQU 115
SYS_SEND_SOCKET EQU 116
SYS_RECV_SOCKET EQU 117
SYS_SHUTDOWN_SOCKET EQU 118
SYS_SEND_TO_SOCKET EQU 119
SYS_RECV_FROM_SOCKET EQU 120
SYS_RESOLVE_HOST EQU 121
SYS_SET_SOCKET_OPTION EQU 122
SYS_GET_SOCKET_OPTION EQU 123
SYS_POLL EQU 124
SYS_UNPUBLISH_QUEUE EQU 125
SYS_MAX EQU 124
SYS_MAX EQU 126
+2
View File
@@ -218,6 +218,7 @@ 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
SYSCALL_STUB2 OpenDisplayClock, SYS_OPEN_DISPLAY_CLOCK
; ---------------------------------------------------------------------------
; Input
@@ -227,6 +228,7 @@ 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 QueryGlobalInputState, SYS_QUERY_GLOBAL_INPUT_STATE
SYSCALL_STUB2 ReadInputEvent, SYS_READ_INPUT_EVENT
SYSCALL_STUB2 PeekInputEvent, SYS_PEEK_INPUT_EVENT
+356
View File
@@ -0,0 +1,356 @@
// Dynamic test runner for every executable in the runner directory.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <ntlite/syscall.h>
#define ALL_TESTS_INITIAL_CAPACITY 32u
typedef struct TEST_ENTRY {
char Name[SYS_DIR_NAME_MAX];
} TEST_ENTRY;
// Writes one NUL-terminated string to the console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Writes one unsigned integer to the console.
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);
}
}
// Writes one signed integer to the console.
static void print_i32(int32_t v) {
if (v < 0) {
print("-");
print_u64((uint32_t)(-(v + 1)) + 1u);
return;
}
print_u64((uint32_t)v);
}
// Writes one 32-bit value as hexadecimal.
static void print_hex32(uint32_t v) {
static const char hex[] = "0123456789abcdef";
for (int i = 7; i >= 0; i--) {
char c[2] = { hex[(v >> (i * 4)) & 0xfu], 0 };
print(c);
}
}
// Exits the process without returning to the runtime.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Converts ASCII to lowercase for stable case-insensitive comparisons.
static char ascii_lower(char c) {
if (c >= 'A' && c <= 'Z') return (char)(c - 'A' + 'a');
return c;
}
// Compares two names case-insensitively, with a case-sensitive tie-breaker.
static int compare_names(const char* a, const char* b) {
size_t i = 0;
for (;;) {
char ca = ascii_lower(a[i]);
char cb = ascii_lower(b[i]);
if (ca != cb) return (unsigned char)ca < (unsigned char)cb ? -1 : 1;
if (ca == '\0') break;
i++;
}
return strcmp(a, b);
}
// Returns whether one character is a path separator.
static bool is_separator(char c) {
return c == '/' || c == '\\';
}
// Copies a SYS_STRING_VIEW into a NUL-terminated fixed buffer.
static bool copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) {
if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize) return false;
memcpy(out, view.Data, view.Length);
out[view.Length] = '\0';
return true;
}
// Splits an executable path into its directory and basename components.
static bool split_self_path(const char* path, char* dir, size_t dirSize, char* name, size_t nameSize) {
if (!path || !dir || !name || dirSize == 0 || nameSize == 0) return false;
size_t len = strlen(path);
size_t last = (size_t)-1;
for (size_t i = 0; i < len; i++) {
if (is_separator(path[i])) last = i;
}
const char* base = path;
size_t dirLen = 1;
if (last == (size_t)-1) {
if (dirSize < 2) return false;
dir[0] = '.';
dir[1] = '\0';
} else {
dirLen = last;
if (last == 0) dirLen = 1;
if (last >= 2 && path[1] == ':' && is_separator(path[2])) dirLen = last + 1;
if (dirLen + 1 > dirSize) return false;
memcpy(dir, path, dirLen);
dir[dirLen] = '\0';
base = path + last + 1;
}
size_t baseLen = strlen(base);
if (baseLen + 1 > nameSize) return false;
memcpy(name, base, baseLen + 1);
return true;
}
// Returns whether one filename ends in .exe, case-insensitively.
static bool has_exe_suffix(const char* name) {
size_t len = strlen(name);
if (len < 4) return false;
const char* ext = name + len - 4;
return ascii_lower(ext[0]) == '.' &&
ascii_lower(ext[1]) == 'e' &&
ascii_lower(ext[2]) == 'x' &&
ascii_lower(ext[3]) == 'e';
}
// Allocates a test-entry array from user VM.
static uint32_t alloc_entries(uint32_t capacity, TEST_ENTRY** entriesOut) {
void* base = 0;
size_t bytes = (size_t)capacity * sizeof(TEST_ENTRY);
size_t pages = (bytes + 4095u) / 4096u;
if (pages == 0) pages = 1;
SYS_ALLOC_VM_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Protect = SYS_VM_READWRITE;
req.Flags = SYS_VM_FLAG_NONE;
req.PageCount = pages;
req.BaseOut = &base;
uint32_t status = SysAllocVm(&req);
if (status != NTLSTATUS_SUCCESS || !base) {
*entriesOut = 0;
return status ? status : NTLSTATUS_NO_MEMORY;
}
*entriesOut = (TEST_ENTRY*)base;
return NTLSTATUS_SUCCESS;
}
// Grows the test-entry array.
static uint32_t grow_entries(TEST_ENTRY** entries, uint32_t* capacity, uint32_t count) {
uint32_t oldCapacity = *capacity;
uint32_t newCapacity = oldCapacity ? oldCapacity * 2u : ALL_TESTS_INITIAL_CAPACITY;
TEST_ENTRY* next = 0;
uint32_t status = alloc_entries(newCapacity, &next);
if (status != NTLSTATUS_SUCCESS) return status;
if (*entries && count) memcpy(next, *entries, (size_t)count * sizeof(TEST_ENTRY));
if (*entries) SysFreeVm(*entries);
*entries = next;
*capacity = newCapacity;
return NTLSTATUS_SUCCESS;
}
// Sorts test entries in deterministic execution order.
static void sort_entries(TEST_ENTRY* entries, uint32_t count) {
for (uint32_t i = 1; i < count; i++) {
TEST_ENTRY current = entries[i];
uint32_t j = i;
while (j > 0 && compare_names(current.Name, entries[j - 1].Name) < 0) {
entries[j] = entries[j - 1];
j--;
}
entries[j] = current;
}
}
// Builds one child path from the runner directory and test filename.
static bool build_child_path(const char* dir, const char* name, char* out, size_t outSize) {
if (!dir || !name || !out || outSize == 0) return false;
size_t dirLen = strlen(dir);
size_t nameLen = strlen(name);
bool needSep = dirLen > 0 && !is_separator(dir[dirLen - 1]);
size_t total = dirLen + (needSep ? 1u : 0u) + nameLen;
if (total + 1 > outSize) return false;
memcpy(out, dir, dirLen);
size_t pos = dirLen;
if (needSep) out[pos++] = '\\';
memcpy(out + pos, name, nameLen);
out[total] = '\0';
return true;
}
// Collects executable tests from the runner directory.
static uint32_t collect_tests(const char* dir, const char* selfName, TEST_ENTRY** entriesOut, uint32_t* countOut) {
*entriesOut = 0;
*countOut = 0;
uint32_t hDir = 0;
SYS_PATH_HANDLE_REQUEST openReq = { SysSvFromCstr(dir), &hDir };
uint32_t status = SysOpenDir(&openReq);
if (status != NTLSTATUS_SUCCESS) return status;
TEST_ENTRY* entries = 0;
uint32_t count = 0;
uint32_t capacity = 0;
for (;;) {
SYS_DIR_ENTRY entry;
status = SysReadDir(hDir, &entry);
if (status != NTLSTATUS_SUCCESS) break;
if (!entry.Name[0]) continue;
if ((entry.Attr & 0x10u) != 0) continue;
if (!has_exe_suffix(entry.Name)) continue;
if (compare_names(entry.Name, selfName) == 0) continue;
if (count == capacity) {
status = grow_entries(&entries, &capacity, count);
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(hDir);
if (entries) SysFreeVm(entries);
return status;
}
}
strncpy(entries[count].Name, entry.Name, sizeof(entries[count].Name) - 1);
entries[count].Name[sizeof(entries[count].Name) - 1] = '\0';
count++;
}
SysCloseHandle(hDir);
sort_entries(entries, count);
*entriesOut = entries;
*countOut = count;
return NTLSTATUS_SUCCESS;
}
// Runs one test process and returns the child's exit code through exitCodeOut.
static uint32_t run_test(const char* path, int32_t* exitCodeOut) {
uint32_t process = 0;
SYS_CREATE_PROCESS_REQUEST req;
req.Path = SysSvFromCstr(path);
req.CommandLine = SysSvFromCstr(path);
req.HandleOut = &process;
uint32_t status = SysCreateProcess(&req);
if (status != NTLSTATUS_SUCCESS) return status;
status = SysWait(process, SYS_WAIT_FLAG_NONE);
if (status != NTLSTATUS_SUCCESS) {
SysCloseHandle(process);
return status;
}
int32_t exitCode = 1;
status = SysGetProcessExitCode(process, &exitCode);
SysCloseHandle(process);
if (status != NTLSTATUS_SUCCESS) return status;
*exitCodeOut = exitCode;
return NTLSTATUS_SUCCESS;
}
// Program entry point.
int main(void) {
print("all_tests: start\n");
char selfPath[SYS_PATH_MAX];
char selfDir[SYS_PATH_MAX];
char selfName[SYS_DIR_NAME_MAX];
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION ||
!copy_view(selfPath, sizeof(selfPath), peb->ImagePath) ||
!split_self_path(selfPath, selfDir, sizeof(selfDir), selfName, sizeof(selfName))) {
print("all_tests: failed to resolve runner path\n");
exit_now(1);
}
TEST_ENTRY* entries = 0;
uint32_t count = 0;
uint32_t status = collect_tests(selfDir, selfName, &entries, &count);
if (status != NTLSTATUS_SUCCESS) {
print("all_tests: failed to enumerate tests status=0x");
print_hex32(status);
print("\n");
exit_now(1);
}
if (count == 0) {
print("all_tests: no test executables found\n");
if (entries) SysFreeVm(entries);
exit_now(1);
}
for (uint32_t i = 0; i < count; i++) {
char childPath[SYS_PATH_MAX];
if (!build_child_path(selfDir, entries[i].Name, childPath, sizeof(childPath))) {
print("all_tests: child path too long: ");
print(entries[i].Name);
print("\n");
SysFreeVm(entries);
exit_now(1);
}
print("all_tests: RUN ");
print(childPath);
print("\n");
int32_t exitCode = 1;
status = run_test(childPath, &exitCode);
if (status != NTLSTATUS_SUCCESS) {
print("all_tests: FAIL ");
print(childPath);
print(" launch/wait status=0x");
print_hex32(status);
print("\n");
SysFreeVm(entries);
exit_now(1);
}
if (exitCode != 0) {
print("all_tests: FAIL ");
print(childPath);
print(" exit=");
print_i32(exitCode);
print("\n");
SysFreeVm(entries);
exit_now(exitCode ? exitCode : 1);
}
print("all_tests: PASS ");
print(childPath);
print("\n");
}
print("all_tests: PASS count=");
print_u64(count);
print("\n");
SysFreeVm(entries);
exit_now(0);
return 0;
}
+57 -18
View File
@@ -54,6 +54,51 @@ static void fail(const char* what) {
exit_now(1);
}
// Copies a string view into a NUL-terminated buffer.
static int copy_view(char* dst, size_t cap, SYS_STRING_VIEW view) {
if (!dst || cap == 0 || !view.Data || view.Length + 1 > cap)
return 0;
memcpy(dst, view.Data, view.Length);
dst[view.Length] = '\0';
return 1;
}
// Computes the parent of one canonical native current-directory string.
static int parent_of_cwd(const char* cwd, char* out, size_t cap) {
if (!cwd || !out || cap == 0)
return 0;
size_t len = strlen(cwd);
if (len < 3 || cwd[1] != ':' || (cwd[2] != '/' && cwd[2] != '\\'))
return 0;
if (len + 1 > cap)
return 0;
memcpy(out, cwd, len + 1);
while (len > 3 && (out[len - 1] == '/' || out[len - 1] == '\\'))
out[--len] = '\0';
if (len <= 3) {
out[0] = cwd[0];
out[1] = ':';
out[2] = '/';
out[3] = '\0';
return 1;
}
size_t pos = len;
while (pos > 3 && out[pos - 1] != '/' && out[pos - 1] != '\\')
pos--;
if (pos <= 3) {
out[0] = cwd[0];
out[1] = ':';
out[2] = '/';
out[3] = '\0';
} else {
out[pos - 1] = '\0';
}
return 1;
}
// Sets the current directory to one native path.
static void set_cwd_or_fail(const char* path, const char* what) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
@@ -72,8 +117,8 @@ static void expect_cwd(const char* expected) {
// Program entry point.
int main(void) {
static const char rootPath[] = "0:/";
static const char tempDir[] = "0:/cwd_peb_smoke_dir";
char original[SYS_CWD_MAX];
char parent[SYS_CWD_MAX];
print("cwd_peb_smoke: start\n");
@@ -82,25 +127,19 @@ int main(void) {
if (peb->Magic != SYS_PEB_MAGIC) fail("peb magic mismatch");
if (peb->Version != SYS_ENV_VERSION) fail("peb version mismatch");
set_cwd_or_fail(rootPath, "set root cwd");
expect_cwd(rootPath);
if (!copy_view(original, sizeof(original), SysGetCurrentDirectoryView()))
fail("initial cwd missing");
if (!parent_of_cwd(original, parent, sizeof(parent)))
fail("initial cwd malformed");
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(tempDir) };
uint32_t status = SysRemoveDir(&dirReq);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_NOT_FOUND)
fail_status("remove stale temp dir", status);
set_cwd_or_fail(".", "set dot cwd");
expect_cwd(original);
status = SysCreateDir(&dirReq);
if (status != NTLSTATUS_SUCCESS) fail_status("create temp dir", status);
set_cwd_or_fail("..", "set parent cwd");
expect_cwd(parent);
set_cwd_or_fail(tempDir, "set temp cwd");
expect_cwd(tempDir);
set_cwd_or_fail(rootPath, "restore root cwd");
expect_cwd(rootPath);
status = SysRemoveDir(&dirReq);
if (status != NTLSTATUS_SUCCESS) fail_status("remove temp dir", status);
set_cwd_or_fail(original, "restore original cwd");
expect_cwd(original);
print("cwd_peb_smoke: PASS\n");
exit_now(0);
@@ -68,11 +68,14 @@ static void delete_path(const char* path) {
}
static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
// Writable QEMU vvfat maps long-name creation through a host rename and
// can abort before the guest receives an I/O status. Keep this file in
// 8.3 form so the test measures writeback rather than the host bridge.
static const char* paths[] = {
"dirty_writeback.tmp",
"/dirty_writeback.tmp",
"1:/dirty_writeback.tmp",
"0:/dirty_writeback.tmp",
"dwrite.tmp",
"/dwrite.tmp",
"1:/dwrite.tmp",
"0:/dwrite.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
+77 -100
View File
@@ -1,14 +1,13 @@
// Native file metadata smoke test.
// Verifies path stat, handle stat, directory metadata, missing paths, and invalid handles.
// Verifies path stat, handle stat, directory metadata, missing paths, and invalid handles without mutating the filesystem.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_FILE_SIZE 1537u
static uint8_t gPattern[TEST_FILE_SIZE];
static char gSelfPath[SYS_PATH_MAX];
static char gSelfDir[SYS_PATH_MAX];
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
@@ -58,84 +57,77 @@ static void fail_status(const char* what, uint32_t status) {
exit_now(1);
}
// Removes a file path if it already exists.
static void delete_file_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
// Copies one string view into a fixed NUL-terminated buffer.
static int copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) {
if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize)
return 0;
memcpy(out, view.Data, view.Length);
out[view.Length] = '\0';
return 1;
}
// Removes a directory path if it already exists.
static void remove_dir_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysRemoveDir(&req);
}
// Opens a new temporary file on the first writable path that works.
static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"file_stat_smoke.tmp",
"/file_stat_smoke.tmp",
"1:/file_stat_smoke.tmp",
"0:/file_stat_smoke.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_file_path(paths[i]);
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == NTLSTATUS_SUCCESS) {
*fileOut = h;
*pathOut = paths[i];
return NTLSTATUS_SUCCESS;
}
// Builds the containing directory path for one process image path.
static void build_parent_dir(char* out, size_t outSize, const char* path) {
size_t len = strlen(path);
size_t slash = (size_t)-1;
for (size_t i = 0; i < len; ++i) {
if (path[i] == '/' || path[i] == '\\')
slash = i;
}
return NTLSTATUS_NOT_FOUND;
}
// Creates a temporary directory on the first writable path that works.
static uint32_t create_temp_dir(const char** pathOut) {
static const char* paths[] = {
"file_stat_smoke.dir",
"/file_stat_smoke.dir",
"1:/file_stat_smoke.dir",
"0:/file_stat_smoke.dir",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
remove_dir_path(paths[i]);
SYS_PATH_REQUEST req = { SysSvFromCstr(paths[i]) };
uint32_t s = SysCreateDir(&req);
if (s == NTLSTATUS_SUCCESS) {
*pathOut = paths[i];
return NTLSTATUS_SUCCESS;
}
if (slash == (size_t)-1) {
if (outSize < 2) fail("parent directory buffer too small");
out[0] = '.';
out[1] = '\0';
return;
}
return NTLSTATUS_NOT_FOUND;
if (slash == 0) {
if (outSize < 2) fail("parent directory buffer too small");
out[0] = path[0];
out[1] = '\0';
return;
}
if (slash + 1 >= outSize)
fail("parent directory path too long");
memcpy(out, path, slash);
out[slash] = '\0';
}
// Fills the write pattern used by the test file.
static void fill_pattern(void) {
for (size_t i = 0; i < sizeof(gPattern); ++i)
gPattern[i] = (uint8_t)(0x31u + (uint8_t)(i * 17u + (i >> 3)));
}
// Opens this process image for read/query metadata checks.
static uint32_t open_self_image(uint32_t* fileOut, uint64_t* sizeOut) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
return NTLSTATUS_INVALID_PARAMETER;
if (!copy_view(gSelfPath, sizeof(gSelfPath), peb->ImagePath))
return NTLSTATUS_INVALID_PARAMETER;
build_parent_dir(gSelfDir, sizeof(gSelfDir), gSelfPath);
// Writes the test file contents and flushes them to the filesystem.
static void write_test_file(uint32_t file) {
size_t done = 0;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &done);
uint32_t s = SysWriteFile(&writeReq);
if (s != NTLSTATUS_SUCCESS) fail_status("write test file", s);
if (done != sizeof(gPattern)) fail("short write");
uint32_t h = 0;
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(gSelfPath),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s != NTLSTATUS_SUCCESS)
return s;
s = SysFlushFile(file);
if (s != NTLSTATUS_SUCCESS) fail_status("flush test file", s);
uint64_t size = 0;
s = SysGetFileSize(h, &size);
if (s != NTLSTATUS_SUCCESS) {
SysCloseHandle(h);
return s;
}
if (size == 0) {
SysCloseHandle(h);
return NTLSTATUS_NOT_FOUND;
}
*fileOut = h;
*sizeOut = size;
return NTLSTATUS_SUCCESS;
}
// Checks one regular-file metadata result.
@@ -171,13 +163,13 @@ static void check_directory_stat(const SYS_FILE_STAT* stat) {
if (stat->LinkCount == 0) fail("directory has zero links");
}
// Exercises handle and path stat for one regular file.
static void test_file_stat(uint32_t file, const char* path) {
// Exercises handle and path stat for this process image.
static void test_file_stat(uint32_t file, uint64_t expectedSize) {
SYS_FILE_STAT byHandle;
memset(&byHandle, 0xA5, sizeof(byHandle));
uint32_t s = SysGetFileStat(file, &byHandle);
if (s != NTLSTATUS_SUCCESS) fail_status("get file stat", s);
check_regular_stat(&byHandle, sizeof(gPattern), "handle stat");
check_regular_stat(&byHandle, expectedSize, "handle stat");
uint64_t size = 0;
s = SysGetFileSize(file, &size);
@@ -186,10 +178,10 @@ static void test_file_stat(uint32_t file, const char* path) {
SYS_FILE_STAT byPath;
memset(&byPath, 0x5A, sizeof(byPath));
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &byPath };
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(gSelfPath), &byPath };
s = SysStatFile(&statReq);
if (s != NTLSTATUS_SUCCESS) fail_status("path stat", s);
check_regular_stat(&byPath, sizeof(gPattern), "path stat");
check_regular_stat(&byPath, expectedSize, "path stat");
if (byHandle.VolumeId != 0 && byPath.VolumeId != 0 && byHandle.VolumeId != byPath.VolumeId)
fail("handle and path volume ids differ");
@@ -197,11 +189,11 @@ static void test_file_stat(uint32_t file, const char* path) {
fail("handle and path file ids differ");
}
// Exercises path stat for one directory.
static void test_directory_stat(const char* path) {
// Exercises path stat for the executable's containing directory.
static void test_directory_stat(void) {
SYS_FILE_STAT stat;
memset(&stat, 0xCC, sizeof(stat));
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &stat };
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(gSelfDir), &stat };
uint32_t s = SysStatFile(&statReq);
if (s != NTLSTATUS_SUCCESS) fail_status("directory stat", s);
check_directory_stat(&stat);
@@ -226,30 +218,15 @@ static void test_failure_paths(void) {
int main(void) {
print("file_stat_smoke: start\n");
fill_pattern();
uint32_t file = 0;
const char* filePath = 0;
uint32_t s = create_temp_file(&file, &filePath);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp file", s);
uint64_t size = 0;
uint32_t s = open_self_image(&file, &size);
if (s != NTLSTATUS_SUCCESS) fail_status("open self image", s);
write_test_file(file);
test_file_stat(file, filePath);
test_file_stat(file, size);
SysCloseHandle(file);
delete_file_path(filePath);
const char* dirPath = 0;
s = create_temp_dir(&dirPath);
if (s != NTLSTATUS_SUCCESS) fail_status("create temp dir", s);
test_directory_stat(dirPath);
remove_dir_path(dirPath);
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(dirPath) };
s = SysCreateDir(&dirReq);
if (s != NTLSTATUS_SUCCESS) fail_status("recreate temp dir", s);
test_directory_stat(dirPath);
remove_dir_path(dirPath);
test_directory_stat();
test_failure_paths();
print("file_stat_smoke: PASS\n");
@@ -89,6 +89,27 @@ int main(void) {
badReq.QueueHandle = 0xdeadbeefu;
expect_status("read input bad handle", SysReadInputEvent(&badReq), NTLSTATUS_INVALID_HANDLE);
SYS_GLOBAL_INPUT_STATE state;
state.Size = sizeof(state);
state.Flags = SYS_GLOBAL_INPUT_STATE_FLAG_NONE;
expect_status("query global input null", SysQueryGlobalInputState((SYS_GLOBAL_INPUT_STATE*)0), NTLSTATUS_INVALID_PARAMETER);
SYS_GLOBAL_INPUT_STATE badState = state;
badState.Size = 0;
expect_status("query global input bad size", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER);
badState = state;
badState.Flags = 1;
expect_status("query global input bad flags", SysQueryGlobalInputState(&badState), NTLSTATUS_INVALID_PARAMETER);
status = SysQueryGlobalInputState(&state);
if (status != NTLSTATUS_SUCCESS)
fail_status("query global input", status);
if (state.Size != sizeof(state))
fail_status("query global input size", state.Size);
if (state.Flags != SYS_GLOBAL_INPUT_STATE_FLAG_NONE)
fail_status("query global input flags", state.Flags);
status = SysPeekInputEvent(&req);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_WOULD_BLOCK)
fail_status("peek input immediate", status);
@@ -1,6 +1,6 @@
// 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.
// Reads this test executable through SysReadFile and checks cache behavior when
// the image is large enough to apply pressure.
#include <stdint.h>
#include <stddef.h>
@@ -56,47 +56,50 @@ 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",
static char gSelfPath[SYS_PATH_MAX];
"1:/apps/doom1.wad",
"0:/apps/doom1.wad",
"/apps/doom1.wad",
"doom1.wad",
};
// Copies a string view into a fixed NUL-terminated buffer.
static int copy_view(char* out, size_t outSize, SYS_STRING_VIEW view) {
if (!out || outSize == 0 || !view.Data || view.Length + 1 > outSize)
return 0;
memcpy(out, view.Data, view.Length);
out[view.Length] = '\0';
return 1;
}
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
uint32_t h = 0;
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(candidates[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s != NTLSTATUS_SUCCESS)
continue;
// Opens this process image as the cache-pressure input file.
static uint32_t open_self_image(uint32_t* fileOut, const char** pathOut, uint64_t* sizeOut) {
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
return NTLSTATUS_INVALID_PARAMETER;
if (!copy_view(gSelfPath, sizeof(gSelfPath), peb->ImagePath))
return NTLSTATUS_INVALID_PARAMETER;
uint64_t size = 0;
s = SysGetFileSize(h, &size);
if (s != NTLSTATUS_SUCCESS) {
SysCloseHandle(h);
continue;
}
if (size >= MIN_PRESSURE_BYTES) {
*fileOut = h;
*pathOut = candidates[i];
*sizeOut = size;
return NTLSTATUS_SUCCESS;
}
uint32_t h = 0;
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(gSelfPath),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s != NTLSTATUS_SUCCESS)
return s;
uint64_t size = 0;
s = SysGetFileSize(h, &size);
if (s != NTLSTATUS_SUCCESS) {
SysCloseHandle(h);
return s;
}
if (size == 0) {
SysCloseHandle(h);
return NTLSTATUS_NOT_FOUND;
}
return NTLSTATUS_NOT_FOUND;
*fileOut = h;
*pathOut = gSelfPath;
*sizeOut = size;
return NTLSTATUS_SUCCESS;
}
int main(void) {
@@ -110,9 +113,9 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint64_t size = 0;
uint32_t s = open_first_large(&file, &path, &size);
uint32_t s = open_self_image(&file, &path, &size);
if (s != NTLSTATUS_SUCCESS)
fail_status("open large asset", s);
fail_status("open self image", s);
print("pcache_pressure: using ");
print(path ? path : "<unknown>");
@@ -135,8 +138,8 @@ int main(void) {
SysCloseHandle(file);
if (total < MIN_PRESSURE_BYTES)
fail("asset read was too short to pressure the cache");
if (total == 0)
fail("self image read returned no bytes");
if (query_cache(&after) != NTLSTATUS_SUCCESS)
fail("final cache stats query failed");
@@ -165,12 +168,16 @@ int main(void) {
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");
if (size >= MIN_PRESSURE_BYTES) {
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");
} else {
print("pcache_pressure: self image below pressure size; pressure bounds skipped\n");
}
print("pcache_pressure: PASS\n");
exit_now(0);