refactor syscall ABI and FAT32 driver to use request structs and string views natively, enforce handle access rights on object lookup, add scheduler statistics tracking, fix KiSchedule to prevent 50% idle throttling on CPU-bound threads, better document x64 initial user stack alignment, fix process teardown to properly signal exit events and manage thread counts, optimize doom file I/O with a custom handle struct to internally track position and size, add SubsystemData to TEB for generic thread-local state, apply PAGE_NX to non-executable PE sections in the loader, update native apps, tests, and shells to match new APIs, update kernel32 directory retrieval.

This commit is contained in:
maksw committed 2026-05-26 14:46:49 +02:00
1 parent 603fbdfab1
commit e1248f4afe
43 files changed
+2892 -1857

No files matched your search

+9 -3
View File
@@ -7,7 +7,13 @@
#define EVENT_AUTO_RESET 0u
static void print(const char* s) {
SysWriteConsole(s, strlen(s));
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
}
static void print_hex8(uint8_t v) {
@@ -44,7 +50,7 @@ static void file_apc(void* context, SYS_IO_STATUS_BLOCK* iosb, uint64_t informat
st->Status = iosb ? iosb->Status : 0xFFFFFFFFu;
st->Information = information;
st->Delivered++;
SysDebugPrint("async_apc: user APC callback ran\n");
do { SYS_DEBUG_PRINT_REQUEST req = { SYS_SV_LITERAL("async_apc: user APC callback ran\n") }; SysDebugPrint(&req); } while (0);
}
int main(void) {
@@ -59,7 +65,7 @@ int main(void) {
}
uint32_t file = 0;
s = SysOpenFile("system/init.exe", &file);
s = open_file("system/init.exe", &file);
if (s != SYS_STATUS_SUCCESS) {
print("async_apc: open failed status=");
print_u64(s);
+9 -3
View File
@@ -6,7 +6,13 @@
#define EVENT_AUTO_RESET 0u
static void print(const char* s) {
SysWriteConsole(s, strlen(s));
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
}
static void print_u64(uint64_t v) {
@@ -22,7 +28,7 @@ static void print_u64(uint64_t v) {
}
while (i--) {
char c = buf[i];
SysWriteConsole(&c, 1);
do { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) }; SysWriteConsole(&req); } while (0);
}
}
@@ -33,7 +39,7 @@ int main(void) {
uint32_t event = 0;
uint32_t request = 0;
uint32_t s = SysOpenFile("system/init.exe", &file);
uint32_t s = open_file("system/init.exe", &file);
if (s != SYS_STATUS_SUCCESS) {
print("async_cancel: open failed status="); print_u64(s); print("\n");
SysExitProcess(1);
+1 -1
View File
@@ -1,5 +1,5 @@
[kernel]
; bitmask: 1 = debug prints, 2 = self check
debug = 1
debug = 3
[init]
commandline = apps/shell_native.exe
+62 -27
View File
@@ -19,7 +19,8 @@ int _fltused;
// ---------------------------------------------------------------------------
static void doom_print_wrapper(const char* msg)
{
SysWriteConsole(msg, strlen(msg));
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(msg) };
SysWriteConsole(&req);
}
// ---------------------------------------------------------------------------
@@ -42,74 +43,108 @@ static void doom_free_wrapper(void* ptr)
// ---------------------------------------------------------------------------
// File I/O wrappers
// ---------------------------------------------------------------------------
typedef struct DOOM_FILE_HANDLE {
uint32_t Handle;
uint64_t Pos;
uint64_t Size;
} DOOM_FILE_HANDLE;
static void* doom_open_wrapper(const char* path, const char* mode)
{
uint32_t handle = 0;
uint32_t result;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &handle };
if (mode[0] == 'w') {
result = SysCreateFile(path, &handle);
result = SysCreateFile(&req);
} else {
result = SysOpenFile(path, &handle);
result = SysOpenFile(&req);
}
if (result != 0) return 0;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)SysAlloc(sizeof(DOOM_FILE_HANDLE));
if (!f) {
SysCloseHandle(handle);
return 0;
}
return (result == 0) ? (void*)(uintptr_t)handle : 0;
f->Handle = handle;
f->Pos = 0;
f->Size = SysGetFileSize(handle);
return f;
}
static void doom_close_wrapper(void* handle)
{
SysCloseHandle((uint32_t)(uintptr_t)handle);
if (!handle) return;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
SysCloseHandle(f->Handle);
SysFree(f);
}
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((uint32_t)(uintptr_t)handle, buf, (size_t)count, &read);
SysReadFile(f->Handle, buf, (size_t)count, &read);
f->Pos += read;
if (f->Pos > f->Size) f->Size = f->Pos;
return (int)read;
}
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((uint32_t)(uintptr_t)handle, buf, (size_t)count, &written);
SysWriteFile(f->Handle, buf, (size_t)count, &written);
f->Pos += written;
if (f->Pos > f->Size) f->Size = f->Pos;
return (int)written;
}
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
{
uint32_t h = (uint32_t)(uintptr_t)handle;
uint64_t absolute_offset = 0;
if (!handle) return -1;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
int64_t base = 0;
switch (origin) {
case DOOM_SEEK_SET:
absolute_offset = (uint64_t)offset;
case DOOM_SEEK_SET:
base = 0;
break;
case DOOM_SEEK_CUR:
absolute_offset = SysGetFilePos(h) + offset;
case DOOM_SEEK_CUR:
base = (int64_t)f->Pos;
break;
case DOOM_SEEK_END:
absolute_offset = SysGetFileSize(h) + offset;
case DOOM_SEEK_END:
base = (int64_t)f->Size;
break;
default:
absolute_offset = (uint64_t)offset;
default:
base = 0;
break;
}
SysSeekFile(h, absolute_offset);
int64_t target = base + (int64_t)offset;
if (target < 0) target = 0;
uint32_t s = SysSeekFile(f->Handle, (uint64_t)target);
if (s != 0) return -1;
f->Pos = (uint64_t)target;
return 0;
}
static int doom_tell_wrapper(void* handle)
{
return (int)SysGetFilePos((uint32_t)(uintptr_t)handle);
if (!handle) return -1;
return (int)((DOOM_FILE_HANDLE*)handle)->Pos;
}
static int doom_eof_wrapper(void* handle)
{
uint32_t h = (uint32_t)(uintptr_t)handle;
uint64_t size = SysGetFileSize(h);
uint64_t pos = SysGetFilePos(h);
return (pos >= size) ? 1 : 0;
if (!handle) return 1;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
return (f->Pos >= f->Size) ? 1 : 0;
}
// ---------------------------------------------------------------------------
@@ -305,9 +340,9 @@ static int build_argv_from_command_line(const char* cmdline, char* storage, size
// ---------------------------------------------------------------------------
int main(void)
{
const char* cmdline = SysGetPeb()->CommandLine;
const char* cmdline = SysGetPeb()->CommandLine.Data;
SysWriteConsole("doom: starting\n", 15);
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: starting\n") }; SysWriteConsole(&req); } while (0);
// Wire up PureDOOM core mechanics
doom_set_print(doom_print_wrapper);
@@ -339,7 +374,7 @@ int main(void)
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
doom_init(argc, argv, 0);
SysWriteConsole("doom: entering game loop\n", 25);
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: entering game loop\n") }; SysWriteConsole(&req); } while (0);
// Core execution loop
while (1) {
+248 -144
View File
@@ -425,26 +425,6 @@ static KSTATUS DirentUpdateFirstCluster(FAT32_VOL *v, uint64_t lba,
// ---------------------------------------------------------------------------
// 8.3 name matching / assembly
// ---------------------------------------------------------------------------
static int MatchName83(const char *dirName, const char *dirExt,
const char *want) {
char buf[13]; int n = 0;
for (int i = 0; i < 8 && dirName[i] != ' '; i++)
buf[n++] = (char)(dirName[i] >= 'A' && dirName[i] <= 'Z' ? dirName[i]+32 : dirName[i]);
if (dirExt[0] != ' ') {
buf[n++] = '.';
for (int i = 0; i < 3 && dirExt[i] != ' '; i++)
buf[n++] = (char)(dirExt[i] >= 'A' && dirExt[i] <= 'Z' ? dirExt[i]+32 : dirExt[i]);
}
buf[n] = '\0';
const char *p = buf;
while (*want && *p) {
char a = *p++, b = *want++;
if (a >= 'A' && a <= 'Z') a += 32;
if (b >= 'A' && b <= 'Z') b += 32;
if (a != b) return 0;
}
return *want == '\0' && *p == '\0';
}
static int MatchRaw83(const char dirName[8], const char dirExt[3],
const char wantName[8], const char wantExt[3]) {
@@ -452,34 +432,35 @@ static int MatchRaw83(const char dirName[8], const char dirExt[3],
memcmp(dirExt, wantExt, 3) == 0;
}
static void Make83Name(const char *src, char name8[8], char ext3[3]) {
memset(name8, ' ', 8); memset(ext3, ' ', 3);
static char FatUpperChar(char c);
static int FatIsLowerChar(char c);
static int FatIsValidShortChar(char c);
// A leading dot is not a FAT 8.3 extension separator. The old version
// turned ".doomrc" into an invalid empty-base short name with extension
// "DOO", making .doomrc, .doom and .doo all alias the same entry. Treat
// leading-dot names as a basename here; the create path will usually emit
// a VFAT long-name entry for them anyway.
const char *base = src;
const char *dot = NULL;
if (src && src[0] == '.' && src[1]) {
base = src + 1;
static void Make83NameV(SYS_STRING_VIEW src, char name8[8], char ext3[3]) {
memset(name8, ' ', 8);
memset(ext3, ' ', 3);
if (!SysSvIsValid(src) || src.Length == 0) return;
size_t baseStart = 0;
size_t dot = (size_t)-1;
if (src.Data[0] == '.' && src.Length > 1) {
baseStart = 1;
} else {
for (const char *p = src; p && *p; p++) if (*p == '.') dot = p;
for (size_t i = 0; i < src.Length; i++)
if (src.Data[i] == '.') dot = i;
}
size_t end = (dot != (size_t)-1) ? dot : src.Length;
int ni = 0;
const char *end = dot ? dot : base + strlen(base);
for (const char *p = base; p < end && ni < 8; p++) {
char c = *p;
if (c >= 'a' && c <= 'z') c -= 32;
for (size_t i = baseStart; i < end && ni < 8; i++) {
char c = FatUpperChar(src.Data[i]);
name8[ni++] = c;
}
if (dot) {
if (dot != (size_t)-1) {
int ei = 0;
for (const char *p = dot + 1; *p && ei < 3; p++) {
char c = *p;
if (c >= 'a' && c <= 'z') c -= 32;
for (size_t i = dot + 1; i < src.Length && ei < 3; i++) {
char c = FatUpperChar(src.Data[i]);
ext3[ei++] = c;
}
}
@@ -515,30 +496,31 @@ static int FatIsValidShortChar(char c) {
// True when the user spelling can refer to a normal FAT 8.3 entry. This is
// intentionally structural and case-insensitive: "doom1.wad" must still open
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
static int FatNameIsStructurallyShort83(const char *name) {
if (!name || !name[0]) return 0;
if (name[0] == '.') return 0;
static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) {
if (!SysSvIsValid(name) || name.Length == 0) return 0;
if (name.Data[0] == '.') return 0;
const char *dot = NULL;
for (const char *p = name; *p; p++) {
if (*p == '.') {
if (dot) return 0; // multiple dots need LFN
dot = p;
} else if (!FatIsValidShortChar(*p)) {
size_t dot = (size_t)-1;
for (size_t i = 0; i < name.Length; i++) {
char c = name.Data[i];
if (c == '.') {
if (dot != (size_t)-1) return 0;
dot = i;
} else if (!FatIsValidShortChar(c)) {
return 0;
}
}
size_t baseLen = dot ? (size_t)(dot - name) : strlen(name);
size_t extLen = dot ? strlen(dot + 1) : 0;
size_t baseLen = (dot != (size_t)-1) ? dot : name.Length;
size_t extLen = (dot != (size_t)-1) ? (name.Length - dot - 1) : 0;
return baseLen >= 1 && baseLen <= 8 && extLen <= 3;
}
// Creation uses LFN when needed for structure, or when lowercase would be lost.
static int FatNameNeedsLfnForCreate(const char *name) {
if (!FatNameIsStructurallyShort83(name)) return 1;
for (const char *p = name; *p; p++)
if (FatIsLowerChar(*p)) return 1;
static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) {
if (!FatNameIsStructurallyShort83V(name)) return 1;
for (size_t i = 0; i < name.Length; i++)
if (FatIsLowerChar(name.Data[i])) return 1;
return 0;
}
@@ -624,33 +606,35 @@ static void FatAliasAppendChar(char *dst, int *len, int maxLen, char c) {
if (*len < maxLen) dst[(*len)++] = c;
}
static void BuildAliasStemExt(const char *src, char stem[9], char ext3[3]) {
static void BuildAliasStemExtV(SYS_STRING_VIEW src, char stem[9], char ext3[3]) {
memset(stem, 0, 9);
memset(ext3, ' ', 3);
if (!SysSvIsValid(src)) return;
const char *base = src;
const char *dot = NULL;
if (src[0] == '.' && src[1]) {
size_t baseStart = 0;
size_t dot = (size_t)-1;
if (src.Length > 1 && src.Data[0] == '.') {
// Hidden/Unix-style names like .doomrc become alias stem DOOMRC.
base = src + 1;
baseStart = 1;
} else {
for (const char *p = src; *p; p++) if (*p == '.') dot = p;
for (size_t i = 0; i < src.Length; i++)
if (src.Data[i] == '.') dot = i;
}
int slen = 0;
const char *end = dot ? dot : src + strlen(src);
for (const char *p = base; p < end && *p; p++)
FatAliasAppendChar(stem, &slen, 8, *p);
size_t end = (dot != (size_t)-1) ? dot : src.Length;
for (size_t i = baseStart; i < end; i++)
FatAliasAppendChar(stem, &slen, 8, src.Data[i]);
if (slen == 0) {
memcpy(stem, "FILE", 4);
slen = 4;
}
stem[slen] = '\0';
if (dot && dot[1]) {
if (dot != (size_t)-1 && dot + 1 < src.Length) {
int ei = 0;
for (const char *p = dot + 1; *p && ei < 3; p++) {
char c = FatUpperChar(*p);
for (size_t i = dot + 1; i < src.Length && ei < 3; i++) {
char c = FatUpperChar(src.Data[i]);
if (!FatIsValidShortChar(c)) continue;
ext3[ei++] = c;
}
@@ -683,11 +667,11 @@ static void MakeAliasAttempt(const char stem[9], const char extIn[3],
memcpy(name8 + keep, suffix, suffixLen);
}
static int MakeUniqueShortAlias(FAT32_VOL *v, uint32_t dirCluster,
const char *longName,
char name8[8], char ext3[3]) {
static int MakeUniqueShortAliasV(FAT32_VOL *v, uint32_t dirCluster,
SYS_STRING_VIEW longName,
char name8[8], char ext3[3]) {
char stem[9], extCandidate[3];
BuildAliasStemExt(longName, stem, extCandidate);
BuildAliasStemExtV(longName, stem, extCandidate);
for (uint32_t attempt = 1; attempt < 1000000; attempt++) {
MakeAliasAttempt(stem, extCandidate, attempt, name8, ext3);
@@ -696,12 +680,25 @@ static int MakeUniqueShortAlias(FAT32_VOL *v, uint32_t dirCluster,
return 0;
}
static KSTATUS DeleteLegacyBrokenDotAlias(FAT32_VOL *v, uint32_t dirCluster,
const char *longName) {
if (!longName || longName[0] != '.' || !longName[1]) return STATUS_SUCCESS;
static void MakeLegacyBrokenDot83V(SYS_STRING_VIEW src, char name8[8], char ext3[3]) {
// This exactly models the pre-fix bug for ".doomrc": empty name + "DOO".
memset(name8, ' ', 8);
memset(ext3, ' ', 3);
if (!SysSvIsValid(src)) return;
size_t dot = (size_t)-1;
for (size_t i = 0; i < src.Length; i++) if (src.Data[i] == '.') dot = i;
if (dot == (size_t)-1) return;
int ei = 0;
for (size_t i = dot + 1; i < src.Length && ei < 3; i++)
ext3[ei++] = FatUpperChar(src.Data[i]);
}
static KSTATUS DeleteLegacyBrokenDotAliasV(FAT32_VOL *v, uint32_t dirCluster,
SYS_STRING_VIEW longName) {
if (!SysSvIsValid(longName) || longName.Length < 2 || longName.Data[0] != '.') return STATUS_SUCCESS;
char badName[8], badExt[3];
MakeLegacyBrokenDot83(longName, badName, badExt);
MakeLegacyBrokenDot83V(longName, badName, badExt);
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY;
@@ -754,29 +751,71 @@ static void LfnAppend(char *lfn, const LFN_ENTRY *e) {
}
}
static int LfnMatch(const char *lfn, const char *want) {
while (*lfn && *want) {
char a = *lfn++, b = *want++;
// ---------------------------------------------------------------------------
// Directory search and view-native path traversal
// ---------------------------------------------------------------------------
static int Fat32IsPathSep(char c) {
return c == '\\' || c == '/';
}
static int Fat32NameViewIsValid(SYS_STRING_VIEW name) {
if (!SysSvIsValid(name)) return 0;
if (name.Length == 0 || name.Length >= MAX_LFN) return 0;
for (size_t i = 0; i < name.Length; i++) {
char c = name.Data[i];
if (c == '\0' || Fat32IsPathSep(c)) return 0;
}
return 1;
}
static int LfnMatchV(const char *lfn, SYS_STRING_VIEW want) {
if (!lfn || !SysSvIsValid(want)) return 0;
size_t i = 0;
while (lfn[i] && i < want.Length) {
char a = lfn[i], b = want.Data[i];
if (a >= 'A' && a <= 'Z') a += 32;
if (b >= 'A' && b <= 'Z') b += 32;
if (a != b) return 0;
i++;
}
return lfn[i] == '\0' && i == want.Length;
}
static int MatchName83V(const char *dirName, const char *dirExt,
SYS_STRING_VIEW want) {
char buf[13];
int n = 0;
for (int i = 0; i < 8 && dirName[i] != ' '; i++)
buf[n++] = (char)(dirName[i] >= 'A' && dirName[i] <= 'Z' ? dirName[i]+32 : dirName[i]);
if (dirExt[0] != ' ') {
buf[n++] = '.';
for (int i = 0; i < 3 && dirExt[i] != ' '; i++)
buf[n++] = (char)(dirExt[i] >= 'A' && dirExt[i] <= 'Z' ? dirExt[i]+32 : dirExt[i]);
}
if ((size_t)n != want.Length) return 0;
for (int i = 0; i < n; i++) {
char a = buf[i], b = want.Data[i];
if (a >= 'A' && a <= 'Z') a += 32;
if (b >= 'A' && b <= 'Z') b += 32;
if (a != b) return 0;
}
return *lfn == '\0' && *want == '\0';
return 1;
}
// ---------------------------------------------------------------------------
// Directory search
// ---------------------------------------------------------------------------
static int SearchDir(FAT32_VOL *v, uint32_t dirCluster, const char *name,
uint32_t *outCluster, uint32_t *outSize, uint8_t *outAttr,
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
static int SearchDirV(FAT32_VOL *v, uint32_t dirCluster, SYS_STRING_VIEW name,
uint32_t *outCluster, uint32_t *outSize, uint8_t *outAttr,
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
if (!Fat32NameViewIsValid(name)) return 0;
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return 0;
char lfn[MAX_LFN]; int hasLfn = 0; memset(lfn, 0, sizeof(lfn));
char wantName83[8], wantExt83[3];
int allowShortMatch = FatNameIsStructurallyShort83(name);
Make83Name(name, wantName83, wantExt83);
int allowShortMatch = FatNameIsStructurallyShort83V(name);
Make83NameV(name, wantName83, wantExt83);
uint32_t cluster = dirCluster;
while (cluster >= 2 && cluster < FAT32_EOC) {
@@ -792,9 +831,9 @@ static int SearchDir(FAT32_VOL *v, uint32_t dirCluster, const char *name,
if (dir[i].Attr == ATTR_LFN) { LfnAppend(lfn, (LFN_ENTRY *)&dir[i]); hasLfn = 1; continue; }
int match = 0;
if (hasLfn && lfn[0]) match = LfnMatch(lfn, name);
if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name);
if (!match && allowShortMatch)
match = MatchName83(dir[i].Name, dir[i].Ext, name);
match = MatchName83V(dir[i].Name, dir[i].Ext, name);
if (!match && allowShortMatch)
match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83);
hasLfn = 0; memset(lfn, 0, sizeof(lfn));
@@ -819,58 +858,92 @@ done:
return 0;
}
// ---------------------------------------------------------------------------
// Path traversal
// ---------------------------------------------------------------------------
static KSTATUS Fat32LookupPath(FAT32_VOL *v, const char *path,
uint32_t *outCluster, uint32_t *outSize,
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
static int Fat32PathNextComponent(SYS_STRING_VIEW path, size_t* cursor,
SYS_STRING_VIEW* component) {
if (!cursor || !component || !SysSvIsValid(path)) return 0;
size_t pos = *cursor;
while (pos < path.Length && Fat32IsPathSep(path.Data[pos])) pos++;
if (pos >= path.Length) {
*cursor = pos;
*component = SYS_SV_NULL;
return 0;
}
size_t start = pos;
while (pos < path.Length && !Fat32IsPathSep(path.Data[pos])) pos++;
*cursor = pos;
*component = SysSvFromParts(path.Data + start, pos - start);
return 1;
}
static KSTATUS Fat32LookupPathV(FAT32_VOL *v, SYS_STRING_VIEW path,
uint32_t *outCluster, uint32_t *outSize,
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
if (!v || !outCluster || !outSize || !SysSvIsValid(path))
return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(path, '\0'))
return STATUS_INVALID_PARAMETER;
uint32_t cluster = v->RootCluster, size = 0;
uint8_t attr = ATTR_DIRECTORY;
if (outDirentLba) *outDirentLba = 0;
if (outDirentOffset) *outDirentOffset = 0;
char component[256]; const char *p = path;
while (*p) {
while (*p == '\\' || *p == '/') p++;
if (!*p) break;
int len = 0;
while (*p && *p != '\\' && *p != '/' && len < 255) component[len++] = *p++;
component[len] = '\0';
if (!len) break;
size_t cursor = 0;
SYS_STRING_VIEW component;
while (Fat32PathNextComponent(path, &cursor, &component)) {
if (component.Length >= MAX_LFN) return STATUS_BUFFER_TOO_SMALL;
if (!Fat32NameViewIsValid(component)) return STATUS_INVALID_PARAMETER;
if (!(attr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
uint32_t childCluster, childSize; uint8_t childAttr;
uint64_t childLba = 0; uint32_t childOffset = 0;
if (!SearchDir(v, cluster, component, &childCluster, &childSize,
&childAttr, &childLba, &childOffset))
if (!SearchDirV(v, cluster, component, &childCluster, &childSize,
&childAttr, &childLba, &childOffset))
return STATUS_NOT_FOUND;
cluster = childCluster; size = childSize; attr = childAttr;
if (outDirentLba) *outDirentLba = childLba;
if (outDirentOffset) *outDirentOffset = childOffset;
}
*outCluster = cluster; *outSize = size;
*outCluster = cluster;
*outSize = size;
return STATUS_SUCCESS;
}
static KSTATUS Traverse(FAT32_VOL *v, const char *path,
static KSTATUS TraverseV(FAT32_VOL *v, SYS_STRING_VIEW path,
uint32_t *outCluster, uint32_t *outSize) {
return Fat32LookupPath(v, path, outCluster, outSize, NULL, NULL);
return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL);
}
// ---------------------------------------------------------------------------
// Path helpers for Fat32Create
// ---------------------------------------------------------------------------
static void SplitPath(const char *path, char parent[256], char name[256]) {
const char *last = NULL;
for (const char *p = path; *p; p++) if (*p == '\\' || *p == '/') last = p;
if (!last) { parent[0] = '\0'; strncpy(name, path, 255); name[255] = '\0'; }
else {
size_t len = (size_t)(last - path); if (len > 255) len = 255;
memcpy(parent, path, len); parent[len] = '\0';
strncpy(name, last+1, 255); name[255] = '\0';
static KSTATUS SplitPathV(SYS_STRING_VIEW path, SYS_STRING_VIEW* parent,
SYS_STRING_VIEW* name) {
if (!parent || !name || !SysSvIsValid(path)) return STATUS_INVALID_PARAMETER;
*parent = SYS_SV_NULL;
*name = SYS_SV_NULL;
while (path.Length && Fat32IsPathSep(path.Data[path.Length - 1]))
path.Length--;
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(path, '\0')) return STATUS_INVALID_PARAMETER;
size_t lastSep = (size_t)-1;
for (size_t i = 0; i < path.Length; i++) {
if (Fat32IsPathSep(path.Data[i])) lastSep = i;
}
if (lastSep == (size_t)-1) {
*parent = SYS_SV_NULL;
*name = path;
} else {
*parent = SysSvFromParts(path.Data, lastSep);
*name = SysSvFromParts(path.Data + lastSep + 1, path.Length - lastSep - 1);
}
if (!Fat32NameViewIsValid(*name)) return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
@@ -969,11 +1042,15 @@ static int FindFreeDirentRun(FAT32_VOL *v, uint32_t dirCluster, uint32_t needed,
// ---------------------------------------------------------------------------
// Open / Close
// ---------------------------------------------------------------------------
static KSTATUS Fat32Open(IO_FILESYSTEM *fs, const char *path, void **fileCtx) {
static KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
KSTATUS s = Fat32LookupPath(v, path, &cluster, &size, &direntLba, &direntOffset);
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
if (!f) return STATUS_NO_MEMORY;
f->StartCluster = cluster; f->FileSize = size; f->Vol = v;
@@ -1013,11 +1090,19 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
FAT32_VOL *v = f->Vol;
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS;
if (offset + size > f->FileSize) size = (size_t)(f->FileSize - offset);
if (size > (size_t)(f->FileSize - offset)) size = (size_t)(f->FileSize - offset);
size_t wanted = size;
// Single allocation for the largest possible coalesced run.
uint8_t *runBuf = (uint8_t *)MmAllocatePool(MAX_RUN_SECTORS * 512u);
// Allocate only as much bounce space as this read can actually use.
// The old code allocated 64 KiB and could read a 64 KiB contiguous run even
// for tiny random reads; Doom does lots of seek/read pairs for small lumps.
uint32_t initialOffsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
uint64_t initialBytesCovered = (uint64_t)initialOffsetInCluster + (uint64_t)size;
uint32_t maxUsefulSectors = (uint32_t)((initialBytesCovered + 511u) / 512u);
if (maxUsefulSectors < v->SectorsPerCluster) maxUsefulSectors = v->SectorsPerCluster;
if (maxUsefulSectors > MAX_RUN_SECTORS) maxUsefulSectors = MAX_RUN_SECTORS;
uint8_t *runBuf = (uint8_t *)MmAllocatePool((size_t)maxUsefulSectors * 512u);
if (!runBuf) return STATUS_NO_MEMORY;
size_t totalRead = 0;
@@ -1031,13 +1116,21 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
cluster = FatEntry(v, cluster);
while (size > 0 && cluster >= 2 && cluster < FAT32_EOC) {
// Build the longest physically-contiguous run we can.
// Build the longest physically-contiguous run that can contribute to
// this caller's remaining read. Do not prefetch an unrelated 64 KiB run
// for a tiny random read.
uint64_t bytesCovered = (uint64_t)offsetInCluster + (uint64_t)size;
uint32_t usefulSectors = (uint32_t)((bytesCovered + 511u) / 512u);
if (usefulSectors < v->SectorsPerCluster) usefulSectors = v->SectorsPerCluster;
if (usefulSectors > MAX_RUN_SECTORS) usefulSectors = MAX_RUN_SECTORS;
if (usefulSectors > maxUsefulSectors) usefulSectors = maxUsefulSectors;
uint32_t runStart = cluster;
uint32_t runSectors = v->SectorsPerCluster;
uint32_t next = FatEntry(v, cluster);
while (next == cluster + 1 && next < FAT32_EOC
&& runSectors + v->SectorsPerCluster <= MAX_RUN_SECTORS) {
&& runSectors + v->SectorsPerCluster <= usefulSectors) {
cluster = next;
next = FatEntry(v, cluster);
runSectors += v->SectorsPerCluster;
@@ -1188,21 +1281,25 @@ static KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
// ---------------------------------------------------------------------------
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
static KSTATUS Fat32Create(IO_FILESYSTEM *fs, const char *path, void **fileCtx) {
static KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
char parentPath[256], filename[256];
SplitPath(path, parentPath, filename);
if (!filename[0]) return STATUS_INVALID_PARAMETER;
SYS_STRING_VIEW parentPath;
SYS_STRING_VIEW filenameView;
KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
if (!KSUCCESS(s)) return s;
uint32_t parentCluster, parentSize;
KSTATUS s = Traverse(v, parentPath, &parentCluster, &parentSize);
s = TraverseV(v, parentPath, &parentCluster, &parentSize);
if (!KSUCCESS(s)) return s;
{
uint32_t ec, es; uint8_t ea;
uint64_t existingLba = 0; uint32_t existingOffset = 0;
if (SearchDir(v, parentCluster, filename, &ec, &es, &ea,
&existingLba, &existingOffset)) {
if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
&existingLba, &existingOffset)) {
if (ea & ATTR_DIRECTORY)
return STATUS_OBJECT_NAME_COLLISION;
@@ -1224,18 +1321,18 @@ static KSTATUS Fat32Create(IO_FILESYSTEM *fs, const char *path, void **fileCtx)
}
}
int useLfn = FatNameNeedsLfnForCreate(filename);
int useLfn = FatNameNeedsLfnForCreateV(filenameView);
char shortName[8], shortExt[3];
if (useLfn) {
s = DeleteLegacyBrokenDotAlias(v, parentCluster, filename);
s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
if (!KSUCCESS(s)) return s;
if (!MakeUniqueShortAlias(v, parentCluster, filename, shortName, shortExt))
if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
return STATUS_OBJECT_NAME_COLLISION;
} else {
Make83Name(filename, shortName, shortExt);
Make83NameV(filenameView, shortName, shortExt);
}
size_t nameLen = strlen(filename);
size_t nameLen = filenameView.Length;
if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
uint32_t neededEntries = lfnCount + 1u;
@@ -1258,7 +1355,7 @@ static KSTATUS Fat32Create(IO_FILESYSTEM *fs, const char *path, void **fileCtx)
uint8_t checksum = LfnChecksum(shortName, shortExt);
for (uint32_t slot = 0; slot < lfnCount; slot++) {
uint32_t ordinal = lfnCount - slot;
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filename,
BuildLfnEntry((LFN_ENTRY *)&dir[firstIndex + slot], filenameView.Data,
nameLen, ordinal, lfnCount, checksum);
}
}
@@ -1348,10 +1445,13 @@ update_size:
// ---------------------------------------------------------------------------
// Delete
// ---------------------------------------------------------------------------
static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, const char *path) {
static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
if (!fs || !SysSvIsValid(path) || path.Length == 0)
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
KSTATUS s = Fat32LookupPath(v, path, &cluster, &size, &direntLba, &direntOffset);
KSTATUS s = Fat32LookupPathV(v, path, &cluster, &size, &direntLba, &direntOffset);
if (!KSUCCESS(s)) return s;
if (!direntLba) return STATUS_ACCESS_DENIED;
@@ -1389,13 +1489,17 @@ typedef struct {
uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir
} FAT32_DIR_CTX;
static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, const char *dirPath, void **dirCtx) {
static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx) {
if (!fs || !dirCtx || !SysSvIsValid(dirPath))
return STATUS_INVALID_PARAMETER;
FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster;
if (!dirPath || dirPath[0] == '\0') {
if (dirPath.Length == 0) {
cluster = v->RootCluster;
} else {
uint32_t size; KSTATUS s = Traverse(v, dirPath, &cluster, &size);
uint32_t size;
KSTATUS s = TraverseV(v, dirPath, &cluster, &size);
if (!KSUCCESS(s)) return s;
}
+8 -5
View File
@@ -302,8 +302,9 @@ static void move_down(void) {
static void save_file(void) {
uint32_t h = 0;
uint32_t st = SysCreateFile(gFilename, &h);
if (st != 0) st = SysOpenFile(gFilename, &h);
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
uint32_t st = SysCreateFile(&pathReq);
if (st != 0) st = SysOpenFile(&pathReq);
if (st != 0) { msg_set("save failed: cannot open file"); return; }
SysTruncateFile(h, 0);
@@ -339,7 +340,8 @@ static void load_file(void) {
reset_buffer();
uint32_t h = 0;
if (SysOpenFile(gFilename, &h) != 0) {
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
if (SysOpenFile(&pathReq) != 0) {
msg_set("new file");
return;
}
@@ -411,7 +413,7 @@ static bool read_token(const char* s, char* out, size_t cap) {
static void parse_filename_from_cmdline(void) {
SYS_PEB* peb = SysGetPeb();
const char* cmd = peb ? peb->CommandLine : NULL;
const char* cmd = (peb && peb->CommandLine.Data) ? peb->CommandLine.Data : NULL;
const char* arg = skip_token(cmd);
if (read_token(arg, gFilename, sizeof(gFilename))) return;
strcpy(gFilename, "untitled.txt");
@@ -476,7 +478,8 @@ static void editor_loop(void) {
}
static void print(const char* s) {
SysWriteConsole(s, strlen(s));
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
int main(void) {
+29 -10
View File
@@ -23,6 +23,7 @@ static volatile uint32_t* gLapic = NULL;
static uint32_t gApicTicksPerUs = 0;
static uint32_t gApicTicksPerQuantum = 0;
static uint64_t gHpetTicksPerUs = 0;
static uint64_t gHpetHz = 0;
static volatile uint64_t* gHpet = NULL;
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
@@ -39,11 +40,28 @@ static void TimerIsr(void) {
}
static uint64_t QueryTicks(void) {
uint64_t baseTicks = gTicks; // The 10ms "coarse" ticks
// The LAPIC timer counts DOWN.
// Ticks elapsed = (Initial Value) - (Current Value)
uint32_t current = LAPIC_REG(LAPIC_TIMER_CUR);
// Prefer HPET when present: it is already a monotonically increasing main
// counter and avoids races between the LAPIC periodic tick and the current
// countdown register.
if (gHpet && gHpetHz) {
uint64_t counter = gHpet[30]; // offset 0xF0: main counter
uint64_t seconds = counter / gHpetHz;
uint64_t rem = counter % gHpetHz;
return seconds * 1000000ull + (rem * 1000000ull) / gHpetHz;
}
// Fallback: combine the coarse periodic tick with the current LAPIC
// countdown. Read the coarse tick twice so an interrupt between gTicks and
// LAPIC_TIMER_CUR cannot make time jump backwards by almost one quantum.
uint64_t baseTicks1;
uint64_t baseTicks2;
uint32_t current;
do {
baseTicks1 = gTicks;
current = LAPIC_REG(LAPIC_TIMER_CUR);
baseTicks2 = gTicks;
} while (baseTicks1 != baseTicks2);
uint32_t initial = gApicTicksPerQuantum;
uint32_t elapsedApicTicks = 0;
@@ -51,11 +69,11 @@ static uint64_t QueryTicks(void) {
elapsedApicTicks = initial - current;
}
// Convert base 10ms ticks to microseconds + the remainder
// gTicks * 10,000 (ms to us) + (elapsed / ticks_per_us)
uint64_t totalUs = (baseTicks * QUANTUM_MS * 1000) + (elapsedApicTicks / gApicTicksPerUs);
return totalUs;
uint64_t subQuantumUs = 0;
if (gApicTicksPerUs)
subQuantumUs = elapsedApicTicks / gApicTicksPerUs;
return (baseTicks1 * QUANTUM_MS * 1000ull) + subQuantumUs;
}
static void Stall(uint32_t us) {
@@ -181,6 +199,7 @@ KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *o
if (hpetPhys) {
gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys);
uint64_t hpetHz = HpetInit(hpetPhys);
gHpetHz = hpetHz;
gHpetTicksPerUs = hpetHz / 1000000;
}
+5 -5
View File
@@ -5,11 +5,12 @@
#include <syscall.h>
static void print(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
}
static void print_char(char c) {
SysWriteConsole(&c, 1);
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
@@ -115,8 +116,7 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
SYS_RESOLVE_HOST_REQUEST req;
memset(&resolved, 0, sizeof(resolved));
memset(&req, 0, sizeof(req));
req.Name = host;
req.NameLength = strlen(host);
req.Name = SysSvFromCstr(host);
req.Family = SYS_AF_INET;
req.AddressOut = &resolved;
req.AddressSizeInOut = &resolvedSize;
@@ -142,7 +142,7 @@ int main(void) {
path[0] = '/';
path[1] = 0;
const char* cmd = SysGetPeb()->CommandLine;
const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[256];
cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) {
+17 -11
View File
@@ -48,32 +48,38 @@ void itoa(int num, char* str) {
}
}
static void debug_print(const char* s) {
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
(void)SysDebugPrint(&req);
}
void _start(void)
{
const char* args = SysGetPeb()->CommandLine;
SYS_STRING_VIEW args = SysGetPeb()->CommandLine;
uint32_t h = 0;
uint32_t s = 0;
while (true) {
SysDebugPrint("[init] starting\n");
s = SysCreateProcess(args, &h, NULL);
debug_print("[init] starting\n");
SYS_CREATE_PROCESS_REQUEST cp = { args, SysSvFromParts(0, 0), &h };
s = SysCreateProcess(&cp);
if (s != STATUS_SUCCESS) {
SysDebugPrint("[init] FATAL: failed to start!\n");
debug_print("[init] FATAL: failed to start!\n");
char buffer[12];
itoa(s, buffer);
SysDebugPrint(buffer);
SysDebugPrint("\n");
debug_print(buffer);
debug_print("\n");
SysExitProcess(1);
}
SysDebugPrint("[init] waiting\n");
debug_print("[init] waiting\n");
s = SysWaitForProcess(h);
if (s != STATUS_SUCCESS)
SysDebugPrint("[init] WARNING: SysWaitForProcess returned error\n");
debug_print("[init] WARNING: SysWaitForProcess returned error\n");
int exitCode = 0;
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
SysDebugPrint("[init] WARNING: failed to read exit code\n");
debug_print("[init] WARNING: failed to read exit code\n");
exitCode = 1;
}
@@ -82,9 +88,9 @@ void _start(void)
if (exitCode == 0)
break;
SysDebugPrint("[init] child crashed, restarting\n");
debug_print("[init] child crashed, restarting\n");
}
SysDebugPrint("[init] done\n");
debug_print("[init] done\n");
SysExitProcess(0);
}
+176 -145
View File
@@ -387,22 +387,26 @@ __declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJE
// Stored as (CurrentDrive, drive-relative path with no trailing backslash).
// Exported as native "N:/path" so personalities can round-trip volumes.
// ---------------------------------------------------------------------------
#define CWD_MAX 512
#define CWD_MAX PROCESS_CWD_MAX
static size_t KiFormatCwd(uint8_t drive, const char* rel, char* buf, size_t size) {
if (!buf || size == 0) return 0;
typedef struct KRESOLVED_PATH {
uint8_t Drive;
char Storage[CWD_MAX];
SYS_STRING_VIEW Path;
} KRESOLVED_PATH;
static size_t KiFormatCwd(uint8_t drive, SYS_STRING_VIEW rel, char* buf, size_t size) {
if (!buf || size == 0 || !SysSvIsValid(rel)) return 0;
if (drive > 7) drive = 0;
if (!rel) rel = "";
size_t relLen = strlen(rel);
size_t needed = 3 + relLen + 1;
size_t needed = 3 + rel.Length + 1;
if (needed > size) return needed;
buf[0] = (char)('0' + drive);
buf[1] = ':';
buf[2] = '/';
memcpy(buf + 3, rel, relLen);
buf[3 + relLen] = '\0';
if (rel.Length) memcpy(buf + 3, rel.Data, rel.Length);
buf[3 + rel.Length] = '\0';
return needed;
}
@@ -413,17 +417,57 @@ __declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
size_t needed = KiFormatCwd(proc->CurrentDrive, proc->CurrentDirectory, buf, size);
size_t needed = KiFormatCwd(proc->CurrentDrive, SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength), buf, size);
KeReleaseSpinlock(&proc->CwdLock, flags);
return needed && needed <= size ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
}
// Resolve a possibly-relative path component against the CWD.
// Writes the fully-resolved, drive-stripped relative path into 'resolved'
// (caller supplies buf of at least CWD_MAX bytes).
// Returns the drive index.
static uint8_t KiResolvePath(const char* path, char* resolved, size_t resolvedSize) {
static int KiSvHasDriveRoot(SYS_STRING_VIEW path) {
return path.Length >= 3 &&
path.Data[0] >= '0' && path.Data[0] <= '7' &&
path.Data[1] == ':' &&
(path.Data[2] == '\\' || path.Data[2] == '/');
}
static int KiAppendBytes(char* work, size_t* workLen, size_t cap,
const char* data, size_t len) {
if (!work || !workLen || (!data && len)) return 0;
if (*workLen + len >= cap) return 0;
if (len) memcpy(work + *workLen, data, len);
*workLen += len;
work[*workLen] = '\0';
return 1;
}
static int KiAppendComponent(char* work, size_t* workLen, size_t cap,
const char* data, size_t len) {
if (!work || !workLen || (!data && len)) return 0;
if (*workLen && work[*workLen - 1] != '\\' && work[*workLen - 1] != '/') {
if (!KiAppendBytes(work, workLen, cap, "\\", 1)) return 0;
}
return KiAppendBytes(work, workLen, cap, data, len);
}
static void KiPopComponent(char* work, size_t* workLen) {
if (!work || !workLen || *workLen == 0) return;
size_t i = *workLen;
while (i > 0 && work[i - 1] != '\\' && work[i - 1] != '/') i--;
if (i > 0) i--;
*workLen = i;
work[i] = '\0';
}
// Resolve a possibly-relative string view against the CWD.
// The returned view is backed by out->Storage and remains valid while 'out' lives.
static KSTATUS KiResolvePathV(SYS_STRING_VIEW input, KRESOLVED_PATH* out) {
if (!out) return STATUS_INVALID_PARAMETER;
memset(out, 0, sizeof(*out));
if (!SysSvIsValid(input)) return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(input, '\0')) return STATUS_INVALID_PARAMETER;
if (input.Length >= CWD_MAX) return STATUS_BUFFER_TOO_SMALL;
uint8_t drive = 0;
PROCESS* proc = KeGetCurrentProcess();
if (proc) {
@@ -432,128 +476,98 @@ static uint8_t KiResolvePath(const char* path, char* resolved, size_t resolvedSi
KeReleaseSpinlock(&proc->CwdLock, flags);
}
// Strip explicit drive prefix "N:\" or "N:/".
size_t pos = 0;
bool fromDriveRoot = false;
if (path[0] >= '0' && path[0] <= '7' &&
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
drive = (uint8_t)(path[0] - '0');
path = path + 3;
if (KiSvHasDriveRoot(input)) {
drive = (uint8_t)(input.Data[0] - '0');
pos = 3;
fromDriveRoot = true;
}
char work[CWD_MAX * 2];
size_t workLen = 0;
work[0] = '\0';
// Initialize our working base
if (path[0] == '\\' || path[0] == '/' || fromDriveRoot) {
// Absolute path: start from the root (empty work buffer)
// The segment loop below will automatically skip the leading separators.
} else {
// Relative path: start from the current process's CWD
PROCESS* proc = KeGetCurrentProcess();
if (proc) {
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
size_t wlen = strlen(proc->CurrentDirectory);
if (wlen >= sizeof(work)) wlen = sizeof(work) - 1;
memcpy(work, proc->CurrentDirectory, wlen);
work[wlen] = '\0';
KeReleaseSpinlock(&proc->CwdLock, flags);
}
bool absolute = fromDriveRoot ||
(pos < input.Length && (input.Data[pos] == '\\' || input.Data[pos] == '/'));
if (!absolute && proc) {
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
size_t cwdLen = proc->CurrentDirectoryLength;
if (cwdLen >= sizeof(work)) cwdLen = sizeof(work) - 1;
memcpy(work, proc->CurrentDirectory, cwdLen);
work[cwdLen] = '\0';
workLen = cwdLen;
KeReleaseSpinlock(&proc->CwdLock, flags);
}
// Segment-by-segment canonicalization
const char* p = path;
while (*p) {
// Skip separators
while (*p == '\\' || *p == '/') p++;
if (!*p) break;
while (pos < input.Length) {
while (pos < input.Length && (input.Data[pos] == '\\' || input.Data[pos] == '/')) pos++;
if (pos >= input.Length) break;
// Read next component
char comp[256];
int clen = 0;
while (*p && *p != '\\' && *p != '/' && clen < 255)
comp[clen++] = *p++;
comp[clen] = '\0';
size_t compStart = pos;
while (pos < input.Length && input.Data[pos] != '\\' && input.Data[pos] != '/') pos++;
size_t compLen = pos - compStart;
if (compLen == 0) continue;
if (clen == 0) continue;
if (strcmp(comp, ".") == 0) {
// Stay in place
} else if (strcmp(comp, "..") == 0) {
// Remove last component from work
int i = (int)strlen(work) - 1;
while (i >= 0 && work[i] != '\\' && work[i] != '/') i--;
if (i < 0) i = 0;
work[i] = '\0';
} else {
// Append separator + component
size_t wl = strlen(work);
if (wl > 0 && work[wl-1] != '\\' && work[wl-1] != '/') {
if (wl + 1 < sizeof(work)) { work[wl] = '\\'; work[wl+1] = '\0'; }
}
size_t wl2 = strlen(work);
if (wl2 + clen + 1 < sizeof(work)) {
memcpy(work + wl2, comp, clen);
work[wl2 + clen] = '\0';
}
if (compLen == 1 && input.Data[compStart] == '.') {
continue;
}
if (compLen == 2 && input.Data[compStart] == '.' && input.Data[compStart + 1] == '.') {
KiPopComponent(work, &workLen);
continue;
}
if (compLen >= 256) return STATUS_BUFFER_TOO_SMALL;
if (!KiAppendComponent(work, &workLen, sizeof(work), input.Data + compStart, compLen))
return STATUS_BUFFER_TOO_SMALL;
}
// Output the final canonicalized, drive-stripped path
size_t finalLen = strlen(work);
if (finalLen + 1 > resolvedSize) finalLen = resolvedSize - 1;
memcpy(resolved, work, finalLen);
resolved[finalLen] = '\0';
return drive;
if (workLen + 1 > sizeof(out->Storage)) return STATUS_BUFFER_TOO_SMALL;
memcpy(out->Storage, work, workLen);
out->Storage[workLen] = '\0';
out->Drive = drive;
out->Path = SysSvFromParts(out->Storage, workLen);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS IoSetCwd(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path) {
if (path.Length && !path.Data) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED;
// Resolve the new path (this also validates ".." navigation).
// Note: KiResolvePath already uses proc->CurrentDirectory internally!
char resolved[CWD_MAX];
uint8_t drive = KiResolvePath(path, resolved, sizeof(resolved));
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
// Validate: try to open the directory to confirm it actually exists on disk
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
void* dirCtx = NULL;
KSTATUS s = fs->OpenDir(fs, resolved, &dirCtx);
s = fs->OpenDir(fs, resolved.Path, &dirCtx);
if (!KSUCCESS(s)) return s;
fs->CloseDir(fs, dirCtx);
// Commit the new CWD to the process block
size_t len = strlen(resolved);
if (len + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
if (resolved.Path.Length + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
proc->CurrentDrive = drive;
memcpy(proc->CurrentDirectory, resolved, len + 1);
proc->CurrentDrive = resolved.Drive;
proc->CurrentDirectoryLength = resolved.Path.Length;
memcpy(proc->CurrentDirectory, resolved.Path.Data, resolved.Path.Length);
proc->CurrentDirectory[resolved.Path.Length] = '\0';
// Keep the user-visible PEB mirror in sync. The PEB's CWD buffer is a
// fixed SYS_CWD_MAX allocation made at process creation, so the pointer
// remains stable for personalities that cache SYS_PEB*.
if (proc->UserCwdBase && proc->UserCwdSize) {
size_t formatted = KiFormatCwd(drive, resolved, (char*)proc->UserCwdBase, proc->UserCwdSize);
size_t formatted = KiFormatCwd(resolved.Drive, resolved.Path, (char*)proc->UserCwdBase, proc->UserCwdSize);
size_t copyLen = (formatted && formatted <= proc->UserCwdSize) ? formatted - 1 : proc->UserCwdSize - 1;
SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase;
if (peb && peb->Magic == SYS_PEB_MAGIC) {
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = copyLen;
peb->CurrentDirectory = SysSvFromParts((const char*)proc->UserCwdBase, copyLen);
}
}
KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_SUCCESS;
}
@@ -583,18 +597,20 @@ static void KiFileDestroy(OBJECT* obj) {
// ---------------------------------------------------------------------------
// IoOpenFile
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
if (!path || !out) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out) {
if ((!path.Data && path.Length) || !out) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
char relPath[CWD_MAX];
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
void* fileCtx;
KSTATUS s = fs->Open(fs, relPath, &fileCtx);
s = fs->Open(fs, resolved.Path, &fileCtx);
if (!KSUCCESS(s)) return s;
FILE_OBJECT* f = (FILE_OBJECT *)ObAllocateObject(ObjectTypeFile /* reuse slot */,
@@ -603,6 +619,7 @@ __declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) {
f->Header.Destroy = KiFileDestroy;
f->Fs = fs;
f->FileCtx = fileCtx;
f->Lock = (SPINLOCK)SPINLOCK_INIT;
f->Size = fs->GetSize(fs, fileCtx);
f->Offset = 0;
@@ -701,19 +718,20 @@ __declspec(dllexport) KSTATUS IoWriteFile(FILE_OBJECT* file, const void* buf,
// ---------------------------------------------------------------------------
// IoCreateFile - create a new file and return it open for writing
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out) {
if (!path || !out) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoCreateFile(SYS_STRING_VIEW path, FILE_OBJECT** out) {
if ((!path.Data && path.Length) || !out || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX];
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Create) return STATUS_NOT_SUPPORTED;
void* fileCtx;
KSTATUS s = fs->Create(fs, relPath, &fileCtx);
s = fs->Create(fs, resolved.Path, &fileCtx);
if (!KSUCCESS(s)) return s;
FILE_OBJECT* f = (FILE_OBJECT*)ObAllocateObject(ObjectTypeFile,
@@ -733,63 +751,69 @@ __declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out)
// ---------------------------------------------------------------------------
// IoDeleteFile - delete a file by path (does not require an open handle)
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoDeleteFile(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoDeleteFile(SYS_STRING_VIEW path) {
if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX];
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Delete) return STATUS_NOT_SUPPORTED;
return fs->Delete(fs, relPath);
return fs->Delete(fs, resolved.Path);
}
__declspec(dllexport) KSTATUS IoRenameFile(const char* oldPath, const char* newPath) {
if (!oldPath || !newPath) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoRenameFile(SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath) {
if ((!oldPath.Data && oldPath.Length) || (!newPath.Data && newPath.Length) ||
oldPath.Length == 0 || newPath.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
char oldRel[CWD_MAX];
char newRel[CWD_MAX];
uint8_t oldDrive = KiResolvePath(oldPath, oldRel, sizeof(oldRel));
uint8_t newDrive = KiResolvePath(newPath, newRel, sizeof(newRel));
if (oldDrive != newDrive) return STATUS_NOT_SUPPORTED;
KRESOLVED_PATH oldResolved;
KRESOLVED_PATH newResolved;
KSTATUS s = KiResolvePathV(oldPath, &oldResolved);
if (!KSUCCESS(s)) return s;
s = KiResolvePathV(newPath, &newResolved);
if (!KSUCCESS(s)) return s;
if (oldResolved.Drive != newResolved.Drive) return STATUS_NOT_SUPPORTED;
IO_FILESYSTEM* fs = gVolumes[oldDrive].Fs;
IO_FILESYSTEM* fs = gVolumes[oldResolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Rename) return STATUS_NOT_SUPPORTED;
return fs->Rename(fs, oldRel, newRel);
return fs->Rename(fs, oldResolved.Path, newResolved.Path);
}
__declspec(dllexport) KSTATUS IoCreateDir(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoCreateDir(SYS_STRING_VIEW path) {
if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX];
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->CreateDir) return STATUS_NOT_SUPPORTED;
return fs->CreateDir(fs, relPath);
return fs->CreateDir(fs, resolved.Path);
}
__declspec(dllexport) KSTATUS IoRemoveDir(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
__declspec(dllexport) KSTATUS IoRemoveDir(SYS_STRING_VIEW path) {
if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX];
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath));
KRESOLVED_PATH resolved;
KSTATUS s = KiResolvePathV(path, &resolved);
if (!KSUCCESS(s)) return s;
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->RemoveDir) return STATUS_NOT_SUPPORTED;
return fs->RemoveDir(fs, relPath);
return fs->RemoveDir(fs, resolved.Path);
}
// ---------------------------------------------------------------------------
@@ -840,37 +864,44 @@ static void KiDirDestroy(OBJECT* obj) {
if (d->Fs && d->DirCtx) d->Fs->CloseDir(d->Fs, d->DirCtx);
}
__declspec(dllexport) KSTATUS IoOpenDir(const char* dirPath, DIR_OBJECT** out) {
__declspec(dllexport) KSTATUS IoOpenDir(SYS_STRING_VIEW dirPath, DIR_OBJECT** out) {
if (!out) return STATUS_INVALID_PARAMETER;
if (dirPath.Length && !dirPath.Data) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL;
// NULL or empty path means the CWD itself
char relPath[CWD_MAX];
uint8_t drive;
KRESOLVED_PATH resolved;
KSTATUS s;
if (!dirPath || dirPath[0] == '\0') {
if (dirPath.Length == 0) {
PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
memcpy(relPath, proc->CurrentDirectory, strlen(proc->CurrentDirectory) + 1);
drive = proc->CurrentDrive;
size_t cwdLen = proc->CurrentDirectoryLength;
if (cwdLen + 1 > sizeof(resolved.Storage)) {
KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_BUFFER_TOO_SMALL;
}
memcpy(resolved.Storage, proc->CurrentDirectory, cwdLen + 1);
resolved.Drive = proc->CurrentDrive;
resolved.Path = SysSvFromParts(resolved.Storage, cwdLen);
KeReleaseSpinlock(&proc->CwdLock, flags);
} else {
drive = KiResolvePath(dirPath, relPath, sizeof(relPath));
s = KiResolvePathV(dirPath, &resolved);
if (!KSUCCESS(s)) return s;
}
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
void* dirCtx = NULL;
KSTATUS s = fs->OpenDir(fs, relPath, &dirCtx);
s = fs->OpenDir(fs, resolved.Path, &dirCtx);
if (!KSUCCESS(s)) return s;
DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT));
if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; }
d->Header.Destroy = KiDirDestroy;
d->Fs = fs;
d->DirCtx = dirCtx;
+16 -15
View File
@@ -7,6 +7,7 @@
#include <ke_status.h>
#include <ob_object.h>
#include <ke_spinlock.h>
#include <sys_string.h>
// ---------------------------------------------------------------------------
// KDIR_ENTRY - one directory entry returned by ReadDir / SYS_READ_DIR
@@ -35,7 +36,7 @@ typedef struct IO_FILESYSTEM {
// Open a file by absolute path (e.g. "apps\\shell.exe").
// Returns an opaque per-file driver cookie in* fileCtx.
KSTATUS (*Open)(struct IO_FILESYSTEM* fs,
const char* path, void **fileCtx);
SYS_STRING_VIEW path, void **fileCtx);
// Read up to 'size' bytes at 'offset' into 'buf'.
// Sets* bytesRead to actual bytes copied.
@@ -58,19 +59,19 @@ typedef struct IO_FILESYSTEM {
void* completeContext);
// Create a new file at 'path'; returns an open fileCtx on success.
KSTATUS (*Create)(struct IO_FILESYSTEM* fs, const char* path,
KSTATUS (*Create)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path,
void** fileCtx);
// Delete the file at 'path'.
KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, const char* path);
KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
// Rename or move a file/directory within the same filesystem.
KSTATUS (*Rename)(struct IO_FILESYSTEM* fs,
const char* oldPath, const char* newPath);
SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath);
// Create or remove directories.
KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, const char* path);
KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, const char* path);
KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
// Truncate or extend the file to exactly 'newSize' bytes.
KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx,
@@ -92,7 +93,7 @@ typedef struct IO_FILESYSTEM {
// Open a directory iterator for 'dirPath' (relative or drive-prefixed).
// Returns an opaque per-iteration driver cookie in *dirCtx.
KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs,
const char* dirPath, void** dirCtx);
SYS_STRING_VIEW dirPath, void** dirCtx);
// Advance to the next entry.
// Fills 'out' with name, attributes, and size.
@@ -153,7 +154,7 @@ __declspec(dllexport) KSTATUS IoRegisterFilesystem(uint8_t driveIndex, IO_FILESY
// ---------------------------------------------------------------------------
// Kernel-side API
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out);
__declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out);
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf,
size_t size, size_t* bytesRead);
__declspec(dllexport) KSTATUS IoReadFileAt(FILE_OBJECT* file, uint64_t offset,
@@ -175,11 +176,11 @@ __declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OB
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request);
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosbOut);
__declspec(dllexport) KSTATUS IoCreateFile (const char* path, FILE_OBJECT** out);
__declspec(dllexport) KSTATUS IoDeleteFile (const char* path);
__declspec(dllexport) KSTATUS IoRenameFile (const char* oldPath, const char* newPath);
__declspec(dllexport) KSTATUS IoCreateDir (const char* path);
__declspec(dllexport) KSTATUS IoRemoveDir (const char* path);
__declspec(dllexport) KSTATUS IoCreateFile (SYS_STRING_VIEW path, FILE_OBJECT** out);
__declspec(dllexport) KSTATUS IoDeleteFile (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoRenameFile (SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath);
__declspec(dllexport) KSTATUS IoCreateDir (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoRemoveDir (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
@@ -188,12 +189,12 @@ __declspec(dllexport) void IoCloseFile(FILE_OBJECT* file);
// ---------------------------------------------------------------------------
// Directory enumeration API
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoOpenDir (const char* dirPath, DIR_OBJECT** out);
__declspec(dllexport) KSTATUS IoOpenDir (SYS_STRING_VIEW dirPath, DIR_OBJECT** out);
__declspec(dllexport) KSTATUS IoReadDir (DIR_OBJECT* dir, KDIR_ENTRY* entryOut);
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir);
// ---------------------------------------------------------------------------
// Current Working Directory
// ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoSetCwd(const char* path);
__declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size);
+161 -56
View File
@@ -88,15 +88,17 @@ static void KiProcessDestroy(OBJECT* obj) {
// Release all remaining handles.
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
if (proc->Handles[i]) {
ObDereferenceObject(proc->Handles[i]);
proc->Handles[i] = (OBJECT *)0;
if (proc->Handles[i].Object) {
ObDereferenceObject(proc->Handles[i].Object);
proc->Handles[i].Object = (OBJECT*)0;
proc->Handles[i].Rights = 0;
proc->Handles[i].Flags = 0;
}
}
// Release the main executable image.
if (proc->ImageBase && proc->ImageSize) {
MmFreeUserMemory((void *)proc->ImageBase);
MmFreeUserImage((void *)proc->ImageBase, proc->ImageSize);
proc->ImageBase = 0;
proc->ImageSize = 0;
}
@@ -104,7 +106,7 @@ static void KiProcessDestroy(OBJECT* obj) {
// Release per-process modules.
for (uint32_t i = 0; i < proc->ModuleCount; i++) {
if (proc->Modules[i].Base && proc->Modules[i].Size) {
MmFreeUserMemory((void *)proc->Modules[i].Base);
MmFreeUserImage((void *)proc->Modules[i].Base, proc->Modules[i].Size);
}
proc->Modules[i].Base = 0;
proc->Modules[i].Size = 0;
@@ -170,6 +172,8 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->ThreadCount = 0;
proc->HandleLock = (SPINLOCK)SPINLOCK_INIT;
proc->ExitCode = 0;
proc->Terminating = 0;
proc->Exited = 0;
proc->ImageBase = 0;
proc->ImageSize = 0;
proc->UserPebBase = 0;
@@ -182,6 +186,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->UserCwdSize = 0;
proc->ModuleCount = 0;
proc->CurrentDirectory[0] = '\0';
proc->CurrentDirectoryLength = 0;
proc->CurrentDrive = 0;
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
@@ -208,33 +213,47 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
// ---------------------------------------------------------------------------
// KeSetupUserProcessEnvironment
// ---------------------------------------------------------------------------
static KSTATUS KiCopyEnvironmentString(const char* src, uintptr_t* baseOut, size_t* sizeOut) {
static KSTATUS KiCopyEnvironmentStringView(SYS_STRING_VIEW src,
uintptr_t* baseOut,
size_t* sizeOut) {
if (!baseOut || !sizeOut) return STATUS_INVALID_PARAMETER;
if (!src) src = "";
*baseOut = 0;
*sizeOut = 0;
size_t len = strlen(src) + 1;
if (!SysSvIsValid(src)) return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(src, '\0')) return STATUS_INVALID_PARAMETER;
if (src.Length == (size_t)-1) return STATUS_BUFFER_TOO_SMALL;
size_t len = src.Length + 1;
char* dst = (char*)MmAllocateUserMemory(len);
if (!dst) return STATUS_NO_MEMORY;
memcpy(dst, src, len);
if (src.Length) memcpy(dst, src.Data, src.Length);
dst[src.Length] = '\0';
*baseOut = (uintptr_t)dst;
*sizeOut = len;
return STATUS_SUCCESS;
}
static KSTATUS KiCopyEnvironmentStringFixed(const char* src, size_t capacity,
uintptr_t* baseOut, size_t* sizeOut) {
static KSTATUS KiCopyEnvironmentStringViewFixed(SYS_STRING_VIEW src,
size_t capacity,
uintptr_t* baseOut,
size_t* sizeOut) {
if (!baseOut || !sizeOut || capacity == 0) return STATUS_INVALID_PARAMETER;
if (!src) src = "";
*baseOut = 0;
*sizeOut = 0;
if (!SysSvIsValid(src)) return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(src, '\0')) return STATUS_INVALID_PARAMETER;
if (src.Length + 1 > capacity) return STATUS_BUFFER_TOO_SMALL;
char* dst = (char*)MmAllocateUserMemory(capacity);
if (!dst) return STATUS_NO_MEMORY;
memset(dst, 0, capacity);
size_t len = strlen(src);
if (len >= capacity) len = capacity - 1;
memcpy(dst, src, len);
dst[len] = '\0';
if (src.Length) memcpy(dst, src.Data, src.Length);
dst[src.Length] = '\0';
*baseOut = (uintptr_t)dst;
*sizeOut = capacity;
@@ -266,43 +285,68 @@ static void KiFreePartialProcessEnvironment(PROCESS* proc) {
}
}
static void KiFormatNativeCwd(const PROCESS* proc, const char* currentDirectory,
char* out, size_t outSize) {
if (!out || outSize == 0) return;
const char* rel = currentDirectory ? currentDirectory : "";
static KSTATUS KiFormatNativeCwdV(const PROCESS* proc,
SYS_STRING_VIEW currentDirectory,
char* out,
size_t outSize,
size_t* lengthOut) {
if (lengthOut) *lengthOut = 0;
if (!out || outSize == 0) return STATUS_INVALID_PARAMETER;
out[0] = '\0';
if (!SysSvIsValid(currentDirectory)) return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(currentDirectory, '\0')) return STATUS_INVALID_PARAMETER;
uint8_t drive = proc ? proc->CurrentDrive : 0;
if (drive > 7) drive = 0;
if (outSize < 4) { out[0] = '\0'; return; }
if (currentDirectory.Length + 4 > outSize) return STATUS_BUFFER_TOO_SMALL;
out[0] = (char)('0' + drive);
out[1] = ':';
out[2] = '/';
size_t len = strlen(rel);
if (len + 4 > outSize) len = outSize - 4;
memcpy(out + 3, rel, len);
out[3 + len] = '\0';
if (currentDirectory.Length)
memcpy(out + 3, currentDirectory.Data, currentDirectory.Length);
out[3 + currentDirectory.Length] = '\0';
if (lengthOut) *lengthOut = 3 + currentDirectory.Length;
return STATUS_SUCCESS;
}
KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
const char* imagePath,
const char* commandLine,
const char* currentDirectory) {
if (!proc || !imagePath) return STATUS_INVALID_PARAMETER;
SYS_STRING_VIEW imagePath,
SYS_STRING_VIEW commandLine,
SYS_STRING_VIEW currentDirectory) {
if (!proc || imagePath.Length == 0) return STATUS_INVALID_PARAMETER;
if (!SysSvIsValid(imagePath) || !SysSvIsValid(commandLine) || !SysSvIsValid(currentDirectory))
return STATUS_INVALID_PARAMETER;
if (SysSvContainsByte(imagePath, '\0') ||
SysSvContainsByte(commandLine, '\0') ||
SysSvContainsByte(currentDirectory, '\0'))
return STATUS_INVALID_PARAMETER;
SYS_STRING_VIEW finalCommandLine = commandLine.Length ? commandLine : imagePath;
SYS_STRING_VIEW finalCwdRel = currentDirectory;
if (finalCwdRel.Length == 0 && proc->CurrentDirectoryLength)
finalCwdRel = SysSvFromParts(proc->CurrentDirectory, proc->CurrentDirectoryLength);
const char* finalCommandLine = (commandLine && commandLine[0]) ? commandLine : imagePath;
const char* finalCwdRel = currentDirectory ? currentDirectory : proc->CurrentDirectory;
char finalCwd[SYS_CWD_MAX];
KiFormatNativeCwd(proc, finalCwdRel, finalCwd, sizeof(finalCwd));
size_t finalCwdLength = 0;
KSTATUS s = KiFormatNativeCwdV(proc, finalCwdRel, finalCwd, sizeof(finalCwd), &finalCwdLength);
if (!KSUCCESS(s)) return s;
KiFreePartialProcessEnvironment(proc);
KSTATUS s = KiCopyEnvironmentString(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
s = KiCopyEnvironmentStringView(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
if (!KSUCCESS(s)) goto fail;
s = KiCopyEnvironmentString(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
s = KiCopyEnvironmentStringView(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
if (!KSUCCESS(s)) goto fail;
s = KiCopyEnvironmentStringFixed(finalCwd, SYS_CWD_MAX, &proc->UserCwdBase, &proc->UserCwdSize);
s = KiCopyEnvironmentStringViewFixed(SysSvFromParts(finalCwd, finalCwdLength),
SYS_CWD_MAX,
&proc->UserCwdBase,
&proc->UserCwdSize);
if (!KSUCCESS(s)) goto fail;
SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB));
@@ -312,12 +356,9 @@ KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
peb->Magic = SYS_PEB_MAGIC;
peb->Version = SYS_ENV_VERSION;
peb->ProcessId = proc->ProcessId;
peb->ImagePath = (const char*)proc->UserImagePathBase;
peb->ImagePathLength = proc->UserImagePathSize ? proc->UserImagePathSize - 1 : 0;
peb->CommandLine = (const char*)proc->UserCmdlineBase;
peb->CommandLineLength = proc->UserCmdlineSize ? proc->UserCmdlineSize - 1 : 0;
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = strlen(finalCwd);
peb->ImagePath = SysSvFromParts((const char*)proc->UserImagePathBase, imagePath.Length);
peb->CommandLine = SysSvFromParts((const char*)proc->UserCmdlineBase, finalCommandLine.Length);
peb->CurrentDirectory = SysSvFromParts((const char*)proc->UserCwdBase, finalCwdLength);
proc->UserPebBase = (uintptr_t)peb;
proc->UserPebSize = sizeof(SYS_PEB);
@@ -329,16 +370,56 @@ fail:
}
// ---------------------------------------------------------------------------
// KeExitProcess
// Process exit / termination
// ---------------------------------------------------------------------------
static void KiCompleteProcessExitIfLastThread(PROCESS* process) {
if (!process) return;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
uint32_t threadCount = process->ThreadCount;
KeReleaseSpinlock(&process->HandleLock, flags);
if (threadCount == 0 && _InterlockedExchange(&process->Exited, 1) == 0) {
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
}
}
void KeExitProcess(PROCESS* process, int exitCode) {
if (!process) return;
process->ExitCode = exitCode;
// Wake any threads waiting for this process to finish.
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
// The last thread exiting via KeExitThread will drop the process refcount.
// This function is a policy hook; actual cleanup is in KiProcessDestroy.
ObDereferenceObject(&process->Header);
_InterlockedExchange(&process->Terminating, 1);
// The calling thread exits immediately after this in SysExitProcess. Do
// not drop the process object here: each thread owns one process reference,
// and KiThreadDestroy releases that reference after unlinking the thread.
KiCompleteProcessExitIfLastThread(process);
}
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode) {
if (!process) return STATUS_INVALID_PARAMETER;
process->ExitCode = exitCode;
_InterlockedExchange(&process->Terminating, 1);
// Wake alertable waiters so they can notice Terminating at the syscall/
// wait boundary. This is intentionally conservative: fully asynchronous
// forced thread teardown needs object-specific wait-list locking and is a
// larger process-lifecycle project.
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
LIST_ENTRY* head = &process->ThreadList;
for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) {
THREAD* t = CONTAINING_RECORD(node, THREAD, ProcessLink);
if (t != KeGetCurrentThread())
(void)KeAlertThread(t);
}
KeReleaseSpinlock(&process->HandleLock, flags);
KiCompleteProcessExitIfLastThread(process);
return STATUS_SUCCESS;
}
int KeIsProcessTerminating(PROCESS* process) {
return process && process->Terminating != 0;
}
// ---------------------------------------------------------------------------
@@ -353,15 +434,17 @@ KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut) {
// ---------------------------------------------------------------------------
// Handle table
// ---------------------------------------------------------------------------
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut) {
if (!process || !object || !handleOut) return STATUS_INVALID_PARAMETER;
KSTATUS KeAllocateHandleWithRights(PROCESS* process, OBJECT* object, uint32_t rights, uint32_t* handleOut) {
if (!process || !object || !handleOut || rights == 0) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
if (!process->Handles[i]) {
if (!process->Handles[i].Object) {
ObReferenceObject(object);
process->Handles[i] = object;
process->Handles[i].Object = object;
process->Handles[i].Rights = rights;
process->Handles[i].Flags = 0;
*handleOut = (uint32_t)i;
KeReleaseSpinlock(&process->HandleLock, flags);
return STATUS_SUCCESS;
@@ -372,13 +455,19 @@ KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut)
return STATUS_NO_MEMORY;
}
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut) {
return KeAllocateHandleWithRights(process, object, HANDLE_RIGHT_ALL, handleOut);
}
void KeCloseHandle(PROCESS* process, uint32_t handle) {
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
OBJECT* obj = process->Handles[handle];
OBJECT* obj = process->Handles[handle].Object;
if (obj) {
process->Handles[handle] = (OBJECT *)0;
process->Handles[handle].Object = (OBJECT*)0;
process->Handles[handle].Rights = 0;
process->Handles[handle].Flags = 0;
KeReleaseSpinlock(&process->HandleLock, flags);
ObDereferenceObject(obj);
return;
@@ -386,17 +475,33 @@ void KeCloseHandle(PROCESS* process, uint32_t handle) {
KeReleaseSpinlock(&process->HandleLock, flags);
}
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle) {
OBJECT* KeLookupHandleWithRights(PROCESS* process, uint32_t handle, uint32_t requiredRights) {
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE)
return (OBJECT *)0;
return (OBJECT*)0;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
OBJECT* obj = process->Handles[handle];
HANDLE_ENTRY* entry = &process->Handles[handle];
OBJECT* obj = entry->Object;
if (obj && requiredRights && ((entry->Rights & requiredRights) != requiredRights))
obj = (OBJECT*)0;
if (obj) ObReferenceObject(obj);
KeReleaseSpinlock(&process->HandleLock, flags);
return obj;
}
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle) {
return KeLookupHandleWithRights(process, handle, 0);
}
uint32_t KeGetHandleRights(PROCESS* process, uint32_t handle) {
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return 0;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
uint32_t rights = process->Handles[handle].Object ? process->Handles[handle].Rights : 0;
KeReleaseSpinlock(&process->HandleLock, flags);
return rights;
}
PROCESS* KeGetProcessById(uint32_t pid) {
KiProcessInit();
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
+33 -7
View File
@@ -8,12 +8,27 @@
#include <ke_event.h>
#include <stddef.h>
#include <stdint.h>
#include <sys_string.h>
#define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256
#define PROCESS_MODULE_MAX 16
#define PROCESS_CWD_MAX 512
#define HANDLE_RIGHT_READ 0x00000001u
#define HANDLE_RIGHT_WRITE 0x00000002u
#define HANDLE_RIGHT_WAIT 0x00000004u
#define HANDLE_RIGHT_SIGNAL 0x00000008u
#define HANDLE_RIGHT_DUPLICATE 0x00000010u
#define HANDLE_RIGHT_QUERY 0x00000020u
#define HANDLE_RIGHT_TERMINATE 0x00000040u
#define HANDLE_RIGHT_ALL 0xFFFFFFFFu
typedef struct HANDLE_ENTRY {
OBJECT* Object;
uint32_t Rights;
uint32_t Flags;
} HANDLE_ENTRY;
typedef struct PROCESS_MODULE {
char Name[32];
@@ -27,7 +42,8 @@ typedef struct PROCESS {
uint32_t ProcessId;
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
char CurrentDirectory[512];
char CurrentDirectory[PROCESS_CWD_MAX];
size_t CurrentDirectoryLength;
uint8_t CurrentDrive;
SPINLOCK CwdLock;
@@ -36,12 +52,14 @@ typedef struct PROCESS {
uint32_t ThreadCount;
/* Handle table.
Index 0 is always NULL (the invalid handle).
Entries are OBJECT pointers, each holding a reference. */
OBJECT* Handles[HANDLE_TABLE_SIZE];
Index 0 is always empty (the invalid handle).
Each live entry holds one object reference plus rights metadata. */
HANDLE_ENTRY Handles[HANDLE_TABLE_SIZE];
SPINLOCK HandleLock;
int ExitCode;
volatile long Terminating;
volatile long Exited;
/* Main executable image. Used for cleanup. */
uintptr_t ImageBase;
@@ -70,14 +88,22 @@ typedef struct PROCESS {
KSTATUS KeCreateProcess(const char* name, PROCESS **out);
KSTATUS KeCreateSystemProcess(PROCESS** out);
KSTATUS KeSetupUserProcessEnvironment(PROCESS* process, const char* imagePath, const char* commandLine, const char* currentDirectory);
KSTATUS KeSetupUserProcessEnvironment(PROCESS* process,
SYS_STRING_VIEW imagePath,
SYS_STRING_VIEW commandLine,
SYS_STRING_VIEW currentDirectory);
void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode);
int KeIsProcessTerminating(PROCESS* process);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
/* Handle table operations. */
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut);
KSTATUS KeAllocateHandleWithRights(PROCESS* process, OBJECT* object, uint32_t rights, uint32_t* handleOut);
void KeCloseHandle(PROCESS* process, uint32_t handle);
OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
OBJECT* KeLookupHandleWithRights(PROCESS* process, uint32_t handle, uint32_t requiredRights);
uint32_t KeGetHandleRights(PROCESS* process, uint32_t handle);
/* Return the global process list head (kernel-internal). */
LIST_ENTRY* KeGetProcessList(void);
+31
View File
@@ -20,10 +20,18 @@ static THREAD* gIdleThread;
static THREAD* gOutgoingThread;
static uint32_t gAgingCounter = 0;
static volatile long gReschedulePending = 0;
static KE_SCHED_STATS gSchedStats;
THREAD* KeGetCurrentThread(void) { return gCurrentThread; }
void KeSetCurrentThread(THREAD* t) { gCurrentThread = t; }
void KeGetSchedulerStats(KE_SCHED_STATS* out) {
if (!out) return;
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
*out = gSchedStats;
KeReleaseSpinlock(&gSchedLock, flags);
}
static void KiIdleLoop(void) {
while (true) { __halt(); }
}
@@ -121,6 +129,14 @@ void KeUnreadyThread(THREAD* thread) {
KeReleaseSpinlock(&gSchedLock, flags);
}
static int KiAnyRunnableLocked(void) {
for (int p = 0; p < PriorityCount; p++) {
if (!LIST_IS_EMPTY(&gRunQueue[p]))
return 1;
}
return 0;
}
static THREAD* KiPickNext(void) {
for (int p = PriorityCount - 1; p >= 0; p--) {
if (!LIST_IS_EMPTY(&gRunQueue[p])) {
@@ -149,7 +165,18 @@ static void KiSchedule(void) {
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
cur->SchedLockFlags = flags;
// The currently running thread is not present in a run queue. If it is
// still runnable and nobody else is ready, keep executing it. Otherwise a
// lone CPU-bound thread gets alternated with the idle thread on every timer
// tick, effectively turning a periodic preemption check into a 50% throttle.
if (cur != gIdleThread && cur->State == ThreadRunning && !KiAnyRunnableLocked()) {
gSchedStats.KeepCurrentDecisions++;
KeReleaseSpinlock(&gSchedLock, flags);
return;
}
THREAD* next = KiPickNext();
if (next == gIdleThread) gSchedStats.IdleSelections++;
if (next == cur) {
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead
// caller has already placed SchedulerLink elsewhere (or is discarding
@@ -174,6 +201,7 @@ static void KiSchedule(void) {
}
gOutgoingThread = cur;
gSchedStats.ContextSwitches++;
KeSwitchContext(cur, next);
KeSchedulerPostSwitch();
}
@@ -190,12 +218,14 @@ void KeSchedulerTick(void) {
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
cur->SchedLockFlags = flags; // Issue 5 fix
gSchedStats.Ticks++;
cur->QuantaConsumed++;
if (cur != gIdleThread && cur->QuantaConsumed >= DEMOTION_QUANTA) {
cur->QuantaConsumed = 0;
if (cur->Priority > PriorityLow) {
cur->Priority = (THREAD_PRIORITY)(cur->Priority - 1);
gSchedStats.Demotions++;
}
}
@@ -208,6 +238,7 @@ void KeSchedulerTick(void) {
LIST_ENTRY* node = ListPopHead(&gRunQueue[p]);
THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink);
t->Priority = PriorityHigh;
gSchedStats.AgingPromotions++;
ListInsertTail(&gRunQueue[PriorityHigh], &t->SchedulerLink);
}
}
+45 -34
View File
@@ -1,34 +1,45 @@
#pragma once
// ke_sched.h - Multi-Level Feedback Queue scheduler
// Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick.
#include <ke_thread.h>
#define QUANTUM_MS 10 // base time slice in milliseconds
// Called once by KernelMain before any threads are created.
void KeInitializeScheduler(void);
// Add / remove a thread from the run queues.
void KeReadyThread(THREAD* thread);
void KeUnreadyThread(THREAD* thread);
// Called from the timer ISR every QUANTUM_MS. This only performs timer
// accounting and requests a reschedule; the actual context switch happens from
// KiExitInterrupt after the IRQ handler has returned and EOI has been sent.
void KeSchedulerTick(void);
// Internal scheduler hooks used by the interrupt-exit path. KiRequestReschedule
// is cheap and can be called from interrupt context. KiScheduleFromInterrupt
// consumes the pending request and performs the context switch, if needed.
void KiRequestReschedule(void);
int KiIsReschedulePending(void);
void KiScheduleFromInterrupt(void);
// Voluntarily yield the CPU. Picks the next runnable thread immediately.
__declspec(dllexport) void KeYield(void);
// Returns the thread currently running on this CPU (reads GS:[0]).
THREAD* KeGetCurrentThread(void);
// Set the current-thread GS slot. Called once per CPU at boot.
void KeSetCurrentThread(THREAD* thread);
#pragma once
// ke_sched.h - Multi-Level Feedback Queue scheduler
// Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick.
#include <ke_thread.h>
#define QUANTUM_MS 10 // base time slice in milliseconds
typedef struct KE_SCHED_STATS {
uint64_t Ticks;
uint64_t ContextSwitches;
uint64_t KeepCurrentDecisions;
uint64_t IdleSelections;
uint64_t Demotions;
uint64_t AgingPromotions;
} KE_SCHED_STATS;
// Called once by KernelMain before any threads are created.
void KeInitializeScheduler(void);
// Add / remove a thread from the run queues.
void KeReadyThread(THREAD* thread);
void KeUnreadyThread(THREAD* thread);
// Called from the timer ISR every QUANTUM_MS. This only performs timer
// accounting and requests a reschedule; the actual context switch happens from
// KiExitInterrupt after the IRQ handler has returned and EOI has been sent.
void KeSchedulerTick(void);
// Internal scheduler hooks used by the interrupt-exit path. KiRequestReschedule
// is cheap and can be called from interrupt context. KiScheduleFromInterrupt
// consumes the pending request and performs the context switch, if needed.
void KiRequestReschedule(void);
int KiIsReschedulePending(void);
void KiScheduleFromInterrupt(void);
// Voluntarily yield the CPU. Picks the next runnable thread immediately.
__declspec(dllexport) void KeYield(void);
void KeGetSchedulerStats(KE_SCHED_STATS* out);
// Returns the thread currently running on this CPU (reads GS:[0]).
THREAD* KeGetCurrentThread(void);
// Set the current-thread GS slot. Called once per CPU at boot.
void KeSetCurrentThread(THREAD* thread);
+42 -4
View File
@@ -476,6 +476,43 @@ static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) {
}
// -------------------------------------------------------------------------
// String-view ABI self-test
// -------------------------------------------------------------------------
static void KiRunStringViewSelfTest(void) {
DbgPrint("STRINGVIEW selftest: begin\n");
SYS_STRING_VIEW hello = SYS_SV_LITERAL("Hello");
if (hello.Length != 5 || !SysSvEqualLit(hello, "Hello"))
KePanic("STRINGVIEW selftest: literal/equality failed\n");
static const char embedded[] = { 'a', 'b', '\0', 'c', 'd' };
SYS_STRING_VIEW emb = SysSvFromParts(embedded, sizeof(embedded));
if (emb.Length != 5 || !SysSvContainsByte(emb, '\0'))
KePanic("STRINGVIEW selftest: embedded-NUL detection failed\n");
char copy[6];
if (!SysSvCopyZ(copy, sizeof(copy), emb))
KePanic("STRINGVIEW selftest: bounded copy failed\n");
if (memcmp(copy, embedded, sizeof(embedded)) != 0 || copy[5] != '\0')
KePanic("STRINGVIEW selftest: bounded copy contents wrong\n");
if (SysSvCopyZ(copy, 4, emb))
KePanic("STRINGVIEW selftest: undersized copy unexpectedly succeeded\n");
// Paths are string views at the ABI boundary, but FAT/VFS path names do not
// accept embedded NUL bytes. This must fail before reaching any filesystem.
FILE_OBJECT* f = (FILE_OBJECT*)0;
KSTATUS s = IoOpenFile(SysSvFromParts("system/init.exe\0junk", 20), &f);
if (s != STATUS_INVALID_PARAMETER)
KePanic("STRINGVIEW selftest: embedded-NUL path status=%u\n", s);
DbgPrint("STRINGVIEW selftest: sample view='%v' len=%llu\n",
hello, (uint64_t)hello.Length);
DbgPrint("STRINGVIEW selftest: PASS\n");
}
// -------------------------------------------------------------------------
// Async file I/O self-test
// -------------------------------------------------------------------------
@@ -534,7 +571,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
memset(&st, 0, sizeof(st));
st.Iosb.Status = STATUS_FAILURE;
KSTATUS s = IoOpenFile("system/init.exe", &st.File);
KSTATUS s = IoOpenFile(SYS_SV_LITERAL("system/init.exe"), &st.File);
if (!KSUCCESS(s)) {
DbgPrint("FILE/ASYNC selftest: SKIP, system/init.exe open failed %u\n", s);
return;
@@ -614,7 +651,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
wiosb.Status = STATUS_FAILURE;
wiosb.Information = 0;
s = IoCreateFile("asyncio.tmp", &wf);
s = IoCreateFile(SYS_SV_LITERAL("asyncio.tmp"), &wf);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: create write temp failed %u\n", s);
@@ -651,7 +688,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
IoCloseFile(wf);
wf = (FILE_OBJECT*)0;
s = IoOpenFile("asyncio.tmp", &rf);
s = IoOpenFile(SYS_SV_LITERAL("asyncio.tmp"), &rf);
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: reopen write temp failed %u\n", s);
@@ -664,7 +701,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
IoCloseFile(rf);
ObDereferenceObject(&wev->Header);
s = IoDeleteFile("asyncio.tmp");
s = IoDeleteFile(SYS_SV_LITERAL("asyncio.tmp"));
if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: delete temp failed %u\n", s);
@@ -695,6 +732,7 @@ static void KiRunTrapFrameSelfTest(void) {
void KiRunKernelSelfTests(const HAL_API* hal) {
DbgPrint("Kernel selftests: begin\n");
KiRunKernelApcSelfTest();
KiRunStringViewSelfTest();
KiRunIrpKernelApcSelfTest(hal);
KiRunAsyncFileIoSelfTest();
KiRunTrapFrameSelfTest();
+1 -1
View File
@@ -54,7 +54,7 @@ __declspec(dllexport) KSTATUS KeGetSettingString(const char* section,
buffer[0] = '\0';
FILE_OBJECT* file = NULL;
KSTATUS st = IoOpenFile(KE_SETTINGS_PATH, &file);
KSTATUS st = IoOpenFile(SYS_SV_LITERAL(KE_SETTINGS_PATH), &file);
if (!KSUCCESS(st))
return st;
+356 -195
View File
File diff suppressed because it is too large. Load diff
+18 -7
View File
@@ -40,11 +40,20 @@ static void KiThreadDestroy(OBJECT* obj) {
}
if (thread->Owner) {
uintptr_t flags = KeAcquireSpinlock(&thread->Owner->HandleLock);
PROCESS* owner = thread->Owner;
uint32_t remaining = 0;
uintptr_t flags = KeAcquireSpinlock(&owner->HandleLock);
ListRemove(&thread->ProcessLink);
thread->Owner->ThreadCount--;
KeReleaseSpinlock(&thread->Owner->HandleLock, flags);
ObDereferenceObject(&thread->Owner->Header);
if (owner->ThreadCount) owner->ThreadCount--;
remaining = owner->ThreadCount;
KeReleaseSpinlock(&owner->HandleLock, flags);
if (remaining == 0 && owner->Terminating &&
_InterlockedExchange(&owner->Exited, 1) == 0) {
if (owner->ExitEvent) KeSignalEvent(owner->ExitEvent);
}
ObDereferenceObject(&owner->Header);
thread->Owner = (PROCESS *)0;
}
@@ -104,9 +113,11 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
return STATUS_NO_MEMORY;
}
thread->UserStackBase = ustack;
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8; // i don't know why, i don't want to know why
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness
// this magic number will hunt me in my dreams
// Ring-3 starts at the image entry via IRET, not CALL, so there is no
// pushed return address. The x64 ABI expects RSP % 16 == 8 at function
// entry; bias the initial stack as if a call had happened so compiler
// generated aligned SSE spills (movaps) are safe.
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8;
SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
if (!teb) {
+14 -8
View File
@@ -4,25 +4,24 @@
#include <stdint.h>
#include <stddef.h>
#include <sys_string.h>
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
#define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 520u
#ifndef SYS_USER_ENV_TYPES_DEFINED
#define SYS_USER_ENV_TYPES_DEFINED 1
typedef struct SYS_PEB {
uint32_t Magic;
uint32_t Version;
uint32_t ProcessId;
const char* ImagePath;
size_t ImagePathLength;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
SYS_STRING_VIEW ImagePath;
SYS_STRING_VIEW CommandLine;
SYS_STRING_VIEW CurrentDirectory;
} SYS_PEB;
typedef struct SYS_TEB {
@@ -32,4 +31,11 @@ typedef struct SYS_TEB {
struct SYS_TEB* Self;
uint32_t ThreadId;
uint32_t Magic;
// Native per-thread extension pointer. The kernel leaves this NULL;
// user-mode personalities/libraries may hang their own thread-local
// state here without baking subsystem semantics into SYS_TEB.
void* SubsystemData;
} SYS_TEB;
#endif // SYS_USER_ENV_TYPES_DEFINED
+9 -6
View File
@@ -188,7 +188,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
KSTATUS s;
FILE_OBJECT* f;
s = IoOpenFile("drivers\\drivers.ini", &f);
s = IoOpenFile(SYS_SV_LITERAL("drivers\\drivers.ini"), &f);
if (s != STATUS_SUCCESS) {
KePanic("KiInitializeDrivers: Failed to open drivers.ini (status %d)\n", s);
}
@@ -222,7 +222,7 @@ static void KiInitializeDrivers(BOOT_INFO* bootInfo, const HAL_API* hal) {
memcpy(path, "drivers\\", 8);
memcpy(path + 8, runtimeKeys[i], len + 1);
s = IoOpenFile(path, &f);
s = IoOpenFile(SysSvFromCstr(path), &f);
if (s != STATUS_SUCCESS) {
DbgPrint("KiInitializeDrivers: Failed to open runtime driver: %s (%s)\n", runtimeKeys[i], path);
continue; // Skip this driver but keep going with others
@@ -285,7 +285,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
FILE_OBJECT* f;
// Read the executable file into a raw buffer.
s = IoOpenFile(filePath, &f);
s = IoOpenFile(SysSvFromCstr(filePath), &f);
DbgPrint("KiLaunchUserProcess: IoOpenFile: %d\n", s);
if (s != STATUS_SUCCESS) {
DbgPrint("Failed to load file: \"%s\"!\n", filePath);
@@ -323,7 +323,10 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
}
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, (args && args[0]) ? args : filePath);
s = KeSetupUserProcessEnvironment(proc, filePath, args, proc->CurrentDirectory);
s = KeSetupUserProcessEnvironment(proc,
SysSvFromCstr(filePath),
SysSvFromCstr(args),
SysSvFromCstr(proc->CurrentDirectory));
if (!KSUCCESS(s)) {
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
MmFreePool(mem);
@@ -344,7 +347,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
s = LdrResolveImportsForProcess(proc, imageBase);
if (!KSUCCESS(s)) {
DbgPrint("KiLaunchUserProcess: Failed to resolve EXE imports for %s (status %d)\n", filePath, s);
MmFreeUserMemory((void*)imageBase);
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header);
return 0;
}
@@ -361,7 +364,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
DbgPrint("KiLaunchUserProcess: Failed to create thread. Status: %d\n", s);
proc->ImageBase = 0;
proc->ImageSize = 0;
MmFreeUserMemory((void*)imageBase);
MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header);
return 0;
}
+52 -6
View File
@@ -107,6 +107,10 @@ typedef struct {
#pragma pack(pop)
#define IMAGE_REL_BASED_DIR64 10
#define IMAGE_SCN_CNT_CODE 0x00000020u
#define IMAGE_SCN_MEM_EXECUTE 0x20000000u
#define IMAGE_SCN_MEM_READ 0x40000000u
#define IMAGE_SCN_MEM_WRITE 0x80000000u
// ---------------------------------------------------------------------------
// Module registry
@@ -248,6 +252,40 @@ size_t LdrGetImageSize(uintptr_t base) {
return (size_t)pe->OptionalHeader.SizeOfImage;
}
static void KiFreeLoadedImage(uintptr_t imageBase, bool user) {
if (!imageBase) return;
size_t imageSize = LdrGetImageSize(imageBase);
if (user) MmFreeUserImage((void*)imageBase, imageSize);
else MmFreePool((void*)imageBase);
}
KSTATUS LdrProtectImage(uintptr_t imageBase, bool user) {
if (!imageBase) return STATUS_INVALID_PARAMETER;
uint32_t peOffset = *(uint32_t *)(imageBase + 0x3C);
PE_HEADER* pe = (PE_HEADER *)(imageBase + peOffset);
if (pe->Signature != PE_SIGNATURE) return STATUS_FAILURE;
SECTION_HEADER* sec = (SECTION_HEADER *)((uint8_t *)&pe->OptionalHeader + pe->SizeOfOptionalHeader);
for (int i = 0; i < pe->NumberOfSections; i++) {
size_t protectSize = sec[i].VirtualSize ? sec[i].VirtualSize : sec[i].SizeOfRawData;
if (!protectSize) continue;
uint64_t prot = PAGE_PRESENT | (user ? PAGE_USER : 0);
if (sec[i].Characteristics & IMAGE_SCN_MEM_WRITE)
prot |= PAGE_WRITE;
if (!((sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
(sec[i].Characteristics & IMAGE_SCN_CNT_CODE)))
prot |= PAGE_NX;
KSTATUS s = MmSetPageProtect(imageBase + sec[i].VirtualAddress, protectSize, prot);
if (!KSUCCESS(s)) return s;
}
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// Entry point lookup
// ---------------------------------------------------------------------------
@@ -470,13 +508,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
}
FILE_OBJECT* f;
KSTATUS s = IoOpenFile(filename, &f);
KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f);
if (!KSUCCESS(s)) {
char path[260];
memcpy(path, "0:/system/", 10);
memcpy(path + 10, filename, strnlen(filename, 248) + 1);
path[259] = '\0';
s = IoOpenFile(path, &f);
s = IoOpenFile(SysSvFromCstr(path), &f);
if (!KSUCCESS(s)) {
DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s);
return s;
@@ -509,7 +547,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
s = LdrResolveImportsForProcess(process, imageBase);
if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase);
KiFreeLoadedImage(imageBase, true);
return s;
}
s = LdrProtectImage(imageBase, true);
if (!KSUCCESS(s)) {
KiFreeLoadedImage(imageBase, true);
return s;
}
@@ -517,7 +561,7 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
s = LdrRegisterProcessModule(process, filename, imageBase);
if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase);
KiFreeLoadedImage(imageBase, true);
return s;
}
@@ -561,7 +605,7 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
OPT_HEADER64* opt = &pe->OptionalHeader;
size_t imageSize = opt->SizeOfImage;
void* mem = user ? MmAllocateUserMemory(imageSize) : MmAllocatePool(imageSize);
void* mem = user ? MmAllocateUserImage(imageSize) : MmAllocatePool(imageSize);
if (!mem) return STATUS_NO_MEMORY;
memset(mem, 0, imageSize);
@@ -575,7 +619,9 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
}
uint64_t prot = PAGE_PRESENT | PAGE_WRITE | (user ? PAGE_USER : 0);
MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress, sec[i].VirtualSize, prot);
MmSetPageProtect((uintptr_t)mem + sec[i].VirtualAddress,
sec[i].VirtualSize ? sec[i].VirtualSize : sec[i].SizeOfRawData,
prot);
}
uintptr_t delta = (uintptr_t)mem - opt->ImageBase;
+41 -38
View File
@@ -1,38 +1,41 @@
#pragma once
// ldr_image.h - minimal PE32+ loader
// Loads an already-mapped PE image and resolves its imports.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <ke_status.h>
// Map a PE32+ image from 'buffer' into the kernel virtual address space.
// On success, *baseOut receives the virtual base address.
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user);
// Return the SizeOfImage recorded in the PE header for an already-mapped image.
size_t LdrGetImageSize(uintptr_t base);
// Look up a named export in a loaded module.
// 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll").
void* LdrGetExport(const char* moduleName, const char* functionName);
// Get the entry point address of a loaded image.
uintptr_t LdrGetEntryPoint(uintptr_t base);
// Resolve the import table of an already-loaded image using the global registry.
KSTATUS LdrResolveImports(uintptr_t imageBase);
// Resolve imports using a process-local module registry.
struct PROCESS;
KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase);
// Load a DLL for a specific process, resolve it against that process's
// loaded modules, and register it in the process-local registry.
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
// Called by KernelMain to register all modules from BOOT_INFO so that
// LdrGetExport can find them.
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
void LdrRegisterModule(const char* name, uintptr_t base);
#pragma once
// ldr_image.h - minimal PE32+ loader
// Loads an already-mapped PE image and resolves its imports.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <ke_status.h>
// Map a PE32+ image from 'buffer' into the kernel virtual address space.
// On success, *baseOut receives the virtual base address.
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user);
// Return the SizeOfImage recorded in the PE header for an already-mapped image.
size_t LdrGetImageSize(uintptr_t base);
// Look up a named export in a loaded module.
// 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll").
void* LdrGetExport(const char* moduleName, const char* functionName);
// Get the entry point address of a loaded image.
uintptr_t LdrGetEntryPoint(uintptr_t base);
// Resolve the import table of an already-loaded image using the global registry.
KSTATUS LdrResolveImports(uintptr_t imageBase);
// Resolve imports using a process-local module registry.
struct PROCESS;
KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase);
// Apply final page permissions for a loaded image after relocations/imports.
KSTATUS LdrProtectImage(uintptr_t imageBase, bool user);
// Load a DLL for a specific process, resolve it against that process's
// loaded modules, and register it in the process-local registry.
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
// Called by KernelMain to register all modules from BOOT_INFO so that
// LdrGetExport can find them.
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
void LdrRegisterModule(const char* name, uintptr_t base);
+65 -24
View File
@@ -290,29 +290,6 @@ __declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, s
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS CopyStringFromUser(char* kernelDst,
size_t kernelDstSize,
const char* userSrc) {
if (!kernelDst || kernelDstSize == 0 || !userSrc)
return STATUS_INVALID_PARAMETER;
for (size_t i = 0; i < kernelDstSize; i++) {
KSTATUS s = MmProbeUserRead(userSrc + i, 1);
if (!KSUCCESS(s)) {
kernelDst[0] = '\0';
return s;
}
char ch = userSrc[i];
kernelDst[i] = ch;
if (ch == '\0')
return STATUS_SUCCESS;
}
kernelDst[kernelDstSize - 1] = '\0';
return STATUS_BUFFER_TOO_SMALL;
}
// ---------------------------------------------------------------------------
// Boundary-tag allocator (used for both pool and user heap)
// ---------------------------------------------------------------------------
@@ -595,6 +572,9 @@ static HEAP gPoolHeap;
// User heap
// ---------------------------------------------------------------------------
static HEAP gUserHeap;
__declspec(dllexport) void MmFreeUserImage(void* ptr, size_t size);
static SPINLOCK gUserImageLock = SPINLOCK_INIT;
static uintptr_t gUserImageNext = USER_LIMIT;
// ---------------------------------------------------------------------------
// Module init
@@ -621,8 +601,15 @@ void MmInitializeVirtual(PHYSICAL_ADDRESS bootPml4Phys, size_t poolSize) {
gUserHeap.Start = USER_BASE;
gUserHeap.Brk = USER_BASE;
gUserHeap.Limit = USER_LIMIT;
gUserHeap.MapFlags = PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
gUserHeap.MapFlags = PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX;
gUserHeap.Lock = (SPINLOCK)SPINLOCK_INIT;
// Executable image mappings grow downward from the top of user space.
// The regular user heap grows upward from USER_BASE. Keeping them in
// separate page-aligned regions prevents W^X section protection from
// changing heap metadata or adjacent allocations.
gUserImageLock = (SPINLOCK)SPINLOCK_INIT;
gUserImageNext = USER_LIMIT;
}
void MmActivate(void) {
@@ -654,6 +641,60 @@ __declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize) {
return KiHeapRealloc(&gUserHeap, ptr, newSize);
}
__declspec(dllexport) void* MmAllocateUserImage(size_t size) {
KeAssertIrql(PASSIVE_LEVEL);
if (!size) return (void*)0;
size_t pagesSize = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
if (pagesSize < size) return (void*)0;
uintptr_t flags = KeAcquireSpinlock(&gUserImageLock);
if (pagesSize > gUserImageNext || gUserImageNext - pagesSize < gUserHeap.Brk) {
KeReleaseSpinlock(&gUserImageLock, flags);
return (void*)0;
}
uintptr_t base = gUserImageNext - pagesSize;
gUserImageNext = base;
KeReleaseSpinlock(&gUserImageLock, flags);
for (uintptr_t va = base; va < base + pagesSize; va += PAGE_SIZE) {
PHYSICAL_ADDRESS frame = MmAllocateFrame();
if (!frame) {
MmFreeUserImage((void*)base, va - base);
return (void*)0;
}
KSTATUS s = MmMapPage(va, frame, PAGE_PRESENT | PAGE_WRITE | PAGE_USER | PAGE_NX);
if (!KSUCCESS(s)) {
MmFreeFrame(frame);
MmFreeUserImage((void*)base, va - base);
return (void*)0;
}
}
memset((void*)base, 0, pagesSize);
return (void*)base;
}
__declspec(dllexport) void MmFreeUserImage(void* ptr, size_t size) {
KeAssertIrql(PASSIVE_LEVEL);
if (!ptr || !size) return;
uintptr_t base = (uintptr_t)ptr & PAGE_MASK;
size_t pagesSize = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
if (pagesSize < size) return;
for (uintptr_t va = base; va < base + pagesSize; va += PAGE_SIZE) {
PHYSICAL_ADDRESS phys = MmGetPhysicalAddress(va);
if (!phys) continue;
if (KSUCCESS(MmUnmapPage(va))) {
MmFreeFrame(phys);
}
}
}
size_t MmGetKernelPoolUsage(void) {
// Returns bytes committed to the kernel pool
return (size_t)(gPoolHeap.Brk - gPoolHeap.Start);
+7 -6
View File
@@ -73,11 +73,6 @@ __declspec(dllexport) KSTATUS MmProbeUserWrite(void* ptr, size_t size);
__declspec(dllexport) KSTATUS CopyFromUser(void* kernelDst, const void* userSrc, size_t size);
__declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, size_t size);
// Bounded NUL-terminated string import from user memory. Always NUL-terminates
// kernelDst when kernelDstSize is non-zero. Returns STATUS_BUFFER_TOO_SMALL if
// no NUL byte was found within kernelDstSize bytes.
__declspec(dllexport) KSTATUS CopyStringFromUser(char* kernelDst, size_t kernelDstSize, const char* userSrc);
// ---------------------------------------------------------------------------
// Kernel pool - ring 0 only, never PAGE_USER.
// Best-fit free-list allocator, thread-safe via spinlock.
@@ -88,11 +83,17 @@ __declspec(dllexport) void* MmReallocatePool(void* ptr, size_t newSize);
// ---------------------------------------------------------------------------
// User heap - shared flat space, ring 3 accessible.
// All allocations are PAGE_PRESENT | PAGE_WRITE | PAGE_USER.
// Allocations are writable user pages with NX set by default; PE image
// sections opt back into execution through the loader.
// ---------------------------------------------------------------------------
__declspec(dllexport) void* MmAllocateUserMemory(size_t size);
__declspec(dllexport) void MmFreeUserMemory(void* ptr);
__declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize);
// Page-aligned user image mapping. PE section protection is page based, so
// executable images must not be allocated from the normal sub-page heap.
__declspec(dllexport) void* MmAllocateUserImage(size_t size);
__declspec(dllexport) void MmFreeUserImage(void* ptr, size_t size);
size_t MmGetKernelPoolUsage(void);
size_t MmGetUserHeapUsage(void);
+5 -5
View File
@@ -12,11 +12,12 @@
#define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES)
static void print(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
}
static void print_char(char c) {
SysWriteConsole(&c, 1);
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
@@ -126,8 +127,7 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
SYS_RESOLVE_HOST_REQUEST req;
memset(&resolved, 0, sizeof(resolved));
memset(&req, 0, sizeof(req));
req.Name = host;
req.NameLength = strlen(host);
req.Name = SysSvFromCstr(host);
req.Family = SYS_AF_INET;
req.AddressOut = &resolved;
req.AddressSizeInOut = &resolvedSize;
@@ -218,7 +218,7 @@ int main(void) {
uint32_t count = PING_DEFAULT_COUNT;
strcpy(host, PING_DEFAULT_TARGET);
const char* cmd = SysGetPeb()->CommandLine;
const char* cmd = SysGetPeb()->CommandLine.Data;
cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) {
const char* rest = next_token(cmd, tok, sizeof(tok));
+8 -3
View File
@@ -18,7 +18,8 @@ typedef struct {
__declspec(dllexport) int chdir(const char* path) {
char native[512];
PosixTranslatePath(path, native, sizeof(native));
uint32_t s = SysSetCwd(native);
SYS_PATH_REQUEST req = { SysSvFromCstr(native) };
uint32_t s = SysSetCwd(&req);
if (s != 0) { errno = ENOENT; return -1; }
return 0;
}
@@ -26,7 +27,10 @@ __declspec(dllexport) int chdir(const char* path) {
__declspec(dllexport) char* getcwd(char* buf, size_t size) {
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
char native[512];
uint32_t s = SysGetCwd(native, sizeof(native));
size_t nativeLen = 0;
SYS_GET_CWD_REQUEST req = { native, sizeof(native) - 1, &nativeLen };
uint32_t s = SysGetCwd(&req);
if (s == 0) native[nativeLen] = '\0';
if (s != 0) { errno = ERANGE; return (char*)0; }
NativeToPosix(native, buf, size);
return buf;
@@ -36,7 +40,8 @@ __declspec(dllexport) DIR* opendir(const char* path) {
char native[512];
PosixTranslatePath(path, native, sizeof(native));
uint32_t kh;
uint32_t s = SysOpenDir(native, &kh);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(native), &kh };
uint32_t s = SysOpenDir(&req);
if (s != 0) { errno = ENOENT; return (DIR*)0; }
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
+1 -1
View File
@@ -1,7 +1,7 @@
// posix_fd.c - file descriptor table
#include <syscall.h>
#include "posix.h"
#include "posix_fd.h"
#include <syscall.h>
int errno = 0;
+9 -5
View File
@@ -33,11 +33,12 @@ __declspec(dllexport) int open(const char* path, int flags, ...) {
}
uint32_t kh = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(p), &kh };
uint32_t s;
if (flags & O_WRONLY) {
s = SysCreateFile(p, &kh);
s = SysCreateFile(&req);
} else {
s = SysOpenFile(p, &kh);
s = SysOpenFile(&req);
}
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
@@ -98,9 +99,11 @@ __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
if (!e) return -1;
switch (e->Type) {
case FD_CONSOLE_W:
SysWriteConsole((const char*)buf, count);
case FD_CONSOLE_W: {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts((const char*)buf, count) };
SysWriteConsole(&req);
return (ssize_t)count;
}
case FD_DEVNULL:
case FD_DEVZERO:
return (ssize_t)count;
@@ -143,7 +146,8 @@ __declspec(dllexport) int unlink(const char* path) {
return -1;
}
uint32_t s = SysDeleteFile(p);
SYS_PATH_REQUEST req = { SysSvFromCstr(p) };
uint32_t s = SysDeleteFile(&req);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
return 0;
}
+4 -3
View File
@@ -15,8 +15,8 @@ static SYS_PEB* PosixPeb(void) {
__declspec(dllexport) const char* posix_get_command_line(void) {
SYS_PEB* peb = PosixPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return peb->CommandLine;
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
return peb->CommandLine.Data;
}
// pid -> proc handle table (user-mode side, for waitpid)
@@ -104,7 +104,8 @@ __declspec(dllexport) int posix_spawn(pid_t* pidOut,
}
uint32_t procHandle;
uint32_t s = SysCreateProcess(native, &procHandle, cmdline);
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(native), SysSvFromCstr(cmdline), &procHandle };
uint32_t s = SysCreateProcess(&req);
if (s != 0) { errno = ENOENT; return -1; }
uint32_t kpid = 0;
+1 -2
View File
@@ -392,8 +392,7 @@ static int ResolveNameIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4* out) {
size_t size = sizeof(*out);
SYS_RESOLVE_HOST_REQUEST req;
memset(&req, 0, sizeof(req));
req.Name = name;
req.NameLength = strlen(name);
req.Name = SysSvFromCstr(name);
req.Family = SYS_AF_INET;
req.AddressOut = out;
req.AddressSizeInOut = &size;
+952 -920
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -4,11 +4,12 @@
#include <syscall.h>
static void print(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
}
static void print_char(char c) {
SysWriteConsole(&c, 1);
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
@@ -43,7 +44,7 @@ static void print_ipv4(const uint8_t ip[4]) {
int main(void) {
char name[256];
const char* cmd = SysGetPeb()->CommandLine;
const char* cmd = SysGetPeb()->CommandLine.Data;
cmd = next_token(cmd, name, sizeof(name)); // program path
(void)cmd;
@@ -60,8 +61,7 @@ int main(void) {
SYS_RESOLVE_HOST_REQUEST req;
memset(&addr, 0, sizeof(addr));
memset(&req, 0, sizeof(req));
req.Name = name;
req.NameLength = strlen(name);
req.Name = SysSvFromCstr(name);
req.Family = SYS_AF_INET;
req.AddressOut = &addr;
req.AddressSizeInOut = &addrSize;
+38 -8
View File
@@ -10,7 +10,37 @@
// ---------------------------------------------------------------------------
// Output helpers
// ---------------------------------------------------------------------------
static void print(const char* s) { SysWriteConsole(s, strlen(s)); }
static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
}
static uint32_t create_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysCreateFile(&req);
}
static uint32_t open_dir(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenDir(&req);
}
static uint32_t set_cwd(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysSetCwd(&req);
}
static uint32_t delete_file(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysDeleteFile(&req);
}
static uint32_t create_process(const char* path, uint32_t* handleOut, const char* cmdline) {
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(cmdline), handleOut };
return SysCreateProcess(&req);
}
static void printUInt(uint32_t n) {
char buf[12]; int i = 0;
@@ -101,7 +131,7 @@ static void cmd_write(const char* args) {
filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
uint32_t hFile;
if (SysCreateFile(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
if (create_file(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
size_t written;
SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written);
SysCloseHandle(hFile);
@@ -112,7 +142,7 @@ static void cmd_dir(const char* path) {
uint32_t hDir;
const char* target = (path && *path != '\0') ? path : ".";
if (SysOpenDir(target, &hDir) != 0) {
if (open_dir(target, &hDir) != 0) {
print("dir: failed to open directory\n");
return;
}
@@ -142,7 +172,7 @@ static void cmd_dir(const char* path) {
static void cmd_type(const char* filename) {
uint32_t hFile;
if (SysOpenFile(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
if (open_file(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
char buffer[512]; size_t bytesRead;
while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) {
buffer[bytesRead] = '\0'; print(buffer);
@@ -183,8 +213,8 @@ static int runCommand(const char* cmd) {
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { if (SysSetCwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); return 0; }
if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
@@ -192,7 +222,7 @@ static int runCommand(const char* cmd) {
if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
uint32_t hProc;
if (SysCreateProcess(program, &hProc, cmd) == 0) {
if (create_process(program, &hProc, cmd) == 0) {
SysWaitForProcess(hProc);
SysCloseHandle(hProc);
while (SysKbPoll()) (void)SysKbReadChar();
@@ -201,7 +231,7 @@ static int runCommand(const char* cmd) {
}
int main(void) {
const char* cmdline = SysGetPeb()->CommandLine;
const char* cmdline = SysGetPeb()->CommandLine.Data;
char lineBuf[MAX_CMD];
print("shell: command line: ");
+1 -1
View File
@@ -165,7 +165,7 @@ static int runCommand(const char* cmd) {
int main(void) {
PosixFdInit();
const char* cmdline = SysGetPeb()->CommandLine;
const char* cmdline = SysGetPeb()->CommandLine.Data;
char lineBuf[MAX_CMD];
print("shell: command line: ");
+112
View File
@@ -0,0 +1,112 @@
#pragma once
// sys_string.h - shared native string-view ABI.
// A SYS_STRING_VIEW is a borrowed byte span. It is not NUL-terminated by
// contract and it does not own the pointed-to storage.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifndef SYS_STRING_VIEW_DEFINED
#define SYS_STRING_VIEW_DEFINED 1
typedef struct SYS_STRING_VIEW {
const char* Data;
size_t Length;
} SYS_STRING_VIEW;
#endif
#define SYS_SV_LITERAL(s) ((SYS_STRING_VIEW){ (s), sizeof(s) - 1 })
#define SYS_SV_NULL ((SYS_STRING_VIEW){ (const char*)0, 0 })
static inline SYS_STRING_VIEW SysSvFromParts(const char* data, size_t length) {
SYS_STRING_VIEW v;
v.Data = data;
v.Length = length;
return v;
}
static inline SYS_STRING_VIEW SysSvFromCstr(const char* s) {
SYS_STRING_VIEW v;
v.Data = s;
v.Length = s ? strlen(s) : 0;
return v;
}
static inline int SysSvIsEmpty(SYS_STRING_VIEW s) {
return s.Length == 0;
}
static inline int SysSvIsValid(SYS_STRING_VIEW s) {
return s.Data != (const char*)0 || s.Length == 0;
}
static inline int SysSvEqual(SYS_STRING_VIEW a, SYS_STRING_VIEW b) {
if (a.Length != b.Length) return 0;
if (a.Length == 0) return 1;
if (!a.Data || !b.Data) return 0;
return memcmp(a.Data, b.Data, a.Length) == 0;
}
static inline char SysAsciiLower(char c) {
return (c >= 'A' && c <= 'Z') ? (char)(c + ('a' - 'A')) : c;
}
static inline int SysSvEqualAsciiI(SYS_STRING_VIEW a, SYS_STRING_VIEW b) {
if (a.Length != b.Length) return 0;
if (a.Length == 0) return 1;
if (!a.Data || !b.Data) return 0;
for (size_t i = 0; i < a.Length; i++) {
if (SysAsciiLower(a.Data[i]) != SysAsciiLower(b.Data[i])) return 0;
}
return 1;
}
static inline int SysSvEqualLit(SYS_STRING_VIEW a, const char* lit) {
return SysSvEqual(a, SysSvFromCstr(lit));
}
static inline int SysSvEqualLitAsciiI(SYS_STRING_VIEW a, const char* lit) {
return SysSvEqualAsciiI(a, SysSvFromCstr(lit));
}
static inline int SysSvContainsByte(SYS_STRING_VIEW s, char ch) {
if (!s.Data && s.Length) return 0;
for (size_t i = 0; i < s.Length; i++) {
if (s.Data[i] == ch) return 1;
}
return 0;
}
static inline SYS_STRING_VIEW SysSvSlice(SYS_STRING_VIEW s, size_t off, size_t len) {
if (off > s.Length) off = s.Length;
if (len > s.Length - off) len = s.Length - off;
return SysSvFromParts(s.Data ? s.Data + off : (const char*)0, len);
}
static inline SYS_STRING_VIEW SysSvTrimAscii(SYS_STRING_VIEW s) {
size_t begin = 0;
size_t end = s.Length;
while (begin < end) {
char c = s.Data[begin];
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break;
begin++;
}
while (end > begin) {
char c = s.Data[end - 1];
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break;
end--;
}
return SysSvSlice(s, begin, end - begin);
}
static inline int SysSvCopyZ(char* dst, size_t dstSize, SYS_STRING_VIEW src) {
if (!dst || dstSize == 0) return 0;
if (src.Length && !src.Data) return 0;
if (src.Length + 1 > dstSize) {
dst[0] = '\0';
return 0;
}
if (src.Length) memcpy(dst, src.Data, src.Length);
dst[src.Length] = '\0';
return 1;
}
+82 -25
View File
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <intrin.h>
#include "sys_string.h"
#define SYS_STATUS_SUCCESS 0u
#define SYS_STATUS_TIMEOUT 3u
@@ -25,19 +26,17 @@
#define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 520u
#ifndef SYS_USER_ENV_TYPES_DEFINED
#define SYS_USER_ENV_TYPES_DEFINED 1
typedef struct SYS_PEB {
uint32_t Magic;
uint32_t Version;
uint32_t ProcessId;
const char* ImagePath;
size_t ImagePathLength;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
SYS_STRING_VIEW ImagePath;
SYS_STRING_VIEW CommandLine;
SYS_STRING_VIEW CurrentDirectory;
} SYS_PEB;
typedef struct SYS_TEB {
@@ -47,8 +46,15 @@ typedef struct SYS_TEB {
struct SYS_TEB* Self;
uint32_t ThreadId;
uint32_t Magic;
// Native per-thread extension pointer. The kernel leaves this NULL;
// user-mode personalities/libraries may hang their own thread-local
// state here without baking subsystem semantics into SYS_TEB.
void* SubsystemData;
} SYS_TEB;
#endif // SYS_USER_ENV_TYPES_DEFINED
static inline SYS_PEB* SysGetPeb(void) {
return (SYS_PEB*)(uintptr_t)__readgsqword(0);
}
@@ -124,12 +130,64 @@ typedef struct SYS_TRAP_FRAME_INFO {
uint64_t R11;
} SYS_TRAP_FRAME_INFO;
// ---------------------------------------------------------------------------
// String-view based syscall request structures
// ---------------------------------------------------------------------------
typedef struct SYS_CREATE_PROCESS_REQUEST {
SYS_STRING_VIEW Path;
SYS_STRING_VIEW CommandLine;
uint32_t* HandleOut;
} SYS_CREATE_PROCESS_REQUEST;
typedef struct SYS_PATH_HANDLE_REQUEST {
SYS_STRING_VIEW Path;
uint32_t* HandleOut;
} SYS_PATH_HANDLE_REQUEST;
typedef struct SYS_PATH_REQUEST {
SYS_STRING_VIEW Path;
} SYS_PATH_REQUEST;
typedef struct SYS_RENAME_FILE_REQUEST {
SYS_STRING_VIEW OldPath;
SYS_STRING_VIEW NewPath;
} SYS_RENAME_FILE_REQUEST;
typedef struct SYS_GET_CWD_REQUEST {
char* Buffer;
size_t Capacity;
size_t* LengthOut;
} SYS_GET_CWD_REQUEST;
typedef struct SYS_WRITE_CONSOLE_REQUEST {
SYS_STRING_VIEW Text;
} SYS_WRITE_CONSOLE_REQUEST;
typedef struct SYS_DEBUG_PRINT_REQUEST {
SYS_STRING_VIEW Text;
} SYS_DEBUG_PRINT_REQUEST;
typedef struct SYS_PUBLISH_QUEUE_REQUEST {
SYS_STRING_VIEW Name;
uint32_t QueueHandle;
} SYS_PUBLISH_QUEUE_REQUEST;
typedef struct SYS_OPEN_NAMED_QUEUE_REQUEST {
SYS_STRING_VIEW Name;
uint32_t* HandleOut;
} SYS_OPEN_NAMED_QUEUE_REQUEST;
typedef struct SYS_NAMED_QUEUE_OWNER_REQUEST {
SYS_STRING_VIEW Name;
uint32_t* PidOut;
} SYS_NAMED_QUEUE_OWNER_REQUEST;
// ---------------------------------------------------------------------------
// Process & thread lifecycle (0-14)
// ---------------------------------------------------------------------------
void SysExitProcess(int exitCode);
void SysExitThread(int exitCode);
uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, const char* cmdline);
uint32_t SysCreateProcess(const SYS_CREATE_PROCESS_REQUEST* request);
uint32_t SysWaitForProcess(uint32_t handle);
uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
uint32_t SysKillProcess(uint32_t pid, int exitCode);
@@ -178,16 +236,16 @@ uint32_t SysEventWait(uint32_t handle, uint32_t alertable);
uint32_t SysQueueCreate(uint32_t* handleOut);
uint32_t SysQueueSend(uint32_t handle, const void* msg, bool blocking);
uint32_t SysQueueRecv(uint32_t handle, void* msg, bool blocking);
uint32_t SysPublishQueue(const char* name, uint32_t handle);
uint32_t SysOpenNamedQueue(const char* name, uint32_t* handleOut);
uint32_t SysPublishQueue(const SYS_PUBLISH_QUEUE_REQUEST* request);
uint32_t SysOpenNamedQueue(const SYS_OPEN_NAMED_QUEUE_REQUEST* request);
uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut);
uint32_t SysNamedQueueOwnerPid(const char* name, uint32_t* pidOut);
uint32_t SysNamedQueueOwnerPid(const SYS_NAMED_QUEUE_OWNER_REQUEST* request);
// ---------------------------------------------------------------------------
// Files, directories, and current directory (39-56)
// ---------------------------------------------------------------------------
uint32_t SysOpenFile(const char* path, uint32_t* handleOut);
uint32_t SysCreateFile(const char* path, uint32_t* handleOut);
uint32_t SysOpenFile(const SYS_PATH_HANDLE_REQUEST* request);
uint32_t SysCreateFile(const SYS_PATH_HANDLE_REQUEST* request);
uint32_t SysReadFile(uint32_t handle, void* buf, size_t size, size_t* bytesRead);
uint32_t SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* written);
uint32_t SysReadFileAsync(const SYS_ASYNC_FILE_REQUEST* request);
@@ -197,20 +255,20 @@ uint32_t SysSeekFile(uint32_t handle, uint64_t offset);
uint64_t SysGetFilePos(uint32_t handle);
uint64_t SysGetFileSize(uint32_t handle);
uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
uint32_t SysDeleteFile(const char* path);
uint32_t SysRenameFile(const char* oldPath, const char* newPath);
uint32_t SysDeleteFile(const SYS_PATH_REQUEST* request);
uint32_t SysRenameFile(const SYS_RENAME_FILE_REQUEST* request);
uint32_t SysOpenDir(const char* path, uint32_t* handleOut);
uint32_t SysOpenDir(const SYS_PATH_HANDLE_REQUEST* request);
uint32_t SysReadDir(uint32_t handle, void* dirEntryBuffer);
uint32_t SysCreateDir(const char* path);
uint32_t SysRemoveDir(const char* path);
uint32_t SysSetCwd(const char* path);
uint32_t SysGetCwd(char* buf, size_t size);
uint32_t SysCreateDir(const SYS_PATH_REQUEST* request);
uint32_t SysRemoveDir(const SYS_PATH_REQUEST* request);
uint32_t SysSetCwd(const SYS_PATH_REQUEST* request);
uint32_t SysGetCwd(const SYS_GET_CWD_REQUEST* request);
// ---------------------------------------------------------------------------
// Console and graphics (57-61)
// ---------------------------------------------------------------------------
uint32_t SysWriteConsole(const char* buf, size_t len);
uint32_t SysWriteConsole(const SYS_WRITE_CONSOLE_REQUEST* request);
uint32_t SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
uint32_t SysScreenClear(uint32_t color);
uint32_t SysScreenGetSize(int* widthOut, int* heightOut);
@@ -286,8 +344,7 @@ typedef struct SYS_SOCKET_RECV_FROM_REQUEST {
typedef struct SYS_RESOLVE_HOST_REQUEST {
const char* Name;
size_t NameLength;
SYS_STRING_VIEW Name;
uint32_t Family;
uint32_t Flags;
SYS_SOCKET_ADDRESS_IPV4* AddressOut;
@@ -333,7 +390,7 @@ typedef struct SYS_MEMORY_STATUS {
// Time and diagnostics
// ---------------------------------------------------------------------------
uint64_t SysGetTick(void);
uint32_t SysDebugPrint(const char* str);
uint32_t SysDebugPrint(const SYS_DEBUG_PRINT_REQUEST* request);
uint32_t SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info);
uint32_t SysGetMemoryStatus(SYS_MEMORY_STATUS* statusOut);
+4 -3
View File
@@ -5,11 +5,12 @@
#include <syscall.h>
static void print(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
}
static void print_char(char c) {
SysWriteConsole(&c, 1);
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
@@ -63,7 +64,7 @@ static void usage(void) {
int main(void) {
uint16_t port = 8080;
const char* cmd = SysGetPeb()->CommandLine;
const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[64];
cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) {
+4 -3
View File
@@ -5,11 +5,12 @@
#include <syscall.h>
static void print(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
}
static void print_char(char c) {
SysWriteConsole(&c, 1);
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
static void print_u64(uint64_t v) {
@@ -211,7 +212,7 @@ static int run_listen(const char* cmd) {
}
int main(void) {
const char* cmd = SysGetPeb()->CommandLine;
const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[64];
cmd = next_token(cmd, tok, sizeof(tok)); // program path
cmd = next_token(cmd, tok, sizeof(tok)); // verb
+114 -33
View File
@@ -11,8 +11,8 @@
// - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
// always use the single kernel user-heap. This is fine: we don't expose
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - GetLastError / SetLastError are per-thread on Windows. We keep a single
// global for now; enough for Bink and similar single-threaded guests.
// - Win32 per-thread state lives behind SYS_TEB::SubsystemData so the native
// TEB ABI stays subsystem-neutral.
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
// SysCloseFile).
@@ -160,14 +160,94 @@ static void TranslatePath(const char* input, char* output) {
}
}
static DWORD gLastError = 0;
static uint32_t K32OpenFileNative(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenFile(&req);
}
static uint32_t K32CreateFileNative(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysCreateFile(&req);
}
static uint32_t K32DeleteFileNative(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysDeleteFile(&req);
}
static uint32_t K32OpenDirNative(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysOpenDir(&req);
}
static uint32_t K32SetCwdNative(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
return SysSetCwd(&req);
}
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
size_t len = 0;
size_t writable = (buffer && capacity) ? capacity - 1 : 0;
SYS_GET_CWD_REQUEST req = { buffer, writable, &len };
uint32_t s = SysGetCwd(&req);
if (lengthOut) *lengthOut = len;
if (s == 0 && buffer && capacity) buffer[len] = '\0';
return s;
}
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);
}
static void K32WriteConsoleSpan(const char* data, size_t length) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(data, length) };
SysWriteConsole(&req);
}
static void K32DebugPrintA(const char* s) {
if (!s) return;
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
SysDebugPrint(&req);
}
#define WIN32_TLS_SLOTS 64
typedef struct WIN32_THREAD_ENV {
DWORD LastError;
DWORD Reserved0;
void* TlsSlots[WIN32_TLS_SLOTS];
} WIN32_THREAD_ENV;
static WIN32_THREAD_ENV gFallbackThreadEnv;
static WIN32_THREAD_ENV* K32ThreadEnv(void) {
SYS_TEB* teb = SysGetTeb();
if (!teb || teb->Magic != SYS_TEB_MAGIC) return &gFallbackThreadEnv;
WIN32_THREAD_ENV* env = (WIN32_THREAD_ENV*)teb->SubsystemData;
if (!env) {
env = (WIN32_THREAD_ENV*)SysAlloc(sizeof(WIN32_THREAD_ENV));
if (!env) return &gFallbackThreadEnv;
memset(env, 0, sizeof(*env));
teb->SubsystemData = env;
}
return env;
}
static DWORD* K32LastErrorSlot(void) {
return &K32ThreadEnv()->LastError;
}
#define gLastError (*K32LastErrorSlot())
static BOOL KiFileExistsA(const char* path) {
if (!path || !*path) return FALSE;
char native[MAX_PATH];
TranslatePath(path, native);
uint32_t h = 0;
if (SysOpenFile(native, &h) != 0) return FALSE;
if (K32OpenFileNative(native, &h) != 0) return FALSE;
SysCloseHandle(h);
return TRUE;
}
@@ -268,7 +348,7 @@ __declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
// Debug output
// ---------------------------------------------------------------------------
__declspec(dllexport) void OutputDebugStringA(const char* str) {
if (str) SysDebugPrint(str);
if (str) K32DebugPrintA(str);
}
// ---------------------------------------------------------------------------
@@ -510,7 +590,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
// Open the file via the read path (it must already exist).
uint32_t kh = 0;
uint32_t s = SysOpenFile(translatedName, &kh);
uint32_t s = K32OpenFileNative(translatedName, &kh);
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
if (disposition == TRUNCATE_EXISTING && wantWrite) {
@@ -529,10 +609,10 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
// Create-or-replace: try to create; if the file already exists,
// open it and truncate it to zero instead.
uint32_t kh = 0;
uint32_t s = SysCreateFile(translatedName, &kh);
uint32_t s = K32CreateFileNative(translatedName, &kh);
if (s != 0) {
// File may already exist - open and truncate.
s = SysOpenFile(translatedName, &kh);
s = K32OpenFileNative(translatedName, &kh);
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
SysTruncateFile(kh, 0);
}
@@ -542,8 +622,8 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
if (disposition == OPEN_ALWAYS && wantWrite) {
// Open if exists, create if not.
uint32_t kh = 0;
if (SysOpenFile(translatedName, &kh) != 0) {
if (SysCreateFile(translatedName, &kh) != 0) {
if (K32OpenFileNative(translatedName, &kh) != 0) {
if (K32CreateFileNative(translatedName, &kh) != 0) {
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
@@ -555,7 +635,7 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
// Fail if the file already exists (we can't check atomically,
// but SysCreateFile returns NAME_COLLISION for existing files).
uint32_t kh = 0;
uint32_t s = SysCreateFile(translatedName, &kh);
uint32_t s = K32CreateFileNative(translatedName, &kh);
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
return KH_TO_HANDLE(kh);
}
@@ -677,7 +757,7 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
char translatedPath[MAX_PATH];
TranslatePath(lpFileName, translatedPath);
uint32_t s = SysDeleteFile(translatedPath);
uint32_t s = K32DeleteFileNative(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
@@ -747,15 +827,13 @@ __declspec(dllexport) HANDLE GetCurrentThread(void) {
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
// Tiny TLS table; enough for MSVCRT/TCC compatibility paths that probe TLS.
#define WIN32_TLS_SLOTS 64
static void* gTlsSlots[WIN32_TLS_SLOTS];
// Tiny TLS index allocator; slot values are stored per thread in WIN32_THREAD_ENV.
static uint64_t gTlsBitmap = 1; // reserve slot 0
__declspec(dllexport) DWORD TlsAlloc(void) {
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
uint64_t bit = 1ULL << i;
if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; gTlsSlots[i] = NULL; return i; }
if (!(gTlsBitmap & bit)) { gTlsBitmap |= bit; return i; }
}
gLastError = 8;
return 0xFFFFFFFFUL;
@@ -763,20 +841,23 @@ __declspec(dllexport) DWORD TlsAlloc(void) {
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
gTlsSlots[dwTlsIndex] = NULL;
WIN32_THREAD_ENV* env = K32ThreadEnv();
if (env) env->TlsSlots[dwTlsIndex] = NULL;
gTlsBitmap &= ~(1ULL << dwTlsIndex);
return TRUE;
}
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
WIN32_THREAD_ENV* env = K32ThreadEnv();
gLastError = 0;
return gTlsSlots[dwTlsIndex];
return env->TlsSlots[dwTlsIndex];
}
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; }
gTlsSlots[dwTlsIndex] = lpTlsValue;
WIN32_THREAD_ENV* env = K32ThreadEnv();
env->TlsSlots[dwTlsIndex] = lpTlsValue;
return TRUE;
}
@@ -797,7 +878,7 @@ static BOOL KiIsWin32DllNameA(const char* name) {
}
static void KiDbgWriteA(const char* s) {
if (s) SysWriteConsole(s, strlen(s));
if (s) K32WriteConsoleSpan(s, strlen(s));
}
__declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
@@ -856,8 +937,8 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
if (!lpFilename || !nSize) return 0;
SYS_PEB* peb = Win32Peb();
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath)
? peb->ImagePath
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath.Data)
? peb->ImagePath.Data
: "";
DWORD len = (DWORD)strlen(src);
@@ -898,8 +979,8 @@ __declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
__declspec(dllexport) char* GetCommandLineA(void) {
SYS_PEB* peb = Win32Peb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return "";
return (char*)peb->CommandLine;
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
return (char*)peb->CommandLine.Data;
}
// ---------------------------------------------------------------------------
@@ -1025,11 +1106,11 @@ __declspec(dllexport) BOOL CreateProcessA(
if (lpCurrentDirectory && lpCurrentDirectory[0]) {
char translatedCwd[MAX_PATH];
uint32_t gs = SysGetCwd(savedCwd, sizeof(savedCwd));
uint32_t gs = K32GetCwdNative(savedCwd, sizeof(savedCwd), NULL);
if (gs != 0) { gLastError = 3; return FALSE; }
TranslatePath(lpCurrentDirectory, translatedCwd);
gs = SysSetCwd(translatedCwd);
gs = K32SetCwdNative(translatedCwd);
if (gs != 0) { gLastError = 3; return FALSE; }
changedCwd = true;
}
@@ -1038,7 +1119,7 @@ __declspec(dllexport) BOOL CreateProcessA(
// 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 = SysCreateProcess(translatedPath, &kh, cmdline);
uint32_t s = K32CreateProcessNative(translatedPath, &kh, cmdline);
if (changedCwd) {
if (savedCwd[0]) {
@@ -1048,9 +1129,9 @@ __declspec(dllexport) BOOL CreateProcessA(
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
memcpy(restoreCwd + 3, savedCwd, rlen);
restoreCwd[3 + rlen] = '\0';
SysSetCwd(restoreCwd);
K32SetCwdNative(restoreCwd);
} else {
SysSetCwd("0:/");
K32SetCwdNative("0:/");
}
}
@@ -1166,7 +1247,7 @@ __declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput,
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
return TRUE;
}
SysWriteConsole((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
K32WriteConsoleSpan((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;
}
@@ -1306,7 +1387,7 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
// Open the kernel directory iterator
uint32_t kh = 0;
uint32_t s = SysOpenDir(dir[0] ? dir : NULL, &kh);
uint32_t s = K32OpenDirNative(dir[0] ? dir : NULL, &kh);
if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE;
@@ -1387,7 +1468,7 @@ __declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
char translatedPath[MAX_PATH];
TranslatePath(lpPathName, translatedPath);
uint32_t s = SysSetCwd(translatedPath);
uint32_t s = K32SetCwdNative(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; }
return TRUE;
}
@@ -1400,7 +1481,7 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
// Kernel returns native "N:/path". Present it as a Win32 drive letter.
char raw[512];
uint32_t s = SysGetCwd(raw, sizeof(raw));
uint32_t s = K32GetCwdNative(raw, sizeof(raw), NULL);
if (s != 0) { gLastError = 1; return 0; }
char full[515];
+76 -69
View File
@@ -1,69 +1,76 @@
// user32.c - Win32 User Interface
#include <stdint.h>
#include <stddef.h>
#include <syscall.h>
typedef void* HWND;
typedef void* HDC;
typedef long LONG;
typedef int BOOL;
typedef struct _RECT { LONG left, top, right, bottom; } RECT;
typedef struct tagPAINTSTRUCT {
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
} PAINTSTRUCT;
// --- Painting Functions ---
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
(void)hwnd;
if (lpPaint) {
lpPaint->hdc = (HDC)1;
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
}
return (HDC)1;
}
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
// --- Message Loop Stubs ---
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
}
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
(void)m; (void)h; (void)f; (void)l; (void)r;
return 0;
}
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
// --- Dialogs & UI ---
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
(void)h; (void)cap; (void)type;
SysDebugPrint("USER32: MessageBoxA: ");
SysDebugPrint(txt);
SysDebugPrint("\n");
return 1; // IDOK
}
// --- Window Queries ---
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
(void)hwnd; (void)cmd; return (HWND)0;
}
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
(void)hwnd; (void)index; return 0;
}
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
(void)hwnd; (void)index; return 0;
}
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
(void)hwnd;
if (pid) *pid = 1;
return 1; // Thread ID
}
// user32.c - Win32 User Interface
#include <stdint.h>
#include <stddef.h>
#include <syscall.h>
typedef void* HWND;
typedef void* HDC;
typedef long LONG;
typedef int BOOL;
typedef struct _RECT { LONG left, top, right, bottom; } RECT;
typedef struct tagPAINTSTRUCT {
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
} PAINTSTRUCT;
static void User32DebugPrintA(const char* s) {
if (!s) return;
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
SysDebugPrint(&req);
}
// --- Painting Functions ---
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
(void)hwnd;
if (lpPaint) {
lpPaint->hdc = (HDC)1;
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
}
return (HDC)1;
}
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
// --- Message Loop Stubs ---
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
}
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
(void)m; (void)h; (void)f; (void)l; (void)r;
return 0;
}
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
// --- Dialogs & UI ---
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
(void)h; (void)cap; (void)type;
User32DebugPrintA("USER32: MessageBoxA: ");
User32DebugPrintA(txt);
User32DebugPrintA("\n");
return 1; // IDOK
}
// --- Window Queries ---
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
(void)hwnd; (void)cmd; return (HWND)0;
}
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
(void)hwnd; (void)index; return 0;
}
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
(void)hwnd; (void)index; return 0;
}
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
(void)hwnd;
if (pid) *pid = 1;
return 1; // Thread ID
}