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
+1845 -810

No files matched your search

Binary file not shown.
+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
+57 -22
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;
base = 0;
break;
case DOOM_SEEK_CUR:
absolute_offset = SysGetFilePos(h) + offset;
base = (int64_t)f->Pos;
break;
case DOOM_SEEK_END:
absolute_offset = SysGetFileSize(h) + offset;
base = (int64_t)f->Size;
break;
default:
absolute_offset = (uint64_t)offset;
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) {
+27 -8
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
// 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);
// The LAPIC timer counts DOWN.
// Ticks elapsed = (Initial Value) - (Current Value)
uint32_t current = LAPIC_REG(LAPIC_TIMER_CUR);
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);
uint64_t subQuantumUs = 0;
if (gApicTicksPerUs)
subQuantumUs = elapsedApicTicks / gApicTicksPerUs;
return totalUs;
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);
}
+175 -144
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,32 +864,39 @@ 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));
+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);
}
}
+11
View File
@@ -5,6 +5,15 @@
#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);
@@ -27,6 +36,8 @@ 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);
+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;
+355 -194
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;
+3
View File
@@ -27,6 +27,9 @@ KSTATUS LdrResolveImports(uintptr_t imageBase);
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);
+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;
+52 -20
View File
@@ -34,6 +34,7 @@
#include <stdint.h>
#include "printf.h"
#include "syscall/sys_string.h"
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
@@ -229,6 +230,44 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen
}
static size_t _out_string_view(out_fct_type out, char* buffer, size_t idx, size_t maxlen,
const char* str, size_t len,
unsigned int precision, unsigned int width, unsigned int flags)
{
const size_t start_idx = idx;
if ((flags & FLAGS_PRECISION) && len > precision) {
len = precision;
}
if (!str && len) {
str = "(null)";
len = 6U;
if ((flags & FLAGS_PRECISION) && len > precision) {
len = precision;
}
}
if (!(flags & FLAGS_LEFT)) {
for (size_t i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
for (size_t i = 0; i < len; i++) {
out(str ? str[i] : '\0', buffer, idx++, maxlen);
}
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa format
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
{
@@ -800,27 +839,20 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const
}
case 's' : {
const char* p = va_arg(va, char*);
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
const char* p = va_arg(va, const char*);
if (!p) {
p = "(null)";
}
size_t max = (flags & FLAGS_PRECISION) ? (size_t)precision : (size_t)-1;
size_t l = _strnlen_s(p, max);
idx = _out_string_view(out, buffer, idx, maxlen, p, l, precision, width, flags);
format++;
break;
}
case 'v' : {
SYS_STRING_VIEW sv = va_arg(va, SYS_STRING_VIEW);
idx = _out_string_view(out, buffer, idx, maxlen, sv.Data, sv.Length, precision, width, flags);
format++;
break;
}
+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];
+10 -3
View File
@@ -13,6 +13,13 @@ 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;
@@ -39,9 +46,9 @@ __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");
User32DebugPrintA("USER32: MessageBoxA: ");
User32DebugPrintA(txt);
User32DebugPrintA("\n");
return 1; // IDOK
}
+21 -10
View File
@@ -18,11 +18,13 @@ The codebase is still small enough to understand, but large enough to hurt your
- higher-half x86-64 kernel
- physical memory manager
- virtual memory manager with kernel pool and user heap
- W^X-style user page protections
- single address-space design with user/supervisor page permissions
- HAL for screen, keyboard, mouse, timer, PCI, APIC, HPET, AHCI, and IDE
- preemptive priority scheduler
- preemptive priority scheduler accounting for ticks, context switches, idle selections, demotions, and aging promotions
- processes and threads
- object handles
- process termination state tracking and exit signaling
- object handles with per-handle rights metadata
- events, semaphores, mutexes, queues, and alertable waits
- IRQL model: `PASSIVE_LEVEL`, `APC_LEVEL`, `DISPATCH_LEVEL`, and device IRQLs
- interrupt-exit scheduling instead of context-switching inside hard IRQ handlers
@@ -42,14 +44,14 @@ The codebase is still small enough to understand, but large enough to hurt your
- lwIP network stack as a runtime driver (`lwip.sys`)
- RTL8139 and RTL816x ethernet drivers
- native BSD-style socket syscalls: `SysSocket`, `SysBind`, `SysConnect`, `SysSend`, `SysRecv`, `SysPoll`, `SysResolveHost`, and friends
- `SysGetMemoryStatus` syscall for querying physical/pool/heap usage
- length-tracked string-view syscall ABI with request structs for path/name/text arguments
- printf/debug formatting support for bounded string views via `%v`
- INI-based configuration
- PE import resolution and dynamic `.sys` driver loading
- partial Win32 personality
- partial (stub/microsoft-level) POSIX personality
- self-tests for APCs, async disk I/O, async file I/O, trap frames, and cancellation
- fully GDPR-compliant
- Doom, because priorities matter
## architecture notes
@@ -59,8 +61,7 @@ Single address space for the whole system. No separate kernel/user page tables.
Kernel lives in the higher half. User processes only see what they are explicitly given. Kernel pages are supervisor-only so user-mode code cannot touch them. User pages are isolated from each other through the allocator and process creation path, not by hardware address-space separation. One rogue pointer can't crash the kernel but it might corrupt other processes. But what are the chances?
Is this how a serious modern OS should isolate processes? No.
Is it very convenient while building? Absolutely.
User heap and stack mappings are non-executable, and the PE loader applies final section protections after loading. Writable sections are left writable and non-executable; executable sections are mapped executable without write access where possible.
### execution model
@@ -135,7 +136,11 @@ lwIP TCP/IP network stack
PE images get loaded, imports resolved, then the first thread is queued. `syscall.dll` is the shared syscall entry point, and both personalities go through it.
Scheduler is preemptive and priority-based. Process state and thread state are tracked separately, so a process can die or get reaped without taking the whole system down. `init` can restart things if needed.
String and buffer-heavy syscalls use request structs. Text arguments are passed as bounded `SYS_STRING_VIEW` values, then copied and validated by the kernel before use.
Scheduler is preemptive and priority-based. Process state and thread state are tracked separately, so a process can die or get reaped without taking the whole system down. `init` can restart things if needed. Process objects track termination and exit state separately from individual thread lifetime.
Handles carry rights metadata. Syscalls check both object type and required access for operations such as read, write, wait, signal, query, duplicate, and terminate.
Syscalls build real trap frames. That matters because user APC delivery needs to safely redirect the return path back into user mode and then restore the interrupted context with `SysApcReturn`.
@@ -203,6 +208,8 @@ Port forwarding is enabled by default in `runa.bat` — host port 8080 maps to g
Volumes `0`-`7` are available. A volume only works if a filesystem is registered on it (duh).
The Win32 personality translates drive letters to volume numbers, while the POSIX personality maps volumes onto a tree.
Path parsing is length-aware internally. Embedded NUL bytes are rejected for text paths and names.
## how to build
- open the VS x64 build tools as administrator
@@ -260,11 +267,14 @@ todo
## faq
Q: Why?
A: Learning. Fun. Spite.
A: Reasons.
Q: Does it run Doom?
A: `doom.exe`
Q: If I write a program now, will it run in the future?
A: Not a chance. WE DO in fact BREAK USERSPACE.
Q: Can you read and write files?
A: As far as I'm aware.
@@ -278,10 +288,11 @@ Q: Is the Win32 layer real?
A: Depends how you frame it. Some apps will run, most will not.
Q: Are processes isolated?
A: No. But IPC is free.
A: No. But IPC is free. Is this how a serious modern OS should isolate processes? No.
Is it very convenient while building? Absolutely.
Q: How often does it crash?
A: Less often than before.
A: Like Windows 11.
Q: Can it recover from a crash?
A: Yes. Unless the kernel faulted.