redo boot around an explicit system volume and fixed kernel/HAL images, redo volumes and filesystem paths around capabilities instead of drive numbers and kernel CWD state, add a capability/security model with derived and revocable handles and security contexts, add port IPC and rssrv to manage the userspace resource namespace and publish the system volume, move process, dll and path loading to userspace and add process builder syscalls, init now supervises rssrv and the session process, redo the syscall ABI around request structs and four arguments, add a volume manager and proper partition arrival/removal handling so FAT and NTFS mount dynamically, clean up the HAL/DDK boundary so drivers don't depend on HAL internals, save fpu state properly now, the serial driver doesn't hang on a missing port, fix AHCI startup races, fix timeout accounting, fix shared mmap writeback, more smoke tests

This commit is contained in:
maksw committed 2026-08-20 12:33:17 +02:00
1 parent 4add6789ba
commit f882d339fb
232 files changed
+20934 -8898

No files matched your search

+183 -144
View File
@@ -13,8 +13,8 @@
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - Win32 per-thread state lives behind SYS_TEB::SubsystemData so the native
// TEB ABI stays subsystem-neutral.
// - File I/O uses the flags-based SYS_OPEN_FILE request plus the common
// read/write/seek/query/close syscalls.
// - File I/O resolves Win32 paths in userspace, then uses capability-relative
// opens plus the common read/write/seek/query/close syscalls.
#include <stdint.h>
#include <stddef.h>
@@ -24,6 +24,10 @@
#include <intrin.h>
#include <ntlite/syscall.h>
#include <ntlite/exception.h>
#include <ntlite/process_loader.h>
#include <ntlite/path.h>
#include <ntlite/module_loader.h>
#include <ntlite/resource.h>
extern size_t _malloc_usable_size(const void* ptr);
@@ -235,37 +239,33 @@ static inline BOOL IS_VALID_KERNEL_HANDLE(HANDLE h) {
return v != 0 && v != (uintptr_t)INVALID_HANDLE_VALUE;
}
// Path translator.
// Translates Win32 path syntax into native resource/CWD syntax.
static void TranslatePath(const char* input, char* output) {
if (!output) return;
if (!input) { output[0] = '\0'; return; }
if (input[0] != '\0' && input[1] == ':' && (input[2] == '\\' || input[2] == '/')) {
// Translate "C:\" -> "0:/". Native nt-lite paths such as
// "0:/system/foo.dll" are already drive-indexed and pass through.
char drive = input[0];
if (drive >= 'a' && drive <= 'z') drive -= ('a' - 'A'); // To uppercase
output[0] = (drive >= '0' && drive <= '7') ? drive : (char)('0' + (drive - 'C'));
output[1] = ':';
output[2] = '/';
// Copy the rest, swapping backslashes to forward slashes.
const char* src = input + 3;
char* dst = output + 3;
while (*src) {
*dst = (*src == '\\') ? '/' : *src;
src++; dst++;
}
*dst = '\0';
} else {
// Fallback for relative paths: just swap slashes.
while (*input) {
*output = (*input == '\\') ? '/' : *input;
input++; output++;
}
*output = '\0';
const char* src = input;
char* dst = output;
if (src[0] != '\0' && src[1] == ':' && (src[2] == '\\' || src[2] == '/')) {
char drive = src[0];
if (drive >= 'a' && drive <= 'z') drive = (char)(drive - ('a' - 'A'));
if (drive != 'C') { output[0] = '\0'; return; }
memcpy(dst, "/SystemRoot", 11u);
dst += 11u;
src += 3;
if (*src) *dst++ = '/';
} else if (*src == '\\' || *src == '/') {
memcpy(dst, "/SystemRoot", 11u);
dst += 11u;
while (*src == '\\' || *src == '/') src++;
if (*src) *dst++ = '/';
}
while (*src) {
*dst++ = (*src == '\\') ? '/' : *src;
src++;
}
*dst = '\0';
}
// Implements the k32 Open File Native operation.
@@ -273,60 +273,79 @@ static uint32_t K32OpenFileNative(const char* path,
uint32_t access,
uint32_t disposition,
uint32_t* handleOut) {
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
access,
disposition,
SYS_FILE_FLAG_NONE,
handleOut);
return SysOpenFile(&req);
return NtlOpenFile(SysSvFromCstr(path), access, disposition, SYS_FILE_FLAG_NONE, handleOut);
}
// Implements the k32 Delete File Native operation.
static uint32_t K32DeleteFileNative(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysDeleteFile(&req);
return NtlDeleteFile(SysSvFromCstr(path));
}
// Implements the k32 Open Dir Native operation.
static uint32_t K32OpenDirNative(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenDir(&req);
return NtlOpenDir(SysSvFromCstr(path), SYS_DIR_ACCESS_ENUMERATE | SYS_DIR_ACCESS_OPEN,
SYS_DIR_FLAG_NONE, handleOut);
}
// Implements the k32 Set Cwd Native operation.
static uint32_t K32SetCwdNative(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysSetCwd(&req);
return NtlSetCurrentDirectory(SysSvFromCstr(path));
}
// Implements the k32 Get Cwd Native operation.
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
SYS_STRING_VIEW cwd = SysGetCurrentDirectoryView();
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) return NTLSTATUS_INVALID_PARAMETER;
if (lengthOut) *lengthOut = cwd.Length;
if (!buffer || cwd.Length + 1 > capacity) return NTLSTATUS_BUFFER_TOO_SMALL;
size_t required = 0;
uint32_t status = NtlGetCurrentDirectory(buffer, capacity, &required);
if (lengthOut) *lengthOut = required ? required - 1u : 0;
return status;
}
if (cwd.Length) memcpy(buffer, cwd.Data, cwd.Length);
buffer[cwd.Length] = '\0';
// Resolves packaged personality import aliases entirely in userspace.
static KSTATUS K32ResolveModuleName(void* context, SYS_STRING_VIEW requested, SYS_STRING_VIEW* canonicalOut) {
return NtlResolveSystemModuleName(context, requested, canonicalOut);
}
// Translates Win32 process syntax into native resource/CWD syntax.
static uint32_t K32ProcessPath(const char* input, char* output, size_t outputSize) {
if (!input || !output || outputSize == 0) return NTLSTATUS_INVALID_PARAMETER;
char translated[MAX_PATH];
TranslatePath(input, translated);
if (!translated[0]) return NTLSTATUS_NOT_FOUND;
size_t length = strlen(translated);
if (length + 1u > outputSize) return NTLSTATUS_BUFFER_TOO_SMALL;
memcpy(output, translated, length + 1u);
return NTLSTATUS_SUCCESS;
}
// Implements the k32 Create Process Native operation.
static uint32_t K32CreateProcessNative(const char* path, uint32_t* handleOut, const char* commandLine) {
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(commandLine), handleOut };
return SysCreateProcess(&req);
// Creates one Win32 child through RSSRV lookup plus the userspace PE loader.
static uint32_t K32CreateProcessNative(const char* path, uint32_t imageFile, uint32_t* handleOut, const char* commandLine, const char* currentDirectory) {
char relative[MAX_PATH];
uint32_t status = K32ProcessPath(path, relative, sizeof(relative));
if (status != NTLSTATUS_SUCCESS) return status;
NTL_CREATE_SYSTEM_PROCESS_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
req.ImageFile = imageFile;
req.Path = SysSvFromCstr(relative);
req.CommandLine = SysSvFromCstr(commandLine);
req.CurrentDirectory = currentDirectory ? SysSvFromCstr(currentDirectory) : SysSvFromParts((const char*)0, 0);
req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
req.ResolveModuleName = K32ResolveModuleName;
req.ProcessHandleOut = handleOut;
return NtlCreateSystemProcess(&req);
}
// Implements the k32 Write Console Span operation.
static void K32WriteConsoleSpan(const char* data, size_t length) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(data, length) };
SYS_WRITE_CONSOLE_REQUEST req = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromParts(data, length) };
SysWriteConsole(&req);
}
// Implements the k32 Debug Print A operation.
static void K32DebugPrintA(const char* s) {
if (!s) return;
SYS_PRINT_DEBUG_REQUEST req = { SysSvFromCstr(s) };
SYS_PRINT_DEBUG_REQUEST req = { sizeof(SYS_PRINT_DEBUG_REQUEST), SysSvFromCstr(s) };
SysPrintDebug(&req);
}
@@ -657,11 +676,12 @@ __declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
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);
SYS_CREATE_FILE_MAPPING_REQUEST createMapping = {
sizeof(SYS_CREATE_FILE_MAPPING_REQUEST), HANDLE_TO_KH(hFile),
WinFileMappingProtectToSysVm(flProtect), SYS_MAP_FLAG_NONE,
maximumSize, &kh
};
uint32_t s = SysCreateFileMapping(&createMapping);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
@@ -692,12 +712,12 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
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);
SYS_MAP_FILE_REQUEST mapFile = {
sizeof(SYS_MAP_FILE_REQUEST), HANDLE_TO_KH(hFileMappingObject),
WinFileMapAccessToSys(dwDesiredAccess), SYS_MAP_FLAG_NONE,
offset, dwNumberOfBytesToMap, &base
};
uint32_t s = SysMapFile(&mapFile);
if (s != NTLSTATUS_SUCCESS) {
gLastError = (s == NTLSTATUS_NOT_SUPPORTED) ? 120 : ((s == NTLSTATUS_NO_MEMORY) ? 8 : 87);
return NULL;
@@ -869,7 +889,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
DWORD flags, void* templ) {
(void)share; (void)sa; (void)flags; (void)templ;
// Translate the path from Windows format to custom 0:/ format.
// Translate Win32 path syntax into the native resource/CWD form.
char translatedName[260];
TranslatePath(name, translatedName);
@@ -1364,24 +1384,46 @@ __declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName
return lpModuleName ? NULL : WIN32_HMODULE_EXE;
}
// Loads a user-mode DLL through the native module loader.
// Loads one DLL entirely through the process-local userspace module loader.
__declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) {
if (KiIsWin32DllNameA(lpLibFileName)) return WIN32_HMODULE_WIN32;
if (!lpLibFileName) { gLastError = 87; return NULL; }
if (!lpLibFileName || !lpLibFileName[0]) { gLastError = 87; return NULL; }
uint32_t handle = 0;
SYS_LOAD_MODULE_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Path = SysSvFromCstr(lpLibFileName);
req.ModuleHandleOut = &handle;
SYS_HANDLE root = SYS_HANDLE_INVALID;
uint32_t status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &root);
if (status != NTLSTATUS_SUCCESS) { gLastError = 126; return NULL; }
uint32_t status = SysLoadModule(&req);
if (status != NTLSTATUS_SUCCESS) {
char relative[MAX_PATH];
status = K32ProcessPath(lpLibFileName, relative, sizeof(relative));
if (status == NTLSTATUS_SUCCESS && !strchr(relative, '/')) {
size_t length = strlen(relative);
if (length + sizeof("system/") > sizeof(relative)) status = NTLSTATUS_BUFFER_TOO_SMALL;
else {
memmove(relative + 7, relative, length + 1u);
memcpy(relative, "system/", 7);
}
}
NTL_MODULE_HANDLE module = NTL_MODULE_HANDLE_INVALID;
if (status == NTLSTATUS_SUCCESS) {
NTL_LOAD_MODULE_AT_REQUEST load;
memset(&load, 0, sizeof(load));
load.Size = sizeof(load);
load.Flags = NTL_MODULE_LOAD_FLAG_NONE;
load.RootHandle = root;
load.Path = SysSvFromCstr(relative);
load.ModuleRootHandle = root;
load.ModuleDirectory = SYS_SV_LITERAL("system");
load.ResolveModuleName = K32ResolveModuleName;
load.ModuleHandleOut = &module;
status = NtlLoadModuleAt(&load);
}
(void)SysCloseHandle(root);
if (status != NTLSTATUS_SUCCESS || module == NTL_MODULE_HANDLE_INVALID) {
gLastError = 126;
return NULL;
}
return (HANDLE)(uintptr_t)handle;
return (HANDLE)(uintptr_t)module;
}
// GetProcAddress handles both built-in win32 exports and native module handles.
@@ -1415,13 +1457,8 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
if (hModule != WIN32_HMODULE_WIN32) {
uintptr_t address = 0;
SYS_GET_MODULE_EXPORT_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.ModuleHandle = (uint32_t)(uintptr_t)hModule;
req.Name = SysSvFromCstr(lpProcName);
req.AddressOut = &address;
uint32_t status = SysGetModuleExport(&req);
uint32_t status = NtlGetModuleExport((NTL_MODULE_HANDLE)(uintptr_t)hModule,
SysSvFromCstr(lpProcName), &address);
if (status != NTLSTATUS_SUCCESS || !address) { gLastError = 127; return NULL; }
return (void*)address;
}
@@ -1463,7 +1500,7 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam
__declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule) {
if (!hLibModule || hLibModule == WIN32_HMODULE_WIN32 || hLibModule == WIN32_HMODULE_EXE)
return TRUE;
uint32_t status = SysUnloadModule((uint32_t)(uintptr_t)hLibModule);
uint32_t status = NtlUnloadModule((NTL_MODULE_HANDLE)(uintptr_t)hLibModule);
if (status != NTLSTATUS_SUCCESS) {
gLastError = 126;
return FALSE;
@@ -1593,7 +1630,7 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes,
uint32_t tid = 0;
uint32_t kh = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)K32ThreadStart,
sizeof(SYS_CREATE_THREAD_REQUEST), (uintptr_t)K32ThreadStart,
(uintptr_t)context,
0,
(dwCreationFlags & CREATE_SUSPENDED)
@@ -1767,52 +1804,49 @@ __declspec(dllexport) BOOL CreateProcessA(
const char* cmdline = (lpCommandLine && lpCommandLine[0]) ? lpCommandLine : targetPath;
char translatedPath[MAX_PATH];
TranslatePath(targetPath, translatedPath);
char savedCwd[SYS_CWD_MAX];
bool changedCwd = false;
savedCwd[0] = '\0';
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
char translatedCwd[MAX_PATH];
uint32_t gs = K32GetCwdNative(savedCwd, sizeof(savedCwd), NULL);
if (gs != 0) { gLastError = 3; return FALSE; }
TranslatePath(lpCurrentDirectory, translatedCwd);
gs = K32SetCwdNative(translatedCwd);
if (gs != 0) { gLastError = 3; return FALSE; }
changedCwd = true;
}
uint32_t kh = 0;
// Call the syscall.dll stub. The kernel snapshots the current CWD into
// the child process, so the temporary lpCurrentDirectory switch can be
// restored immediately after creation.
uint32_t s = K32CreateProcessNative(translatedPath, &kh, cmdline);
if (changedCwd) {
if (savedCwd[0]) {
char restoreCwd[SYS_CWD_MAX + 3];
restoreCwd[0] = '0'; restoreCwd[1] = ':'; restoreCwd[2] = '/';
size_t rlen = strlen(savedCwd);
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
memcpy(restoreCwd + 3, savedCwd, rlen);
restoreCwd[3 + rlen] = '\0';
K32SetCwdNative(restoreCwd);
} else {
K32SetCwdNative("0:/");
uint32_t imageFile = SYS_HANDLE_INVALID;
bool relativeImage = targetPath[0] != '/' && targetPath[0] != '\\' &&
!(targetPath[0] && targetPath[1] == ':' &&
(targetPath[2] == '\\' || targetPath[2] == '/'));
if (relativeImage) {
TranslatePath(targetPath, translatedPath);
uint32_t openStatus = K32OpenFileNative(translatedPath,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
&imageFile);
if (openStatus != NTLSTATUS_SUCCESS) {
gLastError = 2;
return FALSE;
}
}
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
} else if (K32ProcessPath(targetPath, translatedPath, sizeof(translatedPath)) != NTLSTATUS_SUCCESS) {
gLastError = 3;
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
char translatedCwd[MAX_PATH];
const char* childCwd = (const char*)0;
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
TranslatePath(lpCurrentDirectory, translatedCwd);
if (!translatedCwd[0]) {
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
gLastError = 3;
return FALSE;
}
childCwd = translatedCwd;
}
uint32_t kh = 0;
uint32_t s = K32CreateProcessNative(translatedPath, imageFile, &kh, cmdline, childCwd);
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
if (s != 0 || kh == 0) {
gLastError = 2;
return FALSE;
}
lpProcessInformation->hProcess = KH_TO_HANDLE(kh);
lpProcessInformation->hThread = INVALID_HANDLE_VALUE;
lpProcessInformation->dwProcessId = 0;
lpProcessInformation->dwThreadId = 0;
return TRUE;
}
@@ -1820,7 +1854,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) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
if (!kh) { gLastError = 6; return FALSE; }
int32_t code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
@@ -2037,9 +2071,11 @@ __declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
return TRUE;
size_t written = 0;
uint32_t s = SysFillConsoleCharacter(cCharacter, (int)dwWriteCoord.X,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
SYS_FILL_CONSOLE_CHARACTER_REQUEST request = {
sizeof(SYS_FILL_CONSOLE_CHARACTER_REQUEST), cCharacter, { 0, 0, 0 },
(int)dwWriteCoord.X, (int)dwWriteCoord.Y, (size_t)nLength, &written
};
uint32_t s = SysFillConsoleCharacter(&request);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written;
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
@@ -2066,9 +2102,11 @@ __declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput,
return TRUE;
size_t written = 0;
uint32_t s = SysFillConsoleAttribute(wAttribute, (int)dwWriteCoord.X,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST request = {
sizeof(SYS_FILL_CONSOLE_ATTRIBUTE_REQUEST), wAttribute, 0,
(int)dwWriteCoord.X, (int)dwWriteCoord.Y, (size_t)nLength, &written
};
uint32_t s = SysFillConsoleAttribute(&request);
if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written;
if (s != NTLSTATUS_SUCCESS) {
K32SetConsoleLastError(s);
@@ -2335,24 +2373,25 @@ __declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
__declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) {
if (!lpBuffer || nBufferLength == 0) {
// Return a conservative required size including NUL.
return SYS_CWD_MAX + 1;
return SYS_PATH_MAX + 1u;
}
// Kernel returns native "N:/path". Present it as a Win32 drive letter.
char raw[SYS_CWD_MAX + 1];
char raw[SYS_PATH_MAX + 1u];
uint32_t s = K32GetCwdNative(raw, sizeof(raw), NULL);
if (s != 0) { gLastError = 1; return 0; }
char full[SYS_CWD_MAX + 1];
char drive = 'C';
const char* rel = raw;
if (raw[0] >= '0' && raw[0] <= '7' && raw[1] == ':' &&
(raw[2] == '\\' || raw[2] == '/')) {
drive = (char)('C' + (raw[0] - '0'));
rel = raw + 3;
static const char prefix[] = "/SystemRoot";
size_t prefixLength = sizeof(prefix) - 1u;
if (strncmp(raw, prefix, prefixLength) != 0 ||
(raw[prefixLength] != '\0' && raw[prefixLength] != '/')) {
gLastError = 3;
return 0;
}
const char* rel = raw + prefixLength;
if (*rel == '/') rel++;
full[0] = drive;
char full[SYS_PATH_MAX + 1u];
full[0] = 'C';
full[1] = ':';
full[2] = '\\';
size_t rawLen = strlen(rel);