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

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

No files matched your search

Binary file not shown.
+9 -3
View File
@@ -7,7 +7,13 @@
#define EVENT_AUTO_RESET 0u #define EVENT_AUTO_RESET 0u
static void print(const char* s) { 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) { 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->Status = iosb ? iosb->Status : 0xFFFFFFFFu;
st->Information = information; st->Information = information;
st->Delivered++; 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) { int main(void) {
@@ -59,7 +65,7 @@ int main(void) {
} }
uint32_t file = 0; uint32_t file = 0;
s = SysOpenFile("system/init.exe", &file); s = open_file("system/init.exe", &file);
if (s != SYS_STATUS_SUCCESS) { if (s != SYS_STATUS_SUCCESS) {
print("async_apc: open failed status="); print("async_apc: open failed status=");
print_u64(s); print_u64(s);
+9 -3
View File
@@ -6,7 +6,13 @@
#define EVENT_AUTO_RESET 0u #define EVENT_AUTO_RESET 0u
static void print(const char* s) { 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) { static void print_u64(uint64_t v) {
@@ -22,7 +28,7 @@ static void print_u64(uint64_t v) {
} }
while (i--) { while (i--) {
char c = buf[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 event = 0;
uint32_t request = 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) { if (s != SYS_STATUS_SUCCESS) {
print("async_cancel: open failed status="); print_u64(s); print("\n"); print("async_cancel: open failed status="); print_u64(s); print("\n");
SysExitProcess(1); SysExitProcess(1);
+1 -1
View File
@@ -1,5 +1,5 @@
[kernel] [kernel]
; bitmask: 1 = debug prints, 2 = self check ; bitmask: 1 = debug prints, 2 = self check
debug = 1 debug = 3
[init] [init]
commandline = apps/shell_native.exe commandline = apps/shell_native.exe
+62 -27
View File
@@ -19,7 +19,8 @@ int _fltused;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void doom_print_wrapper(const char* msg) 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 // 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) static void* doom_open_wrapper(const char* path, const char* mode)
{ {
uint32_t handle = 0; uint32_t handle = 0;
uint32_t result; uint32_t result;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &handle };
if (mode[0] == 'w') { if (mode[0] == 'w') {
result = SysCreateFile(path, &handle); result = SysCreateFile(&req);
} else { } 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) 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) 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; 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; return (int)read;
} }
static int doom_write_wrapper(void* handle, const void* buf, int count) 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; 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; return (int)written;
} }
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin) static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
{ {
uint32_t h = (uint32_t)(uintptr_t)handle; if (!handle) return -1;
uint64_t absolute_offset = 0; DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
int64_t base = 0;
switch (origin) { switch (origin) {
case DOOM_SEEK_SET: case DOOM_SEEK_SET:
absolute_offset = (uint64_t)offset; base = 0;
break; break;
case DOOM_SEEK_CUR: case DOOM_SEEK_CUR:
absolute_offset = SysGetFilePos(h) + offset; base = (int64_t)f->Pos;
break; break;
case DOOM_SEEK_END: case DOOM_SEEK_END:
absolute_offset = SysGetFileSize(h) + offset; base = (int64_t)f->Size;
break; break;
default: default:
absolute_offset = (uint64_t)offset; base = 0;
break; 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; return 0;
} }
static int doom_tell_wrapper(void* handle) 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) static int doom_eof_wrapper(void* handle)
{ {
uint32_t h = (uint32_t)(uintptr_t)handle; if (!handle) return 1;
uint64_t size = SysGetFileSize(h); DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
uint64_t pos = SysGetFilePos(h); return (f->Pos >= f->Size) ? 1 : 0;
return (pos >= size) ? 1 : 0;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -305,9 +340,9 @@ static int build_argv_from_command_line(const char* cmdline, char* storage, size
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
int main(void) 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 // Wire up PureDOOM core mechanics
doom_set_print(doom_print_wrapper); 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); int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
doom_init(argc, argv, 0); 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 // Core execution loop
while (1) { while (1) {
+248 -144
View File
@@ -425,26 +425,6 @@ static KSTATUS DirentUpdateFirstCluster(FAT32_VOL *v, uint64_t lba,
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 8.3 name matching / assembly // 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], static int MatchRaw83(const char dirName[8], const char dirExt[3],
const char wantName[8], const char wantExt[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; memcmp(dirExt, wantExt, 3) == 0;
} }
static void Make83Name(const char *src, char name8[8], char ext3[3]) { static char FatUpperChar(char c);
memset(name8, ' ', 8); memset(ext3, ' ', 3); static int FatIsLowerChar(char c);
static int FatIsValidShortChar(char c);
// A leading dot is not a FAT 8.3 extension separator. The old version static void Make83NameV(SYS_STRING_VIEW src, char name8[8], char ext3[3]) {
// turned ".doomrc" into an invalid empty-base short name with extension memset(name8, ' ', 8);
// "DOO", making .doomrc, .doom and .doo all alias the same entry. Treat memset(ext3, ' ', 3);
// leading-dot names as a basename here; the create path will usually emit if (!SysSvIsValid(src) || src.Length == 0) return;
// a VFAT long-name entry for them anyway.
const char *base = src; size_t baseStart = 0;
const char *dot = NULL; size_t dot = (size_t)-1;
if (src && src[0] == '.' && src[1]) { if (src.Data[0] == '.' && src.Length > 1) {
base = src + 1; baseStart = 1;
} else { } 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; int ni = 0;
const char *end = dot ? dot : base + strlen(base); for (size_t i = baseStart; i < end && ni < 8; i++) {
for (const char *p = base; p < end && ni < 8; p++) { char c = FatUpperChar(src.Data[i]);
char c = *p;
if (c >= 'a' && c <= 'z') c -= 32;
name8[ni++] = c; name8[ni++] = c;
} }
if (dot) {
if (dot != (size_t)-1) {
int ei = 0; int ei = 0;
for (const char *p = dot + 1; *p && ei < 3; p++) { for (size_t i = dot + 1; i < src.Length && ei < 3; i++) {
char c = *p; char c = FatUpperChar(src.Data[i]);
if (c >= 'a' && c <= 'z') c -= 32;
ext3[ei++] = c; 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 // 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 // intentionally structural and case-insensitive: "doom1.wad" must still open
// an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3. // an existing short entry named DOOM1.WAD. Leading-dot names are not 8.3.
static int FatNameIsStructurallyShort83(const char *name) { static int FatNameIsStructurallyShort83V(SYS_STRING_VIEW name) {
if (!name || !name[0]) return 0; if (!SysSvIsValid(name) || name.Length == 0) return 0;
if (name[0] == '.') return 0; if (name.Data[0] == '.') return 0;
const char *dot = NULL; size_t dot = (size_t)-1;
for (const char *p = name; *p; p++) { for (size_t i = 0; i < name.Length; i++) {
if (*p == '.') { char c = name.Data[i];
if (dot) return 0; // multiple dots need LFN if (c == '.') {
dot = p; if (dot != (size_t)-1) return 0;
} else if (!FatIsValidShortChar(*p)) { dot = i;
} else if (!FatIsValidShortChar(c)) {
return 0; return 0;
} }
} }
size_t baseLen = dot ? (size_t)(dot - name) : strlen(name); size_t baseLen = (dot != (size_t)-1) ? dot : name.Length;
size_t extLen = dot ? strlen(dot + 1) : 0; size_t extLen = (dot != (size_t)-1) ? (name.Length - dot - 1) : 0;
return baseLen >= 1 && baseLen <= 8 && extLen <= 3; return baseLen >= 1 && baseLen <= 8 && extLen <= 3;
} }
// Creation uses LFN when needed for structure, or when lowercase would be lost. // Creation uses LFN when needed for structure, or when lowercase would be lost.
static int FatNameNeedsLfnForCreate(const char *name) { static int FatNameNeedsLfnForCreateV(SYS_STRING_VIEW name) {
if (!FatNameIsStructurallyShort83(name)) return 1; if (!FatNameIsStructurallyShort83V(name)) return 1;
for (const char *p = name; *p; p++) for (size_t i = 0; i < name.Length; i++)
if (FatIsLowerChar(*p)) return 1; if (FatIsLowerChar(name.Data[i])) return 1;
return 0; return 0;
} }
@@ -624,33 +606,35 @@ static void FatAliasAppendChar(char *dst, int *len, int maxLen, char c) {
if (*len < maxLen) dst[(*len)++] = 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(stem, 0, 9);
memset(ext3, ' ', 3); memset(ext3, ' ', 3);
if (!SysSvIsValid(src)) return;
const char *base = src; size_t baseStart = 0;
const char *dot = NULL; size_t dot = (size_t)-1;
if (src[0] == '.' && src[1]) { if (src.Length > 1 && src.Data[0] == '.') {
// Hidden/Unix-style names like .doomrc become alias stem DOOMRC. // Hidden/Unix-style names like .doomrc become alias stem DOOMRC.
base = src + 1; baseStart = 1;
} else { } 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; int slen = 0;
const char *end = dot ? dot : src + strlen(src); size_t end = (dot != (size_t)-1) ? dot : src.Length;
for (const char *p = base; p < end && *p; p++) for (size_t i = baseStart; i < end; i++)
FatAliasAppendChar(stem, &slen, 8, *p); FatAliasAppendChar(stem, &slen, 8, src.Data[i]);
if (slen == 0) { if (slen == 0) {
memcpy(stem, "FILE", 4); memcpy(stem, "FILE", 4);
slen = 4; slen = 4;
} }
stem[slen] = '\0'; stem[slen] = '\0';
if (dot && dot[1]) { if (dot != (size_t)-1 && dot + 1 < src.Length) {
int ei = 0; int ei = 0;
for (const char *p = dot + 1; *p && ei < 3; p++) { for (size_t i = dot + 1; i < src.Length && ei < 3; i++) {
char c = FatUpperChar(*p); char c = FatUpperChar(src.Data[i]);
if (!FatIsValidShortChar(c)) continue; if (!FatIsValidShortChar(c)) continue;
ext3[ei++] = c; ext3[ei++] = c;
} }
@@ -683,11 +667,11 @@ static void MakeAliasAttempt(const char stem[9], const char extIn[3],
memcpy(name8 + keep, suffix, suffixLen); memcpy(name8 + keep, suffix, suffixLen);
} }
static int MakeUniqueShortAlias(FAT32_VOL *v, uint32_t dirCluster, static int MakeUniqueShortAliasV(FAT32_VOL *v, uint32_t dirCluster,
const char *longName, SYS_STRING_VIEW longName,
char name8[8], char ext3[3]) { char name8[8], char ext3[3]) {
char stem[9], extCandidate[3]; char stem[9], extCandidate[3];
BuildAliasStemExt(longName, stem, extCandidate); BuildAliasStemExtV(longName, stem, extCandidate);
for (uint32_t attempt = 1; attempt < 1000000; attempt++) { for (uint32_t attempt = 1; attempt < 1000000; attempt++) {
MakeAliasAttempt(stem, extCandidate, attempt, name8, ext3); MakeAliasAttempt(stem, extCandidate, attempt, name8, ext3);
@@ -696,12 +680,25 @@ static int MakeUniqueShortAlias(FAT32_VOL *v, uint32_t dirCluster,
return 0; return 0;
} }
static KSTATUS DeleteLegacyBrokenDotAlias(FAT32_VOL *v, uint32_t dirCluster, static void MakeLegacyBrokenDot83V(SYS_STRING_VIEW src, char name8[8], char ext3[3]) {
const char *longName) { // This exactly models the pre-fix bug for ".doomrc": empty name + "DOO".
if (!longName || longName[0] != '.' || !longName[1]) return STATUS_SUCCESS; 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]; char badName[8], badExt[3];
MakeLegacyBrokenDot83(longName, badName, badExt); MakeLegacyBrokenDot83V(longName, badName, badExt);
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster); uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return STATUS_NO_MEMORY; 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 (a >= 'A' && a <= 'Z') a += 32;
if (b >= 'A' && b <= 'Z') b += 32; if (b >= 'A' && b <= 'Z') b += 32;
if (a != b) return 0; if (a != b) return 0;
} }
return *lfn == '\0' && *want == '\0'; return 1;
} }
// --------------------------------------------------------------------------- static int SearchDirV(FAT32_VOL *v, uint32_t dirCluster, SYS_STRING_VIEW name,
// Directory search uint32_t *outCluster, uint32_t *outSize, uint8_t *outAttr,
// --------------------------------------------------------------------------- uint64_t *outDirentLba, uint32_t *outDirentOffset) {
static int SearchDir(FAT32_VOL *v, uint32_t dirCluster, const char *name, if (!Fat32NameViewIsValid(name)) return 0;
uint32_t *outCluster, uint32_t *outSize, uint8_t *outAttr,
uint64_t *outDirentLba, uint32_t *outDirentOffset) {
uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster); uint8_t *clusterBuf = (uint8_t *)MmAllocatePool(v->BytesPerCluster);
if (!clusterBuf) return 0; if (!clusterBuf) return 0;
char lfn[MAX_LFN]; int hasLfn = 0; memset(lfn, 0, sizeof(lfn)); char lfn[MAX_LFN]; int hasLfn = 0; memset(lfn, 0, sizeof(lfn));
char wantName83[8], wantExt83[3]; char wantName83[8], wantExt83[3];
int allowShortMatch = FatNameIsStructurallyShort83(name); int allowShortMatch = FatNameIsStructurallyShort83V(name);
Make83Name(name, wantName83, wantExt83); Make83NameV(name, wantName83, wantExt83);
uint32_t cluster = dirCluster; uint32_t cluster = dirCluster;
while (cluster >= 2 && cluster < FAT32_EOC) { 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; } if (dir[i].Attr == ATTR_LFN) { LfnAppend(lfn, (LFN_ENTRY *)&dir[i]); hasLfn = 1; continue; }
int match = 0; int match = 0;
if (hasLfn && lfn[0]) match = LfnMatch(lfn, name); if (hasLfn && lfn[0]) match = LfnMatchV(lfn, name);
if (!match && allowShortMatch) if (!match && allowShortMatch)
match = MatchName83(dir[i].Name, dir[i].Ext, name); match = MatchName83V(dir[i].Name, dir[i].Ext, name);
if (!match && allowShortMatch) if (!match && allowShortMatch)
match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83); match = MatchRaw83(dir[i].Name, dir[i].Ext, wantName83, wantExt83);
hasLfn = 0; memset(lfn, 0, sizeof(lfn)); hasLfn = 0; memset(lfn, 0, sizeof(lfn));
@@ -819,58 +858,92 @@ done:
return 0; return 0;
} }
// --------------------------------------------------------------------------- static int Fat32PathNextComponent(SYS_STRING_VIEW path, size_t* cursor,
// Path traversal SYS_STRING_VIEW* component) {
// --------------------------------------------------------------------------- if (!cursor || !component || !SysSvIsValid(path)) return 0;
static KSTATUS Fat32LookupPath(FAT32_VOL *v, const char *path, size_t pos = *cursor;
uint32_t *outCluster, uint32_t *outSize, while (pos < path.Length && Fat32IsPathSep(path.Data[pos])) pos++;
uint64_t *outDirentLba, uint32_t *outDirentOffset) { 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; uint32_t cluster = v->RootCluster, size = 0;
uint8_t attr = ATTR_DIRECTORY; uint8_t attr = ATTR_DIRECTORY;
if (outDirentLba) *outDirentLba = 0; if (outDirentLba) *outDirentLba = 0;
if (outDirentOffset) *outDirentOffset = 0; if (outDirentOffset) *outDirentOffset = 0;
char component[256]; const char *p = path; size_t cursor = 0;
while (*p) { SYS_STRING_VIEW component;
while (*p == '\\' || *p == '/') p++; while (Fat32PathNextComponent(path, &cursor, &component)) {
if (!*p) break; if (component.Length >= MAX_LFN) return STATUS_BUFFER_TOO_SMALL;
int len = 0; if (!Fat32NameViewIsValid(component)) return STATUS_INVALID_PARAMETER;
while (*p && *p != '\\' && *p != '/' && len < 255) component[len++] = *p++;
component[len] = '\0';
if (!len) break;
if (!(attr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND; if (!(attr & ATTR_DIRECTORY)) return STATUS_NOT_FOUND;
uint32_t childCluster, childSize; uint8_t childAttr; uint32_t childCluster, childSize; uint8_t childAttr;
uint64_t childLba = 0; uint32_t childOffset = 0; uint64_t childLba = 0; uint32_t childOffset = 0;
if (!SearchDir(v, cluster, component, &childCluster, &childSize, if (!SearchDirV(v, cluster, component, &childCluster, &childSize,
&childAttr, &childLba, &childOffset)) &childAttr, &childLba, &childOffset))
return STATUS_NOT_FOUND; return STATUS_NOT_FOUND;
cluster = childCluster; size = childSize; attr = childAttr; cluster = childCluster; size = childSize; attr = childAttr;
if (outDirentLba) *outDirentLba = childLba; if (outDirentLba) *outDirentLba = childLba;
if (outDirentOffset) *outDirentOffset = childOffset; if (outDirentOffset) *outDirentOffset = childOffset;
} }
*outCluster = cluster; *outSize = size;
*outCluster = cluster;
*outSize = size;
return STATUS_SUCCESS; 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) { uint32_t *outCluster, uint32_t *outSize) {
return Fat32LookupPath(v, path, outCluster, outSize, NULL, NULL); return Fat32LookupPathV(v, path, outCluster, outSize, NULL, NULL);
} }
// --------------------------------------------------------------------------- static KSTATUS SplitPathV(SYS_STRING_VIEW path, SYS_STRING_VIEW* parent,
// Path helpers for Fat32Create SYS_STRING_VIEW* name) {
// --------------------------------------------------------------------------- if (!parent || !name || !SysSvIsValid(path)) return STATUS_INVALID_PARAMETER;
static void SplitPath(const char *path, char parent[256], char name[256]) { *parent = SYS_SV_NULL;
const char *last = NULL; *name = SYS_SV_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'; } while (path.Length && Fat32IsPathSep(path.Data[path.Length - 1]))
else { path.Length--;
size_t len = (size_t)(last - path); if (len > 255) len = 255; if (path.Length == 0) return STATUS_INVALID_PARAMETER;
memcpy(parent, path, len); parent[len] = '\0';
strncpy(name, last+1, 255); name[255] = '\0'; 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 // 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; FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset; 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 (!KSUCCESS(s)) return s;
FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE)); FAT32_FILE *f = (FAT32_FILE *)MmAllocatePool(sizeof(FAT32_FILE));
if (!f) return STATUS_NO_MEMORY; if (!f) return STATUS_NO_MEMORY;
f->StartCluster = cluster; f->FileSize = size; f->Vol = v; 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_FILE *f = (FAT32_FILE *)fileCtx;
FAT32_VOL *v = f->Vol; FAT32_VOL *v = f->Vol;
if (offset >= f->FileSize || size == 0) return STATUS_SUCCESS; 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; size_t wanted = size;
// Single allocation for the largest possible coalesced run. // Allocate only as much bounce space as this read can actually use.
uint8_t *runBuf = (uint8_t *)MmAllocatePool(MAX_RUN_SECTORS * 512u); // 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; if (!runBuf) return STATUS_NO_MEMORY;
size_t totalRead = 0; size_t totalRead = 0;
@@ -1031,13 +1116,21 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
cluster = FatEntry(v, cluster); cluster = FatEntry(v, cluster);
while (size > 0 && cluster >= 2 && cluster < FAT32_EOC) { 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 runStart = cluster;
uint32_t runSectors = v->SectorsPerCluster; uint32_t runSectors = v->SectorsPerCluster;
uint32_t next = FatEntry(v, cluster); uint32_t next = FatEntry(v, cluster);
while (next == cluster + 1 && next < FAT32_EOC while (next == cluster + 1 && next < FAT32_EOC
&& runSectors + v->SectorsPerCluster <= MAX_RUN_SECTORS) { && runSectors + v->SectorsPerCluster <= usefulSectors) {
cluster = next; cluster = next;
next = FatEntry(v, cluster); next = FatEntry(v, cluster);
runSectors += v->SectorsPerCluster; 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 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; FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
char parentPath[256], filename[256]; SYS_STRING_VIEW parentPath;
SplitPath(path, parentPath, filename); SYS_STRING_VIEW filenameView;
if (!filename[0]) return STATUS_INVALID_PARAMETER; KSTATUS s = SplitPathV(path, &parentPath, &filenameView);
if (!KSUCCESS(s)) return s;
uint32_t parentCluster, parentSize; uint32_t parentCluster, parentSize;
KSTATUS s = Traverse(v, parentPath, &parentCluster, &parentSize); s = TraverseV(v, parentPath, &parentCluster, &parentSize);
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
{ {
uint32_t ec, es; uint8_t ea; uint32_t ec, es; uint8_t ea;
uint64_t existingLba = 0; uint32_t existingOffset = 0; uint64_t existingLba = 0; uint32_t existingOffset = 0;
if (SearchDir(v, parentCluster, filename, &ec, &es, &ea, if (SearchDirV(v, parentCluster, filenameView, &ec, &es, &ea,
&existingLba, &existingOffset)) { &existingLba, &existingOffset)) {
if (ea & ATTR_DIRECTORY) if (ea & ATTR_DIRECTORY)
return STATUS_OBJECT_NAME_COLLISION; 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]; char shortName[8], shortExt[3];
if (useLfn) { if (useLfn) {
s = DeleteLegacyBrokenDotAlias(v, parentCluster, filename); s = DeleteLegacyBrokenDotAliasV(v, parentCluster, filenameView);
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
if (!MakeUniqueShortAlias(v, parentCluster, filename, shortName, shortExt)) if (!MakeUniqueShortAliasV(v, parentCluster, filenameView, shortName, shortExt))
return STATUS_OBJECT_NAME_COLLISION; return STATUS_OBJECT_NAME_COLLISION;
} else { } 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; if (nameLen == 0 || nameLen >= MAX_LFN) return STATUS_INVALID_PARAMETER;
uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0; uint32_t lfnCount = useLfn ? (uint32_t)((nameLen + 12u) / 13u) : 0;
uint32_t neededEntries = lfnCount + 1u; 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); uint8_t checksum = LfnChecksum(shortName, shortExt);
for (uint32_t slot = 0; slot < lfnCount; slot++) { for (uint32_t slot = 0; slot < lfnCount; slot++) {
uint32_t ordinal = 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); nameLen, ordinal, lfnCount, checksum);
} }
} }
@@ -1348,10 +1445,13 @@ update_size:
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Delete // 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; FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset; 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 (!KSUCCESS(s)) return s;
if (!direntLba) return STATUS_ACCESS_DENIED; if (!direntLba) return STATUS_ACCESS_DENIED;
@@ -1389,13 +1489,17 @@ typedef struct {
uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir
} FAT32_DIR_CTX; } 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; FAT32_VOL *v = (FAT32_VOL *)fs->DriverCtx;
uint32_t cluster; uint32_t cluster;
if (!dirPath || dirPath[0] == '\0') { if (dirPath.Length == 0) {
cluster = v->RootCluster; cluster = v->RootCluster;
} else { } 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; if (!KSUCCESS(s)) return s;
} }
+8 -5
View File
@@ -302,8 +302,9 @@ static void move_down(void) {
static void save_file(void) { static void save_file(void) {
uint32_t h = 0; uint32_t h = 0;
uint32_t st = SysCreateFile(gFilename, &h); SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
if (st != 0) st = SysOpenFile(gFilename, &h); uint32_t st = SysCreateFile(&pathReq);
if (st != 0) st = SysOpenFile(&pathReq);
if (st != 0) { msg_set("save failed: cannot open file"); return; } if (st != 0) { msg_set("save failed: cannot open file"); return; }
SysTruncateFile(h, 0); SysTruncateFile(h, 0);
@@ -339,7 +340,8 @@ static void load_file(void) {
reset_buffer(); reset_buffer();
uint32_t h = 0; 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"); msg_set("new file");
return; return;
} }
@@ -411,7 +413,7 @@ static bool read_token(const char* s, char* out, size_t cap) {
static void parse_filename_from_cmdline(void) { static void parse_filename_from_cmdline(void) {
SYS_PEB* peb = SysGetPeb(); 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); const char* arg = skip_token(cmd);
if (read_token(arg, gFilename, sizeof(gFilename))) return; if (read_token(arg, gFilename, sizeof(gFilename))) return;
strcpy(gFilename, "untitled.txt"); strcpy(gFilename, "untitled.txt");
@@ -476,7 +478,8 @@ static void editor_loop(void) {
} }
static void print(const char* s) { static void print(const char* s) {
SysWriteConsole(s, strlen(s)); SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
} }
int main(void) { int main(void) {
+29 -10
View File
@@ -23,6 +23,7 @@ static volatile uint32_t* gLapic = NULL;
static uint32_t gApicTicksPerUs = 0; static uint32_t gApicTicksPerUs = 0;
static uint32_t gApicTicksPerQuantum = 0; static uint32_t gApicTicksPerQuantum = 0;
static uint64_t gHpetTicksPerUs = 0; static uint64_t gHpetTicksPerUs = 0;
static uint64_t gHpetHz = 0;
static volatile uint64_t* gHpet = NULL; static volatile uint64_t* gHpet = NULL;
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off))) #define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
@@ -39,11 +40,28 @@ static void TimerIsr(void) {
} }
static uint64_t QueryTicks(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
// The LAPIC timer counts DOWN. // countdown register.
// Ticks elapsed = (Initial Value) - (Current Value) if (gHpet && gHpetHz) {
uint32_t current = LAPIC_REG(LAPIC_TIMER_CUR); uint64_t counter = gHpet[30]; // offset 0xF0: main counter
uint64_t seconds = counter / gHpetHz;
uint64_t rem = counter % gHpetHz;
return seconds * 1000000ull + (rem * 1000000ull) / gHpetHz;
}
// Fallback: combine the coarse periodic tick with the current LAPIC
// countdown. Read the coarse tick twice so an interrupt between gTicks and
// LAPIC_TIMER_CUR cannot make time jump backwards by almost one quantum.
uint64_t baseTicks1;
uint64_t baseTicks2;
uint32_t current;
do {
baseTicks1 = gTicks;
current = LAPIC_REG(LAPIC_TIMER_CUR);
baseTicks2 = gTicks;
} while (baseTicks1 != baseTicks2);
uint32_t initial = gApicTicksPerQuantum; uint32_t initial = gApicTicksPerQuantum;
uint32_t elapsedApicTicks = 0; uint32_t elapsedApicTicks = 0;
@@ -51,11 +69,11 @@ static uint64_t QueryTicks(void) {
elapsedApicTicks = initial - current; elapsedApicTicks = initial - current;
} }
// Convert base 10ms ticks to microseconds + the remainder uint64_t subQuantumUs = 0;
// gTicks * 10,000 (ms to us) + (elapsed / ticks_per_us) if (gApicTicksPerUs)
uint64_t totalUs = (baseTicks * QUANTUM_MS * 1000) + (elapsedApicTicks / gApicTicksPerUs); subQuantumUs = elapsedApicTicks / gApicTicksPerUs;
return totalUs; return (baseTicks1 * QUANTUM_MS * 1000ull) + subQuantumUs;
} }
static void Stall(uint32_t us) { static void Stall(uint32_t us) {
@@ -181,6 +199,7 @@ KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *o
if (hpetPhys) { if (hpetPhys) {
gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys); gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys);
uint64_t hpetHz = HpetInit(hpetPhys); uint64_t hpetHz = HpetInit(hpetPhys);
gHpetHz = hpetHz;
gHpetTicksPerUs = hpetHz / 1000000; gHpetTicksPerUs = hpetHz / 1000000;
} }
+5 -5
View File
@@ -5,11 +5,12 @@
#include <syscall.h> #include <syscall.h>
static void print(const char* s) { 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) { 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) { 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; SYS_RESOLVE_HOST_REQUEST req;
memset(&resolved, 0, sizeof(resolved)); memset(&resolved, 0, sizeof(resolved));
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.Name = host; req.Name = SysSvFromCstr(host);
req.NameLength = strlen(host);
req.Family = SYS_AF_INET; req.Family = SYS_AF_INET;
req.AddressOut = &resolved; req.AddressOut = &resolved;
req.AddressSizeInOut = &resolvedSize; req.AddressSizeInOut = &resolvedSize;
@@ -142,7 +142,7 @@ int main(void) {
path[0] = '/'; path[0] = '/';
path[1] = 0; path[1] = 0;
const char* cmd = SysGetPeb()->CommandLine; const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[256]; char tok[256];
cmd = next_token(cmd, tok, sizeof(tok)); // program path cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) { 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) void _start(void)
{ {
const char* args = SysGetPeb()->CommandLine; SYS_STRING_VIEW args = SysGetPeb()->CommandLine;
uint32_t h = 0; uint32_t h = 0;
uint32_t s = 0; uint32_t s = 0;
while (true) { while (true) {
SysDebugPrint("[init] starting\n"); debug_print("[init] starting\n");
s = SysCreateProcess(args, &h, NULL); SYS_CREATE_PROCESS_REQUEST cp = { args, SysSvFromParts(0, 0), &h };
s = SysCreateProcess(&cp);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
SysDebugPrint("[init] FATAL: failed to start!\n"); debug_print("[init] FATAL: failed to start!\n");
char buffer[12]; char buffer[12];
itoa(s, buffer); itoa(s, buffer);
SysDebugPrint(buffer); debug_print(buffer);
SysDebugPrint("\n"); debug_print("\n");
SysExitProcess(1); SysExitProcess(1);
} }
SysDebugPrint("[init] waiting\n"); debug_print("[init] waiting\n");
s = SysWaitForProcess(h); s = SysWaitForProcess(h);
if (s != STATUS_SUCCESS) if (s != STATUS_SUCCESS)
SysDebugPrint("[init] WARNING: SysWaitForProcess returned error\n"); debug_print("[init] WARNING: SysWaitForProcess returned error\n");
int exitCode = 0; int exitCode = 0;
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) { 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; exitCode = 1;
} }
@@ -82,9 +88,9 @@ void _start(void)
if (exitCode == 0) if (exitCode == 0)
break; 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); SysExitProcess(0);
} }
+176 -145
View File
@@ -387,22 +387,26 @@ __declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJE
// Stored as (CurrentDrive, drive-relative path with no trailing backslash). // Stored as (CurrentDrive, drive-relative path with no trailing backslash).
// Exported as native "N:/path" so personalities can round-trip volumes. // 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) { typedef struct KRESOLVED_PATH {
if (!buf || size == 0) return 0; 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 (drive > 7) drive = 0;
if (!rel) rel = "";
size_t relLen = strlen(rel); size_t needed = 3 + rel.Length + 1;
size_t needed = 3 + relLen + 1;
if (needed > size) return needed; if (needed > size) return needed;
buf[0] = (char)('0' + drive); buf[0] = (char)('0' + drive);
buf[1] = ':'; buf[1] = ':';
buf[2] = '/'; buf[2] = '/';
memcpy(buf + 3, rel, relLen); if (rel.Length) memcpy(buf + 3, rel.Data, rel.Length);
buf[3 + relLen] = '\0'; buf[3 + rel.Length] = '\0';
return needed; return needed;
} }
@@ -413,17 +417,57 @@ __declspec(dllexport) KSTATUS IoGetCwd(char* buf, size_t size) {
if (!proc) return STATUS_ACCESS_DENIED; if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock); 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); KeReleaseSpinlock(&proc->CwdLock, flags);
return needed && needed <= size ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL; return needed && needed <= size ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
} }
// Resolve a possibly-relative path component against the CWD. static int KiSvHasDriveRoot(SYS_STRING_VIEW path) {
// Writes the fully-resolved, drive-stripped relative path into 'resolved' return path.Length >= 3 &&
// (caller supplies buf of at least CWD_MAX bytes). path.Data[0] >= '0' && path.Data[0] <= '7' &&
// Returns the drive index. path.Data[1] == ':' &&
static uint8_t KiResolvePath(const char* path, char* resolved, size_t resolvedSize) { (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; uint8_t drive = 0;
PROCESS* proc = KeGetCurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (proc) { if (proc) {
@@ -432,128 +476,98 @@ static uint8_t KiResolvePath(const char* path, char* resolved, size_t resolvedSi
KeReleaseSpinlock(&proc->CwdLock, flags); KeReleaseSpinlock(&proc->CwdLock, flags);
} }
// Strip explicit drive prefix "N:\" or "N:/". size_t pos = 0;
bool fromDriveRoot = false; bool fromDriveRoot = false;
if (path[0] >= '0' && path[0] <= '7' && if (KiSvHasDriveRoot(input)) {
path[1] == ':' && (path[2] == '\\' || path[2] == '/')) { drive = (uint8_t)(input.Data[0] - '0');
drive = (uint8_t)(path[0] - '0'); pos = 3;
path = path + 3;
fromDriveRoot = true; fromDriveRoot = true;
} }
char work[CWD_MAX * 2]; char work[CWD_MAX * 2];
size_t workLen = 0;
work[0] = '\0'; work[0] = '\0';
// Initialize our working base bool absolute = fromDriveRoot ||
if (path[0] == '\\' || path[0] == '/' || fromDriveRoot) { (pos < input.Length && (input.Data[pos] == '\\' || input.Data[pos] == '/'));
// Absolute path: start from the root (empty work buffer) if (!absolute && proc) {
// The segment loop below will automatically skip the leading separators. uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
} else { size_t cwdLen = proc->CurrentDirectoryLength;
// Relative path: start from the current process's CWD if (cwdLen >= sizeof(work)) cwdLen = sizeof(work) - 1;
PROCESS* proc = KeGetCurrentProcess(); memcpy(work, proc->CurrentDirectory, cwdLen);
if (proc) { work[cwdLen] = '\0';
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock); workLen = cwdLen;
size_t wlen = strlen(proc->CurrentDirectory); KeReleaseSpinlock(&proc->CwdLock, flags);
if (wlen >= sizeof(work)) wlen = sizeof(work) - 1;
memcpy(work, proc->CurrentDirectory, wlen);
work[wlen] = '\0';
KeReleaseSpinlock(&proc->CwdLock, flags);
}
} }
// Segment-by-segment canonicalization while (pos < input.Length) {
const char* p = path; while (pos < input.Length && (input.Data[pos] == '\\' || input.Data[pos] == '/')) pos++;
while (*p) { if (pos >= input.Length) break;
// Skip separators
while (*p == '\\' || *p == '/') p++;
if (!*p) break;
// Read next component size_t compStart = pos;
char comp[256]; while (pos < input.Length && input.Data[pos] != '\\' && input.Data[pos] != '/') pos++;
int clen = 0; size_t compLen = pos - compStart;
while (*p && *p != '\\' && *p != '/' && clen < 255) if (compLen == 0) continue;
comp[clen++] = *p++;
comp[clen] = '\0';
if (clen == 0) continue; if (compLen == 1 && input.Data[compStart] == '.') {
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 == 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 if (workLen + 1 > sizeof(out->Storage)) return STATUS_BUFFER_TOO_SMALL;
size_t finalLen = strlen(work); memcpy(out->Storage, work, workLen);
if (finalLen + 1 > resolvedSize) finalLen = resolvedSize - 1; out->Storage[workLen] = '\0';
memcpy(resolved, work, finalLen); out->Drive = drive;
resolved[finalLen] = '\0'; out->Path = SysSvFromParts(out->Storage, workLen);
return STATUS_SUCCESS;
return drive;
} }
__declspec(dllexport) KSTATUS IoSetCwd(const char* path) { __declspec(dllexport) KSTATUS IoSetCwd(SYS_STRING_VIEW path) {
if (!path) return STATUS_INVALID_PARAMETER; if (path.Length && !path.Data) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
PROCESS* proc = KeGetCurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED; if (!proc) return STATUS_ACCESS_DENIED;
// Resolve the new path (this also validates ".." navigation). KRESOLVED_PATH resolved;
// Note: KiResolvePath already uses proc->CurrentDirectory internally! KSTATUS s = KiResolvePathV(path, &resolved);
char resolved[CWD_MAX]; if (!KSUCCESS(s)) return s;
uint8_t drive = KiResolvePath(path, resolved, sizeof(resolved));
// Validate: try to open the directory to confirm it actually exists on disk IO_FILESYSTEM* fs = gVolumes[resolved.Drive].Fs;
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND; if (!fs) return STATUS_NOT_FOUND;
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED; if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
void* dirCtx = NULL; void* dirCtx = NULL;
KSTATUS s = fs->OpenDir(fs, resolved, &dirCtx); s = fs->OpenDir(fs, resolved.Path, &dirCtx);
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
fs->CloseDir(fs, dirCtx); fs->CloseDir(fs, dirCtx);
// Commit the new CWD to the process block if (resolved.Path.Length + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
size_t len = strlen(resolved);
if (len + 1 > sizeof(proc->CurrentDirectory)) return STATUS_BUFFER_TOO_SMALL;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock); uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
proc->CurrentDrive = drive; proc->CurrentDrive = resolved.Drive;
memcpy(proc->CurrentDirectory, resolved, len + 1); 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) { 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; size_t copyLen = (formatted && formatted <= proc->UserCwdSize) ? formatted - 1 : proc->UserCwdSize - 1;
SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase; SYS_PEB* peb = (SYS_PEB*)proc->UserPebBase;
if (peb && peb->Magic == SYS_PEB_MAGIC) { if (peb && peb->Magic == SYS_PEB_MAGIC) {
peb->CurrentDirectory = (const char*)proc->UserCwdBase; peb->CurrentDirectory = SysSvFromParts((const char*)proc->UserCwdBase, copyLen);
peb->CurrentDirectoryLength = copyLen;
} }
} }
KeReleaseSpinlock(&proc->CwdLock, flags); KeReleaseSpinlock(&proc->CwdLock, flags);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@@ -583,18 +597,20 @@ static void KiFileDestroy(OBJECT* obj) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// IoOpenFile // IoOpenFile
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoOpenFile(const char* path, FILE_OBJECT **out) { __declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out) {
if (!path || !out) return STATUS_INVALID_PARAMETER; if ((!path.Data && path.Length) || !out) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
if (path.Length == 0) return STATUS_INVALID_PARAMETER;
char relPath[CWD_MAX]; KRESOLVED_PATH resolved;
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath)); 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) return STATUS_NOT_FOUND;
void* fileCtx; void* fileCtx;
KSTATUS s = fs->Open(fs, relPath, &fileCtx); s = fs->Open(fs, resolved.Path, &fileCtx);
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
FILE_OBJECT* f = (FILE_OBJECT *)ObAllocateObject(ObjectTypeFile /* reuse slot */, 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->Header.Destroy = KiFileDestroy;
f->Fs = fs; f->Fs = fs;
f->FileCtx = fileCtx; f->FileCtx = fileCtx;
f->Lock = (SPINLOCK)SPINLOCK_INIT;
f->Size = fs->GetSize(fs, fileCtx); f->Size = fs->GetSize(fs, fileCtx);
f->Offset = 0; 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 // IoCreateFile - create a new file and return it open for writing
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoCreateFile(const char* path, FILE_OBJECT** out) { __declspec(dllexport) KSTATUS IoCreateFile(SYS_STRING_VIEW path, FILE_OBJECT** out) {
if (!path || !out) return STATUS_INVALID_PARAMETER; if ((!path.Data && path.Length) || !out || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX]; KRESOLVED_PATH resolved;
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath)); 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) return STATUS_NOT_FOUND;
if (!fs->Create) return STATUS_NOT_SUPPORTED; if (!fs->Create) return STATUS_NOT_SUPPORTED;
void* fileCtx; void* fileCtx;
KSTATUS s = fs->Create(fs, relPath, &fileCtx); s = fs->Create(fs, resolved.Path, &fileCtx);
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
FILE_OBJECT* f = (FILE_OBJECT*)ObAllocateObject(ObjectTypeFile, 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) // IoDeleteFile - delete a file by path (does not require an open handle)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
__declspec(dllexport) KSTATUS IoDeleteFile(const char* path) { __declspec(dllexport) KSTATUS IoDeleteFile(SYS_STRING_VIEW path) {
if (!path) return STATUS_INVALID_PARAMETER; if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX]; KRESOLVED_PATH resolved;
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath)); 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) return STATUS_NOT_FOUND;
if (!fs->Delete) return STATUS_NOT_SUPPORTED; 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) { __declspec(dllexport) KSTATUS IoRenameFile(SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath) {
if (!oldPath || !newPath) return STATUS_INVALID_PARAMETER; 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; if (!KeCanWait()) return STATUS_INVALID_IRQL;
char oldRel[CWD_MAX]; KRESOLVED_PATH oldResolved;
char newRel[CWD_MAX]; KRESOLVED_PATH newResolved;
uint8_t oldDrive = KiResolvePath(oldPath, oldRel, sizeof(oldRel)); KSTATUS s = KiResolvePathV(oldPath, &oldResolved);
uint8_t newDrive = KiResolvePath(newPath, newRel, sizeof(newRel)); if (!KSUCCESS(s)) return s;
if (oldDrive != newDrive) return STATUS_NOT_SUPPORTED; 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) return STATUS_NOT_FOUND;
if (!fs->Rename) return STATUS_NOT_SUPPORTED; 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) { __declspec(dllexport) KSTATUS IoCreateDir(SYS_STRING_VIEW path) {
if (!path) return STATUS_INVALID_PARAMETER; if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX]; KRESOLVED_PATH resolved;
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath)); 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) return STATUS_NOT_FOUND;
if (!fs->CreateDir) return STATUS_NOT_SUPPORTED; 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) { __declspec(dllexport) KSTATUS IoRemoveDir(SYS_STRING_VIEW path) {
if (!path) return STATUS_INVALID_PARAMETER; if ((!path.Data && path.Length) || path.Length == 0) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
char relPath[CWD_MAX]; KRESOLVED_PATH resolved;
uint8_t drive = KiResolvePath(path, relPath, sizeof(relPath)); 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) return STATUS_NOT_FOUND;
if (!fs->RemoveDir) return STATUS_NOT_SUPPORTED; if (!fs->RemoveDir) return STATUS_NOT_SUPPORTED;
return fs->RemoveDir(fs, relPath); return fs->RemoveDir(fs, resolved.Path);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -840,37 +864,44 @@ static void KiDirDestroy(OBJECT* obj) {
if (d->Fs && d->DirCtx) d->Fs->CloseDir(d->Fs, d->DirCtx); 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 (!out) return STATUS_INVALID_PARAMETER;
if (dirPath.Length && !dirPath.Data) return STATUS_INVALID_PARAMETER;
if (!KeCanWait()) return STATUS_INVALID_IRQL; if (!KeCanWait()) return STATUS_INVALID_IRQL;
// NULL or empty path means the CWD itself KRESOLVED_PATH resolved;
char relPath[CWD_MAX]; KSTATUS s;
uint8_t drive;
if (!dirPath || dirPath[0] == '\0') { if (dirPath.Length == 0) {
PROCESS* proc = KeGetCurrentProcess(); PROCESS* proc = KeGetCurrentProcess();
if (!proc) return STATUS_ACCESS_DENIED; if (!proc) return STATUS_ACCESS_DENIED;
uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock); uintptr_t flags = KeAcquireSpinlock(&proc->CwdLock);
memcpy(relPath, proc->CurrentDirectory, strlen(proc->CurrentDirectory) + 1); size_t cwdLen = proc->CurrentDirectoryLength;
drive = proc->CurrentDrive; 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); KeReleaseSpinlock(&proc->CwdLock, flags);
} else { } 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) return STATUS_NOT_FOUND;
if (!fs->OpenDir) return STATUS_NOT_SUPPORTED; if (!fs->OpenDir) return STATUS_NOT_SUPPORTED;
void* dirCtx = NULL; void* dirCtx = NULL;
KSTATUS s = fs->OpenDir(fs, relPath, &dirCtx); s = fs->OpenDir(fs, resolved.Path, &dirCtx);
if (!KSUCCESS(s)) return s; if (!KSUCCESS(s)) return s;
DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT)); DIR_OBJECT* d = (DIR_OBJECT*)ObAllocateObject(ObjectTypeDir, sizeof(DIR_OBJECT));
if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; } if (!d) { fs->CloseDir(fs, dirCtx); return STATUS_NO_MEMORY; }
d->Header.Destroy = KiDirDestroy; d->Header.Destroy = KiDirDestroy;
d->Fs = fs; d->Fs = fs;
d->DirCtx = dirCtx; d->DirCtx = dirCtx;
+16 -15
View File
@@ -7,6 +7,7 @@
#include <ke_status.h> #include <ke_status.h>
#include <ob_object.h> #include <ob_object.h>
#include <ke_spinlock.h> #include <ke_spinlock.h>
#include <sys_string.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// KDIR_ENTRY - one directory entry returned by ReadDir / SYS_READ_DIR // 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"). // Open a file by absolute path (e.g. "apps\\shell.exe").
// Returns an opaque per-file driver cookie in* fileCtx. // Returns an opaque per-file driver cookie in* fileCtx.
KSTATUS (*Open)(struct IO_FILESYSTEM* fs, 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'. // Read up to 'size' bytes at 'offset' into 'buf'.
// Sets* bytesRead to actual bytes copied. // Sets* bytesRead to actual bytes copied.
@@ -58,19 +59,19 @@ typedef struct IO_FILESYSTEM {
void* completeContext); void* completeContext);
// Create a new file at 'path'; returns an open fileCtx on success. // 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); void** fileCtx);
// Delete the file at 'path'. // 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. // Rename or move a file/directory within the same filesystem.
KSTATUS (*Rename)(struct IO_FILESYSTEM* fs, KSTATUS (*Rename)(struct IO_FILESYSTEM* fs,
const char* oldPath, const char* newPath); SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath);
// Create or remove directories. // Create or remove directories.
KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, const char* path); KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, const char* path); KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, SYS_STRING_VIEW path);
// Truncate or extend the file to exactly 'newSize' bytes. // Truncate or extend the file to exactly 'newSize' bytes.
KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx, 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). // Open a directory iterator for 'dirPath' (relative or drive-prefixed).
// Returns an opaque per-iteration driver cookie in *dirCtx. // Returns an opaque per-iteration driver cookie in *dirCtx.
KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs, KSTATUS (*OpenDir)(struct IO_FILESYSTEM* fs,
const char* dirPath, void** dirCtx); SYS_STRING_VIEW dirPath, void** dirCtx);
// Advance to the next entry. // Advance to the next entry.
// Fills 'out' with name, attributes, and size. // Fills 'out' with name, attributes, and size.
@@ -153,7 +154,7 @@ __declspec(dllexport) KSTATUS IoRegisterFilesystem(uint8_t driveIndex, IO_FILESY
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Kernel-side API // 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, __declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf,
size_t size, size_t* bytesRead); size_t size, size_t* bytesRead);
__declspec(dllexport) KSTATUS IoReadFileAt(FILE_OBJECT* file, uint64_t offset, __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 IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request);
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request, __declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* request,
IO_STATUS_BLOCK* iosbOut); IO_STATUS_BLOCK* iosbOut);
__declspec(dllexport) KSTATUS IoCreateFile (const char* path, FILE_OBJECT** out); __declspec(dllexport) KSTATUS IoCreateFile (SYS_STRING_VIEW path, FILE_OBJECT** out);
__declspec(dllexport) KSTATUS IoDeleteFile (const char* path); __declspec(dllexport) KSTATUS IoDeleteFile (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoRenameFile (const char* oldPath, const char* newPath); __declspec(dllexport) KSTATUS IoRenameFile (SYS_STRING_VIEW oldPath, SYS_STRING_VIEW newPath);
__declspec(dllexport) KSTATUS IoCreateDir (const char* path); __declspec(dllexport) KSTATUS IoCreateDir (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoRemoveDir (const char* path); __declspec(dllexport) KSTATUS IoRemoveDir (SYS_STRING_VIEW path);
__declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize); __declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset); __declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file); __declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
@@ -188,12 +189,12 @@ __declspec(dllexport) void IoCloseFile(FILE_OBJECT* file);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Directory enumeration API // 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) KSTATUS IoReadDir (DIR_OBJECT* dir, KDIR_ENTRY* entryOut);
__declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir); __declspec(dllexport) void IoCloseDir(DIR_OBJECT* dir);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Current Working Directory // 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); __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. // Release all remaining handles.
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) { for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
if (proc->Handles[i]) { if (proc->Handles[i].Object) {
ObDereferenceObject(proc->Handles[i]); ObDereferenceObject(proc->Handles[i].Object);
proc->Handles[i] = (OBJECT *)0; proc->Handles[i].Object = (OBJECT*)0;
proc->Handles[i].Rights = 0;
proc->Handles[i].Flags = 0;
} }
} }
// Release the main executable image. // Release the main executable image.
if (proc->ImageBase && proc->ImageSize) { if (proc->ImageBase && proc->ImageSize) {
MmFreeUserMemory((void *)proc->ImageBase); MmFreeUserImage((void *)proc->ImageBase, proc->ImageSize);
proc->ImageBase = 0; proc->ImageBase = 0;
proc->ImageSize = 0; proc->ImageSize = 0;
} }
@@ -104,7 +106,7 @@ static void KiProcessDestroy(OBJECT* obj) {
// Release per-process modules. // Release per-process modules.
for (uint32_t i = 0; i < proc->ModuleCount; i++) { for (uint32_t i = 0; i < proc->ModuleCount; i++) {
if (proc->Modules[i].Base && proc->Modules[i].Size) { 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].Base = 0;
proc->Modules[i].Size = 0; proc->Modules[i].Size = 0;
@@ -170,6 +172,8 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->ThreadCount = 0; proc->ThreadCount = 0;
proc->HandleLock = (SPINLOCK)SPINLOCK_INIT; proc->HandleLock = (SPINLOCK)SPINLOCK_INIT;
proc->ExitCode = 0; proc->ExitCode = 0;
proc->Terminating = 0;
proc->Exited = 0;
proc->ImageBase = 0; proc->ImageBase = 0;
proc->ImageSize = 0; proc->ImageSize = 0;
proc->UserPebBase = 0; proc->UserPebBase = 0;
@@ -182,6 +186,7 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
proc->UserCwdSize = 0; proc->UserCwdSize = 0;
proc->ModuleCount = 0; proc->ModuleCount = 0;
proc->CurrentDirectory[0] = '\0'; proc->CurrentDirectory[0] = '\0';
proc->CurrentDirectoryLength = 0;
proc->CurrentDrive = 0; proc->CurrentDrive = 0;
proc->CwdLock = (SPINLOCK)SPINLOCK_INIT; proc->CwdLock = (SPINLOCK)SPINLOCK_INIT;
@@ -208,33 +213,47 @@ KSTATUS KeCreateProcess(const char* name, PROCESS **out) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// KeSetupUserProcessEnvironment // 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 (!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); char* dst = (char*)MmAllocateUserMemory(len);
if (!dst) return STATUS_NO_MEMORY; 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; *baseOut = (uintptr_t)dst;
*sizeOut = len; *sizeOut = len;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static KSTATUS KiCopyEnvironmentStringFixed(const char* src, size_t capacity, static KSTATUS KiCopyEnvironmentStringViewFixed(SYS_STRING_VIEW src,
uintptr_t* baseOut, size_t* sizeOut) { size_t capacity,
uintptr_t* baseOut,
size_t* sizeOut) {
if (!baseOut || !sizeOut || capacity == 0) return STATUS_INVALID_PARAMETER; 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); char* dst = (char*)MmAllocateUserMemory(capacity);
if (!dst) return STATUS_NO_MEMORY; if (!dst) return STATUS_NO_MEMORY;
memset(dst, 0, capacity); memset(dst, 0, capacity);
size_t len = strlen(src); if (src.Length) memcpy(dst, src.Data, src.Length);
if (len >= capacity) len = capacity - 1; dst[src.Length] = '\0';
memcpy(dst, src, len);
dst[len] = '\0';
*baseOut = (uintptr_t)dst; *baseOut = (uintptr_t)dst;
*sizeOut = capacity; *sizeOut = capacity;
@@ -266,43 +285,68 @@ static void KiFreePartialProcessEnvironment(PROCESS* proc) {
} }
} }
static void KiFormatNativeCwd(const PROCESS* proc, const char* currentDirectory, static KSTATUS KiFormatNativeCwdV(const PROCESS* proc,
char* out, size_t outSize) { SYS_STRING_VIEW currentDirectory,
if (!out || outSize == 0) return; char* out,
const char* rel = currentDirectory ? currentDirectory : ""; 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; uint8_t drive = proc ? proc->CurrentDrive : 0;
if (drive > 7) drive = 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[0] = (char)('0' + drive);
out[1] = ':'; out[1] = ':';
out[2] = '/'; out[2] = '/';
size_t len = strlen(rel); if (currentDirectory.Length)
if (len + 4 > outSize) len = outSize - 4; memcpy(out + 3, currentDirectory.Data, currentDirectory.Length);
memcpy(out + 3, rel, len); out[3 + currentDirectory.Length] = '\0';
out[3 + len] = '\0';
if (lengthOut) *lengthOut = 3 + currentDirectory.Length;
return STATUS_SUCCESS;
} }
KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc, KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
const char* imagePath, SYS_STRING_VIEW imagePath,
const char* commandLine, SYS_STRING_VIEW commandLine,
const char* currentDirectory) { SYS_STRING_VIEW currentDirectory) {
if (!proc || !imagePath) return STATUS_INVALID_PARAMETER; 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]; 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); KiFreePartialProcessEnvironment(proc);
KSTATUS s = KiCopyEnvironmentString(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize); s = KiCopyEnvironmentStringView(imagePath, &proc->UserImagePathBase, &proc->UserImagePathSize);
if (!KSUCCESS(s)) goto fail; if (!KSUCCESS(s)) goto fail;
s = KiCopyEnvironmentString(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize); s = KiCopyEnvironmentStringView(finalCommandLine, &proc->UserCmdlineBase, &proc->UserCmdlineSize);
if (!KSUCCESS(s)) goto fail; 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; if (!KSUCCESS(s)) goto fail;
SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB)); SYS_PEB* peb = (SYS_PEB*)MmAllocateUserMemory(sizeof(SYS_PEB));
@@ -312,12 +356,9 @@ KSTATUS KeSetupUserProcessEnvironment(PROCESS* proc,
peb->Magic = SYS_PEB_MAGIC; peb->Magic = SYS_PEB_MAGIC;
peb->Version = SYS_ENV_VERSION; peb->Version = SYS_ENV_VERSION;
peb->ProcessId = proc->ProcessId; peb->ProcessId = proc->ProcessId;
peb->ImagePath = (const char*)proc->UserImagePathBase; peb->ImagePath = SysSvFromParts((const char*)proc->UserImagePathBase, imagePath.Length);
peb->ImagePathLength = proc->UserImagePathSize ? proc->UserImagePathSize - 1 : 0; peb->CommandLine = SysSvFromParts((const char*)proc->UserCmdlineBase, finalCommandLine.Length);
peb->CommandLine = (const char*)proc->UserCmdlineBase; peb->CurrentDirectory = SysSvFromParts((const char*)proc->UserCwdBase, finalCwdLength);
peb->CommandLineLength = proc->UserCmdlineSize ? proc->UserCmdlineSize - 1 : 0;
peb->CurrentDirectory = (const char*)proc->UserCwdBase;
peb->CurrentDirectoryLength = strlen(finalCwd);
proc->UserPebBase = (uintptr_t)peb; proc->UserPebBase = (uintptr_t)peb;
proc->UserPebSize = sizeof(SYS_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) { void KeExitProcess(PROCESS* process, int exitCode) {
if (!process) return; if (!process) return;
process->ExitCode = exitCode; process->ExitCode = exitCode;
// Wake any threads waiting for this process to finish. _InterlockedExchange(&process->Terminating, 1);
if (process->ExitEvent) KeSignalEvent(process->ExitEvent);
// The last thread exiting via KeExitThread will drop the process refcount. // The calling thread exits immediately after this in SysExitProcess. Do
// This function is a policy hook; actual cleanup is in KiProcessDestroy. // not drop the process object here: each thread owns one process reference,
ObDereferenceObject(&process->Header); // 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 // Handle table
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut) { KSTATUS KeAllocateHandleWithRights(PROCESS* process, OBJECT* object, uint32_t rights, uint32_t* handleOut) {
if (!process || !object || !handleOut) return STATUS_INVALID_PARAMETER; if (!process || !object || !handleOut || rights == 0) return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock); uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
for (int i = 1; i < HANDLE_TABLE_SIZE; i++) { for (int i = 1; i < HANDLE_TABLE_SIZE; i++) {
if (!process->Handles[i]) { if (!process->Handles[i].Object) {
ObReferenceObject(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; *handleOut = (uint32_t)i;
KeReleaseSpinlock(&process->HandleLock, flags); KeReleaseSpinlock(&process->HandleLock, flags);
return STATUS_SUCCESS; return STATUS_SUCCESS;
@@ -372,13 +455,19 @@ KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut)
return STATUS_NO_MEMORY; 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) { void KeCloseHandle(PROCESS* process, uint32_t handle) {
if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return; if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE) return;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock); uintptr_t flags = KeAcquireSpinlock(&process->HandleLock);
OBJECT* obj = process->Handles[handle]; OBJECT* obj = process->Handles[handle].Object;
if (obj) { 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); KeReleaseSpinlock(&process->HandleLock, flags);
ObDereferenceObject(obj); ObDereferenceObject(obj);
return; return;
@@ -386,17 +475,33 @@ void KeCloseHandle(PROCESS* process, uint32_t handle) {
KeReleaseSpinlock(&process->HandleLock, flags); 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) if (!process || handle == 0 || handle >= HANDLE_TABLE_SIZE)
return (OBJECT *)0; return (OBJECT*)0;
uintptr_t flags = KeAcquireSpinlock(&process->HandleLock); 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); if (obj) ObReferenceObject(obj);
KeReleaseSpinlock(&process->HandleLock, flags); KeReleaseSpinlock(&process->HandleLock, flags);
return obj; 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) { PROCESS* KeGetProcessById(uint32_t pid) {
KiProcessInit(); KiProcessInit();
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock); uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
+33 -7
View File
@@ -8,12 +8,27 @@
#include <ke_event.h> #include <ke_event.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <sys_string.h>
#define PROCESS_NAME_MAX 64 #define PROCESS_NAME_MAX 64
#define HANDLE_TABLE_SIZE 256 #define HANDLE_TABLE_SIZE 256
#define PROCESS_MODULE_MAX 16 #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 { typedef struct PROCESS_MODULE {
char Name[32]; char Name[32];
@@ -27,7 +42,8 @@ typedef struct PROCESS {
uint32_t ProcessId; uint32_t ProcessId;
char Name[PROCESS_NAME_MAX]; /* UTF-8 */ char Name[PROCESS_NAME_MAX]; /* UTF-8 */
char CurrentDirectory[512]; char CurrentDirectory[PROCESS_CWD_MAX];
size_t CurrentDirectoryLength;
uint8_t CurrentDrive; uint8_t CurrentDrive;
SPINLOCK CwdLock; SPINLOCK CwdLock;
@@ -36,12 +52,14 @@ typedef struct PROCESS {
uint32_t ThreadCount; uint32_t ThreadCount;
/* Handle table. /* Handle table.
Index 0 is always NULL (the invalid handle). Index 0 is always empty (the invalid handle).
Entries are OBJECT pointers, each holding a reference. */ Each live entry holds one object reference plus rights metadata. */
OBJECT* Handles[HANDLE_TABLE_SIZE]; HANDLE_ENTRY Handles[HANDLE_TABLE_SIZE];
SPINLOCK HandleLock; SPINLOCK HandleLock;
int ExitCode; int ExitCode;
volatile long Terminating;
volatile long Exited;
/* Main executable image. Used for cleanup. */ /* Main executable image. Used for cleanup. */
uintptr_t ImageBase; uintptr_t ImageBase;
@@ -70,14 +88,22 @@ typedef struct PROCESS {
KSTATUS KeCreateProcess(const char* name, PROCESS **out); KSTATUS KeCreateProcess(const char* name, PROCESS **out);
KSTATUS KeCreateSystemProcess(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); void KeExitProcess(PROCESS* process, int exitCode);
KSTATUS KeTerminateProcess(PROCESS* process, int exitCode);
int KeIsProcessTerminating(PROCESS* process);
KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut); KSTATUS KeGetProcessExitCode(PROCESS* process, int* exitCodeOut);
/* Handle table operations. */ /* Handle table operations. */
KSTATUS KeAllocateHandle(PROCESS* process, OBJECT* object, uint32_t* handleOut); 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); void KeCloseHandle(PROCESS* process, uint32_t handle);
OBJECT* KeLookupHandle(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). */ /* Return the global process list head (kernel-internal). */
LIST_ENTRY* KeGetProcessList(void); LIST_ENTRY* KeGetProcessList(void);
+31
View File
@@ -20,10 +20,18 @@ static THREAD* gIdleThread;
static THREAD* gOutgoingThread; static THREAD* gOutgoingThread;
static uint32_t gAgingCounter = 0; static uint32_t gAgingCounter = 0;
static volatile long gReschedulePending = 0; static volatile long gReschedulePending = 0;
static KE_SCHED_STATS gSchedStats;
THREAD* KeGetCurrentThread(void) { return gCurrentThread; } THREAD* KeGetCurrentThread(void) { return gCurrentThread; }
void KeSetCurrentThread(THREAD* t) { gCurrentThread = t; } 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) { static void KiIdleLoop(void) {
while (true) { __halt(); } while (true) { __halt(); }
} }
@@ -121,6 +129,14 @@ void KeUnreadyThread(THREAD* thread) {
KeReleaseSpinlock(&gSchedLock, flags); 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) { static THREAD* KiPickNext(void) {
for (int p = PriorityCount - 1; p >= 0; p--) { for (int p = PriorityCount - 1; p >= 0; p--) {
if (!LIST_IS_EMPTY(&gRunQueue[p])) { if (!LIST_IS_EMPTY(&gRunQueue[p])) {
@@ -149,7 +165,18 @@ static void KiSchedule(void) {
uintptr_t flags = KeAcquireSpinlock(&gSchedLock); uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
cur->SchedLockFlags = flags; 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(); THREAD* next = KiPickNext();
if (next == gIdleThread) gSchedStats.IdleSelections++;
if (next == cur) { if (next == cur) {
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead // Only re-queue if still running normally; a ThreadWaiting/ThreadDead
// caller has already placed SchedulerLink elsewhere (or is discarding // caller has already placed SchedulerLink elsewhere (or is discarding
@@ -174,6 +201,7 @@ static void KiSchedule(void) {
} }
gOutgoingThread = cur; gOutgoingThread = cur;
gSchedStats.ContextSwitches++;
KeSwitchContext(cur, next); KeSwitchContext(cur, next);
KeSchedulerPostSwitch(); KeSchedulerPostSwitch();
} }
@@ -190,12 +218,14 @@ void KeSchedulerTick(void) {
uintptr_t flags = KeAcquireSpinlock(&gSchedLock); uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
cur->SchedLockFlags = flags; // Issue 5 fix cur->SchedLockFlags = flags; // Issue 5 fix
gSchedStats.Ticks++;
cur->QuantaConsumed++; cur->QuantaConsumed++;
if (cur != gIdleThread && cur->QuantaConsumed >= DEMOTION_QUANTA) { if (cur != gIdleThread && cur->QuantaConsumed >= DEMOTION_QUANTA) {
cur->QuantaConsumed = 0; cur->QuantaConsumed = 0;
if (cur->Priority > PriorityLow) { if (cur->Priority > PriorityLow) {
cur->Priority = (THREAD_PRIORITY)(cur->Priority - 1); cur->Priority = (THREAD_PRIORITY)(cur->Priority - 1);
gSchedStats.Demotions++;
} }
} }
@@ -208,6 +238,7 @@ void KeSchedulerTick(void) {
LIST_ENTRY* node = ListPopHead(&gRunQueue[p]); LIST_ENTRY* node = ListPopHead(&gRunQueue[p]);
THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink); THREAD* t = CONTAINING_RECORD(node, THREAD, SchedulerLink);
t->Priority = PriorityHigh; t->Priority = PriorityHigh;
gSchedStats.AgingPromotions++;
ListInsertTail(&gRunQueue[PriorityHigh], &t->SchedulerLink); ListInsertTail(&gRunQueue[PriorityHigh], &t->SchedulerLink);
} }
} }
+45 -34
View File
@@ -1,34 +1,45 @@
#pragma once #pragma once
// ke_sched.h - Multi-Level Feedback Queue scheduler // ke_sched.h - Multi-Level Feedback Queue scheduler
// Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick. // Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick.
#include <ke_thread.h> #include <ke_thread.h>
#define QUANTUM_MS 10 // base time slice in milliseconds #define QUANTUM_MS 10 // base time slice in milliseconds
// Called once by KernelMain before any threads are created. typedef struct KE_SCHED_STATS {
void KeInitializeScheduler(void); uint64_t Ticks;
uint64_t ContextSwitches;
// Add / remove a thread from the run queues. uint64_t KeepCurrentDecisions;
void KeReadyThread(THREAD* thread); uint64_t IdleSelections;
void KeUnreadyThread(THREAD* thread); uint64_t Demotions;
uint64_t AgingPromotions;
// Called from the timer ISR every QUANTUM_MS. This only performs timer } KE_SCHED_STATS;
// accounting and requests a reschedule; the actual context switch happens from
// KiExitInterrupt after the IRQ handler has returned and EOI has been sent. // Called once by KernelMain before any threads are created.
void KeSchedulerTick(void); void KeInitializeScheduler(void);
// Internal scheduler hooks used by the interrupt-exit path. KiRequestReschedule // Add / remove a thread from the run queues.
// is cheap and can be called from interrupt context. KiScheduleFromInterrupt void KeReadyThread(THREAD* thread);
// consumes the pending request and performs the context switch, if needed. void KeUnreadyThread(THREAD* thread);
void KiRequestReschedule(void);
int KiIsReschedulePending(void); // Called from the timer ISR every QUANTUM_MS. This only performs timer
void KiScheduleFromInterrupt(void); // accounting and requests a reschedule; the actual context switch happens from
// KiExitInterrupt after the IRQ handler has returned and EOI has been sent.
// Voluntarily yield the CPU. Picks the next runnable thread immediately. void KeSchedulerTick(void);
__declspec(dllexport) void KeYield(void);
// Internal scheduler hooks used by the interrupt-exit path. KiRequestReschedule
// Returns the thread currently running on this CPU (reads GS:[0]). // is cheap and can be called from interrupt context. KiScheduleFromInterrupt
THREAD* KeGetCurrentThread(void); // consumes the pending request and performs the context switch, if needed.
void KiRequestReschedule(void);
// Set the current-thread GS slot. Called once per CPU at boot. int KiIsReschedulePending(void);
void KeSetCurrentThread(THREAD* thread); void KiScheduleFromInterrupt(void);
// Voluntarily yield the CPU. Picks the next runnable thread immediately.
__declspec(dllexport) void KeYield(void);
void KeGetSchedulerStats(KE_SCHED_STATS* out);
// Returns the thread currently running on this CPU (reads GS:[0]).
THREAD* KeGetCurrentThread(void);
// Set the current-thread GS slot. Called once per CPU at boot.
void KeSetCurrentThread(THREAD* thread);
+42 -4
View File
@@ -476,6 +476,43 @@ static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) {
} }
// -------------------------------------------------------------------------
// String-view ABI self-test
// -------------------------------------------------------------------------
static void KiRunStringViewSelfTest(void) {
DbgPrint("STRINGVIEW selftest: begin\n");
SYS_STRING_VIEW hello = SYS_SV_LITERAL("Hello");
if (hello.Length != 5 || !SysSvEqualLit(hello, "Hello"))
KePanic("STRINGVIEW selftest: literal/equality failed\n");
static const char embedded[] = { 'a', 'b', '\0', 'c', 'd' };
SYS_STRING_VIEW emb = SysSvFromParts(embedded, sizeof(embedded));
if (emb.Length != 5 || !SysSvContainsByte(emb, '\0'))
KePanic("STRINGVIEW selftest: embedded-NUL detection failed\n");
char copy[6];
if (!SysSvCopyZ(copy, sizeof(copy), emb))
KePanic("STRINGVIEW selftest: bounded copy failed\n");
if (memcmp(copy, embedded, sizeof(embedded)) != 0 || copy[5] != '\0')
KePanic("STRINGVIEW selftest: bounded copy contents wrong\n");
if (SysSvCopyZ(copy, 4, emb))
KePanic("STRINGVIEW selftest: undersized copy unexpectedly succeeded\n");
// Paths are string views at the ABI boundary, but FAT/VFS path names do not
// accept embedded NUL bytes. This must fail before reaching any filesystem.
FILE_OBJECT* f = (FILE_OBJECT*)0;
KSTATUS s = IoOpenFile(SysSvFromParts("system/init.exe\0junk", 20), &f);
if (s != STATUS_INVALID_PARAMETER)
KePanic("STRINGVIEW selftest: embedded-NUL path status=%u\n", s);
DbgPrint("STRINGVIEW selftest: sample view='%v' len=%llu\n",
hello, (uint64_t)hello.Length);
DbgPrint("STRINGVIEW selftest: PASS\n");
}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Async file I/O self-test // Async file I/O self-test
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@@ -534,7 +571,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
memset(&st, 0, sizeof(st)); memset(&st, 0, sizeof(st));
st.Iosb.Status = STATUS_FAILURE; 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)) { if (!KSUCCESS(s)) {
DbgPrint("FILE/ASYNC selftest: SKIP, system/init.exe open failed %u\n", s); DbgPrint("FILE/ASYNC selftest: SKIP, system/init.exe open failed %u\n", s);
return; return;
@@ -614,7 +651,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
wiosb.Status = STATUS_FAILURE; wiosb.Status = STATUS_FAILURE;
wiosb.Information = 0; wiosb.Information = 0;
s = IoCreateFile("asyncio.tmp", &wf); s = IoCreateFile(SYS_SV_LITERAL("asyncio.tmp"), &wf);
if (!KSUCCESS(s)) if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: create write temp failed %u\n", s); KePanic("FILE/ASYNC selftest: create write temp failed %u\n", s);
@@ -651,7 +688,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
IoCloseFile(wf); IoCloseFile(wf);
wf = (FILE_OBJECT*)0; wf = (FILE_OBJECT*)0;
s = IoOpenFile("asyncio.tmp", &rf); s = IoOpenFile(SYS_SV_LITERAL("asyncio.tmp"), &rf);
if (!KSUCCESS(s)) if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: reopen write temp failed %u\n", s); KePanic("FILE/ASYNC selftest: reopen write temp failed %u\n", s);
@@ -664,7 +701,7 @@ static void KiRunAsyncFileIoSelfTest(void) {
IoCloseFile(rf); IoCloseFile(rf);
ObDereferenceObject(&wev->Header); ObDereferenceObject(&wev->Header);
s = IoDeleteFile("asyncio.tmp"); s = IoDeleteFile(SYS_SV_LITERAL("asyncio.tmp"));
if (!KSUCCESS(s)) if (!KSUCCESS(s))
KePanic("FILE/ASYNC selftest: delete temp failed %u\n", 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) { void KiRunKernelSelfTests(const HAL_API* hal) {
DbgPrint("Kernel selftests: begin\n"); DbgPrint("Kernel selftests: begin\n");
KiRunKernelApcSelfTest(); KiRunKernelApcSelfTest();
KiRunStringViewSelfTest();
KiRunIrpKernelApcSelfTest(hal); KiRunIrpKernelApcSelfTest(hal);
KiRunAsyncFileIoSelfTest(); KiRunAsyncFileIoSelfTest();
KiRunTrapFrameSelfTest(); KiRunTrapFrameSelfTest();
+1 -1
View File
@@ -54,7 +54,7 @@ __declspec(dllexport) KSTATUS KeGetSettingString(const char* section,
buffer[0] = '\0'; buffer[0] = '\0';
FILE_OBJECT* file = NULL; FILE_OBJECT* file = NULL;
KSTATUS st = IoOpenFile(KE_SETTINGS_PATH, &file); KSTATUS st = IoOpenFile(SYS_SV_LITERAL(KE_SETTINGS_PATH), &file);
if (!KSUCCESS(st)) if (!KSUCCESS(st))
return st; return st;
+356 -195
View File
File diff suppressed because it is too large. Load diff
+18 -7
View File
@@ -40,11 +40,20 @@ static void KiThreadDestroy(OBJECT* obj) {
} }
if (thread->Owner) { 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); ListRemove(&thread->ProcessLink);
thread->Owner->ThreadCount--; if (owner->ThreadCount) owner->ThreadCount--;
KeReleaseSpinlock(&thread->Owner->HandleLock, flags); remaining = owner->ThreadCount;
ObDereferenceObject(&thread->Owner->Header); 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; thread->Owner = (PROCESS *)0;
} }
@@ -104,9 +113,11 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
return STATUS_NO_MEMORY; return STATUS_NO_MEMORY;
} }
thread->UserStackBase = ustack; thread->UserStackBase = ustack;
uintptr_t userStackTop = (uintptr_t)ustack + USTACK_SIZE - 8; // i don't know why, i don't want to know why // Ring-3 starts at the image entry via IRET, not CALL, so there is no
// i shouldn't have to wonder why but the stack is off by 8 bytes unless we do this terribleness // pushed return address. The x64 ABI expects RSP % 16 == 8 at function
// this magic number will hunt me in my dreams // 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)); SYS_TEB* teb = (SYS_TEB*)MmAllocateUserMemory(sizeof(SYS_TEB));
if (!teb) { if (!teb) {
+14 -8
View File
@@ -4,25 +4,24 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <sys_string.h>
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */ #define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */ #define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
#define SYS_ENV_VERSION 1u #define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 520u #define SYS_CWD_MAX 520u
#ifndef SYS_USER_ENV_TYPES_DEFINED
#define SYS_USER_ENV_TYPES_DEFINED 1
typedef struct SYS_PEB { typedef struct SYS_PEB {
uint32_t Magic; uint32_t Magic;
uint32_t Version; uint32_t Version;
uint32_t ProcessId; uint32_t ProcessId;
const char* ImagePath; SYS_STRING_VIEW ImagePath;
size_t ImagePathLength; SYS_STRING_VIEW CommandLine;
SYS_STRING_VIEW CurrentDirectory;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
} SYS_PEB; } SYS_PEB;
typedef struct SYS_TEB { typedef struct SYS_TEB {
@@ -32,4 +31,11 @@ typedef struct SYS_TEB {
struct SYS_TEB* Self; struct SYS_TEB* Self;
uint32_t ThreadId; uint32_t ThreadId;
uint32_t Magic; 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; } 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; KSTATUS s;
FILE_OBJECT* f; FILE_OBJECT* f;
s = IoOpenFile("drivers\\drivers.ini", &f); s = IoOpenFile(SYS_SV_LITERAL("drivers\\drivers.ini"), &f);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
KePanic("KiInitializeDrivers: Failed to open drivers.ini (status %d)\n", s); 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, "drivers\\", 8);
memcpy(path + 8, runtimeKeys[i], len + 1); memcpy(path + 8, runtimeKeys[i], len + 1);
s = IoOpenFile(path, &f); s = IoOpenFile(SysSvFromCstr(path), &f);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
DbgPrint("KiInitializeDrivers: Failed to open runtime driver: %s (%s)\n", runtimeKeys[i], path); DbgPrint("KiInitializeDrivers: Failed to open runtime driver: %s (%s)\n", runtimeKeys[i], path);
continue; // Skip this driver but keep going with others 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; FILE_OBJECT* f;
// Read the executable file into a raw buffer. // Read the executable file into a raw buffer.
s = IoOpenFile(filePath, &f); s = IoOpenFile(SysSvFromCstr(filePath), &f);
DbgPrint("KiLaunchUserProcess: IoOpenFile: %d\n", s); DbgPrint("KiLaunchUserProcess: IoOpenFile: %d\n", s);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
DbgPrint("Failed to load file: \"%s\"!\n", filePath); 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); 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)) { if (!KSUCCESS(s)) {
DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s); DbgPrint("KiLaunchUserProcess: Failed to build user process environment. Status: %d\n", s);
MmFreePool(mem); MmFreePool(mem);
@@ -344,7 +347,7 @@ static PROCESS* KiLaunchUserProcess(const char* filePath, const char* args) {
s = LdrResolveImportsForProcess(proc, imageBase); s = LdrResolveImportsForProcess(proc, imageBase);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("KiLaunchUserProcess: Failed to resolve EXE imports for %s (status %d)\n", filePath, 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); ObDereferenceObject(&proc->Header);
return 0; 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); DbgPrint("KiLaunchUserProcess: Failed to create thread. Status: %d\n", s);
proc->ImageBase = 0; proc->ImageBase = 0;
proc->ImageSize = 0; proc->ImageSize = 0;
MmFreeUserMemory((void*)imageBase); MmFreeUserImage((void*)imageBase, LdrGetImageSize(imageBase));
ObDereferenceObject(&proc->Header); ObDereferenceObject(&proc->Header);
return 0; return 0;
} }
+52 -6
View File
@@ -107,6 +107,10 @@ typedef struct {
#pragma pack(pop) #pragma pack(pop)
#define IMAGE_REL_BASED_DIR64 10 #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 // Module registry
@@ -248,6 +252,40 @@ size_t LdrGetImageSize(uintptr_t base) {
return (size_t)pe->OptionalHeader.SizeOfImage; 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 // Entry point lookup
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -470,13 +508,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
} }
FILE_OBJECT* f; FILE_OBJECT* f;
KSTATUS s = IoOpenFile(filename, &f); KSTATUS s = IoOpenFile(SysSvFromCstr(filename), &f);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
char path[260]; char path[260];
memcpy(path, "0:/system/", 10); memcpy(path, "0:/system/", 10);
memcpy(path + 10, filename, strnlen(filename, 248) + 1); memcpy(path + 10, filename, strnlen(filename, 248) + 1);
path[259] = '\0'; path[259] = '\0';
s = IoOpenFile(path, &f); s = IoOpenFile(SysSvFromCstr(path), &f);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s); DbgPrint("LdrLoadModuleForProcess: Failed to open %s (%d)\n", filename, s);
return s; return s;
@@ -509,7 +547,13 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
s = LdrResolveImportsForProcess(process, imageBase); s = LdrResolveImportsForProcess(process, imageBase);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase); KiFreeLoadedImage(imageBase, true);
return s;
}
s = LdrProtectImage(imageBase, true);
if (!KSUCCESS(s)) {
KiFreeLoadedImage(imageBase, true);
return s; return s;
} }
@@ -517,7 +561,7 @@ KSTATUS LdrLoadModuleForProcess(PROCESS* process, const char* filename, uintptr_
s = LdrRegisterProcessModule(process, filename, imageBase); s = LdrRegisterProcessModule(process, filename, imageBase);
if (!KSUCCESS(s)) { if (!KSUCCESS(s)) {
MmFreeUserMemory((void*)imageBase); KiFreeLoadedImage(imageBase, true);
return s; return s;
} }
@@ -561,7 +605,7 @@ KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool u
OPT_HEADER64* opt = &pe->OptionalHeader; OPT_HEADER64* opt = &pe->OptionalHeader;
size_t imageSize = opt->SizeOfImage; 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; if (!mem) return STATUS_NO_MEMORY;
memset(mem, 0, imageSize); 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); 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; uintptr_t delta = (uintptr_t)mem - opt->ImageBase;
+41 -38
View File
@@ -1,38 +1,41 @@
#pragma once #pragma once
// ldr_image.h - minimal PE32+ loader // ldr_image.h - minimal PE32+ loader
// Loads an already-mapped PE image and resolves its imports. // Loads an already-mapped PE image and resolves its imports.
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <ke_status.h> #include <ke_status.h>
// Map a PE32+ image from 'buffer' into the kernel virtual address space. // Map a PE32+ image from 'buffer' into the kernel virtual address space.
// On success, *baseOut receives the virtual base address. // On success, *baseOut receives the virtual base address.
KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user); KSTATUS LdrLoadImage(const void* buffer, size_t size, uintptr_t* baseOut, bool user);
// Return the SizeOfImage recorded in the PE header for an already-mapped image. // Return the SizeOfImage recorded in the PE header for an already-mapped image.
size_t LdrGetImageSize(uintptr_t base); size_t LdrGetImageSize(uintptr_t base);
// Look up a named export in a loaded module. // Look up a named export in a loaded module.
// 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll"). // 'moduleName' must match a name in BOOT_INFO.Modules (e.g., "hal.dll").
void* LdrGetExport(const char* moduleName, const char* functionName); void* LdrGetExport(const char* moduleName, const char* functionName);
// Get the entry point address of a loaded image. // Get the entry point address of a loaded image.
uintptr_t LdrGetEntryPoint(uintptr_t base); uintptr_t LdrGetEntryPoint(uintptr_t base);
// Resolve the import table of an already-loaded image using the global registry. // Resolve the import table of an already-loaded image using the global registry.
KSTATUS LdrResolveImports(uintptr_t imageBase); KSTATUS LdrResolveImports(uintptr_t imageBase);
// Resolve imports using a process-local module registry. // Resolve imports using a process-local module registry.
struct PROCESS; struct PROCESS;
KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase); KSTATUS LdrResolveImportsForProcess(struct PROCESS* process, uintptr_t imageBase);
// Load a DLL for a specific process, resolve it against that process's // Apply final page permissions for a loaded image after relocations/imports.
// loaded modules, and register it in the process-local registry. KSTATUS LdrProtectImage(uintptr_t imageBase, bool user);
KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
// Load a DLL for a specific process, resolve it against that process's
// Called by KernelMain to register all modules from BOOT_INFO so that // loaded modules, and register it in the process-local registry.
// LdrGetExport can find them. KSTATUS LdrLoadModuleForProcess(struct PROCESS* process, const char* filename, uintptr_t* baseOut);
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo); // Called by KernelMain to register all modules from BOOT_INFO so that
void LdrRegisterModule(const char* name, uintptr_t base); // LdrGetExport can find them.
struct BOOT_INFO; // forward declare for the compiler to shut the fuck up
void LdrRegisterModulesFromBootInfo(const struct BOOT_INFO* bootInfo);
void LdrRegisterModule(const char* name, uintptr_t base);
+65 -24
View File
@@ -290,29 +290,6 @@ __declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, s
return STATUS_SUCCESS; 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) // Boundary-tag allocator (used for both pool and user heap)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -595,6 +572,9 @@ static HEAP gPoolHeap;
// User heap // User heap
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static HEAP gUserHeap; 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 // Module init
@@ -621,8 +601,15 @@ void MmInitializeVirtual(PHYSICAL_ADDRESS bootPml4Phys, size_t poolSize) {
gUserHeap.Start = USER_BASE; gUserHeap.Start = USER_BASE;
gUserHeap.Brk = USER_BASE; gUserHeap.Brk = USER_BASE;
gUserHeap.Limit = USER_LIMIT; 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; 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) { void MmActivate(void) {
@@ -654,6 +641,60 @@ __declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize) {
return KiHeapRealloc(&gUserHeap, ptr, 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) { size_t MmGetKernelPoolUsage(void) {
// Returns bytes committed to the kernel pool // Returns bytes committed to the kernel pool
return (size_t)(gPoolHeap.Brk - gPoolHeap.Start); 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 CopyFromUser(void* kernelDst, const void* userSrc, size_t size);
__declspec(dllexport) KSTATUS CopyToUser(void* userDst, const void* kernelSrc, 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. // Kernel pool - ring 0 only, never PAGE_USER.
// Best-fit free-list allocator, thread-safe via spinlock. // 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. // 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* MmAllocateUserMemory(size_t size);
__declspec(dllexport) void MmFreeUserMemory(void* ptr); __declspec(dllexport) void MmFreeUserMemory(void* ptr);
__declspec(dllexport) void* MmReallocateUserMemory(void* ptr, size_t newSize); __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 MmGetKernelPoolUsage(void);
size_t MmGetUserHeapUsage(void); size_t MmGetUserHeapUsage(void);
+5 -5
View File
@@ -12,11 +12,12 @@
#define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES) #define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES)
static void print(const char* s) { 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) { 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) { 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; SYS_RESOLVE_HOST_REQUEST req;
memset(&resolved, 0, sizeof(resolved)); memset(&resolved, 0, sizeof(resolved));
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.Name = host; req.Name = SysSvFromCstr(host);
req.NameLength = strlen(host);
req.Family = SYS_AF_INET; req.Family = SYS_AF_INET;
req.AddressOut = &resolved; req.AddressOut = &resolved;
req.AddressSizeInOut = &resolvedSize; req.AddressSizeInOut = &resolvedSize;
@@ -218,7 +218,7 @@ int main(void) {
uint32_t count = PING_DEFAULT_COUNT; uint32_t count = PING_DEFAULT_COUNT;
strcpy(host, PING_DEFAULT_TARGET); 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 cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) { if (cmd) {
const char* rest = next_token(cmd, tok, sizeof(tok)); 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) { __declspec(dllexport) int chdir(const char* path) {
char native[512]; char native[512];
PosixTranslatePath(path, native, sizeof(native)); 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; } if (s != 0) { errno = ENOENT; return -1; }
return 0; return 0;
} }
@@ -26,7 +27,10 @@ __declspec(dllexport) int chdir(const char* path) {
__declspec(dllexport) char* getcwd(char* buf, size_t size) { __declspec(dllexport) char* getcwd(char* buf, size_t size) {
if (!buf || size == 0) { errno = EINVAL; return (char*)0; } if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
char native[512]; 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; } if (s != 0) { errno = ERANGE; return (char*)0; }
NativeToPosix(native, buf, size); NativeToPosix(native, buf, size);
return buf; return buf;
@@ -36,7 +40,8 @@ __declspec(dllexport) DIR* opendir(const char* path) {
char native[512]; char native[512];
PosixTranslatePath(path, native, sizeof(native)); PosixTranslatePath(path, native, sizeof(native));
uint32_t kh; 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; } if (s != 0) { errno = ENOENT; return (DIR*)0; }
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack. // 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 // posix_fd.c - file descriptor table
#include <syscall.h>
#include "posix.h" #include "posix.h"
#include "posix_fd.h" #include "posix_fd.h"
#include <syscall.h>
int errno = 0; int errno = 0;
+9 -5
View File
@@ -33,11 +33,12 @@ __declspec(dllexport) int open(const char* path, int flags, ...) {
} }
uint32_t kh = 0; uint32_t kh = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(p), &kh };
uint32_t s; uint32_t s;
if (flags & O_WRONLY) { if (flags & O_WRONLY) {
s = SysCreateFile(p, &kh); s = SysCreateFile(&req);
} else { } else {
s = SysOpenFile(p, &kh); s = SysOpenFile(&req);
} }
if (s != 0) { errno = PosixStatusToErrno(s); return -1; } 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; if (!e) return -1;
switch (e->Type) { switch (e->Type) {
case FD_CONSOLE_W: case FD_CONSOLE_W: {
SysWriteConsole((const char*)buf, count); SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts((const char*)buf, count) };
SysWriteConsole(&req);
return (ssize_t)count; return (ssize_t)count;
}
case FD_DEVNULL: case FD_DEVNULL:
case FD_DEVZERO: case FD_DEVZERO:
return (ssize_t)count; return (ssize_t)count;
@@ -143,7 +146,8 @@ __declspec(dllexport) int unlink(const char* path) {
return -1; 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; } if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
return 0; return 0;
} }
+4 -3
View File
@@ -15,8 +15,8 @@ static SYS_PEB* PosixPeb(void) {
__declspec(dllexport) const char* posix_get_command_line(void) { __declspec(dllexport) const char* posix_get_command_line(void) {
SYS_PEB* peb = PosixPeb(); SYS_PEB* peb = PosixPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return ""; if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
return peb->CommandLine; return peb->CommandLine.Data;
} }
// pid -> proc handle table (user-mode side, for waitpid) // 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 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; } if (s != 0) { errno = ENOENT; return -1; }
uint32_t kpid = 0; 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); size_t size = sizeof(*out);
SYS_RESOLVE_HOST_REQUEST req; SYS_RESOLVE_HOST_REQUEST req;
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.Name = name; req.Name = SysSvFromCstr(name);
req.NameLength = strlen(name);
req.Family = SYS_AF_INET; req.Family = SYS_AF_INET;
req.AddressOut = out; req.AddressOut = out;
req.AddressSizeInOut = &size; req.AddressSizeInOut = &size;
+952 -920
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -4,11 +4,12 @@
#include <syscall.h> #include <syscall.h>
static void print(const char* s) { 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) { 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) { static void print_u64(uint64_t v) {
@@ -43,7 +44,7 @@ static void print_ipv4(const uint8_t ip[4]) {
int main(void) { int main(void) {
char name[256]; char name[256];
const char* cmd = SysGetPeb()->CommandLine; const char* cmd = SysGetPeb()->CommandLine.Data;
cmd = next_token(cmd, name, sizeof(name)); // program path cmd = next_token(cmd, name, sizeof(name)); // program path
(void)cmd; (void)cmd;
@@ -60,8 +61,7 @@ int main(void) {
SYS_RESOLVE_HOST_REQUEST req; SYS_RESOLVE_HOST_REQUEST req;
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.Name = name; req.Name = SysSvFromCstr(name);
req.NameLength = strlen(name);
req.Family = SYS_AF_INET; req.Family = SYS_AF_INET;
req.AddressOut = &addr; req.AddressOut = &addr;
req.AddressSizeInOut = &addrSize; req.AddressSizeInOut = &addrSize;
+38 -8
View File
@@ -10,7 +10,37 @@
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Output helpers // 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) { static void printUInt(uint32_t n) {
char buf[12]; int i = 0; char buf[12]; int i = 0;
@@ -101,7 +131,7 @@ static void cmd_write(const char* args) {
filename[i] = '\0'; filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; } if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
uint32_t hFile; 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; size_t written;
SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written); SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written);
SysCloseHandle(hFile); SysCloseHandle(hFile);
@@ -112,7 +142,7 @@ static void cmd_dir(const char* path) {
uint32_t hDir; uint32_t hDir;
const char* target = (path && *path != '\0') ? path : "."; 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"); print("dir: failed to open directory\n");
return; return;
} }
@@ -142,7 +172,7 @@ static void cmd_dir(const char* path) {
static void cmd_type(const char* filename) { static void cmd_type(const char* filename) {
uint32_t hFile; 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; char buffer[512]; size_t bytesRead;
while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) { while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) {
buffer[bytesRead] = '\0'; print(buffer); 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, "mem") == 0) { cmd_mem(); return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); 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, "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, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); 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, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); 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; if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
uint32_t hProc; uint32_t hProc;
if (SysCreateProcess(program, &hProc, cmd) == 0) { if (create_process(program, &hProc, cmd) == 0) {
SysWaitForProcess(hProc); SysWaitForProcess(hProc);
SysCloseHandle(hProc); SysCloseHandle(hProc);
while (SysKbPoll()) (void)SysKbReadChar(); while (SysKbPoll()) (void)SysKbReadChar();
@@ -201,7 +231,7 @@ static int runCommand(const char* cmd) {
} }
int main(void) { int main(void) {
const char* cmdline = SysGetPeb()->CommandLine; const char* cmdline = SysGetPeb()->CommandLine.Data;
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: command line: "); print("shell: command line: ");
+1 -1
View File
@@ -165,7 +165,7 @@ static int runCommand(const char* cmd) {
int main(void) { int main(void) {
PosixFdInit(); PosixFdInit();
const char* cmdline = SysGetPeb()->CommandLine; const char* cmdline = SysGetPeb()->CommandLine.Data;
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: command line: "); 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 <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <intrin.h> #include <intrin.h>
#include "sys_string.h"
#define SYS_STATUS_SUCCESS 0u #define SYS_STATUS_SUCCESS 0u
#define SYS_STATUS_TIMEOUT 3u #define SYS_STATUS_TIMEOUT 3u
@@ -25,19 +26,17 @@
#define SYS_ENV_VERSION 1u #define SYS_ENV_VERSION 1u
#define SYS_CWD_MAX 520u #define SYS_CWD_MAX 520u
#ifndef SYS_USER_ENV_TYPES_DEFINED
#define SYS_USER_ENV_TYPES_DEFINED 1
typedef struct SYS_PEB { typedef struct SYS_PEB {
uint32_t Magic; uint32_t Magic;
uint32_t Version; uint32_t Version;
uint32_t ProcessId; uint32_t ProcessId;
const char* ImagePath; SYS_STRING_VIEW ImagePath;
size_t ImagePathLength; SYS_STRING_VIEW CommandLine;
SYS_STRING_VIEW CurrentDirectory;
const char* CommandLine;
size_t CommandLineLength;
const char* CurrentDirectory;
size_t CurrentDirectoryLength;
} SYS_PEB; } SYS_PEB;
typedef struct SYS_TEB { typedef struct SYS_TEB {
@@ -47,8 +46,15 @@ typedef struct SYS_TEB {
struct SYS_TEB* Self; struct SYS_TEB* Self;
uint32_t ThreadId; uint32_t ThreadId;
uint32_t Magic; 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; } SYS_TEB;
#endif // SYS_USER_ENV_TYPES_DEFINED
static inline SYS_PEB* SysGetPeb(void) { static inline SYS_PEB* SysGetPeb(void) {
return (SYS_PEB*)(uintptr_t)__readgsqword(0); return (SYS_PEB*)(uintptr_t)__readgsqword(0);
} }
@@ -124,12 +130,64 @@ typedef struct SYS_TRAP_FRAME_INFO {
uint64_t R11; uint64_t R11;
} SYS_TRAP_FRAME_INFO; } 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) // Process & thread lifecycle (0-14)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
void SysExitProcess(int exitCode); void SysExitProcess(int exitCode);
void SysExitThread(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 SysWaitForProcess(uint32_t handle);
uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut); uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
uint32_t SysKillProcess(uint32_t pid, int exitCode); 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 SysQueueCreate(uint32_t* handleOut);
uint32_t SysQueueSend(uint32_t handle, const void* msg, bool blocking); uint32_t SysQueueSend(uint32_t handle, const void* msg, bool blocking);
uint32_t SysQueueRecv(uint32_t handle, 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 SysPublishQueue(const SYS_PUBLISH_QUEUE_REQUEST* request);
uint32_t SysOpenNamedQueue(const char* name, uint32_t* handleOut); uint32_t SysOpenNamedQueue(const SYS_OPEN_NAMED_QUEUE_REQUEST* request);
uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut); 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) // Files, directories, and current directory (39-56)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
uint32_t SysOpenFile(const char* path, uint32_t* handleOut); uint32_t SysOpenFile(const SYS_PATH_HANDLE_REQUEST* request);
uint32_t SysCreateFile(const char* path, uint32_t* handleOut); 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 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 SysWriteFile(uint32_t handle, const void* buf, size_t size, size_t* written);
uint32_t SysReadFileAsync(const SYS_ASYNC_FILE_REQUEST* request); 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 SysGetFilePos(uint32_t handle);
uint64_t SysGetFileSize(uint32_t handle); uint64_t SysGetFileSize(uint32_t handle);
uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize); uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
uint32_t SysDeleteFile(const char* path); uint32_t SysDeleteFile(const SYS_PATH_REQUEST* request);
uint32_t SysRenameFile(const char* oldPath, const char* newPath); 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 SysReadDir(uint32_t handle, void* dirEntryBuffer);
uint32_t SysCreateDir(const char* path); uint32_t SysCreateDir(const SYS_PATH_REQUEST* request);
uint32_t SysRemoveDir(const char* path); uint32_t SysRemoveDir(const SYS_PATH_REQUEST* request);
uint32_t SysSetCwd(const char* path); uint32_t SysSetCwd(const SYS_PATH_REQUEST* request);
uint32_t SysGetCwd(char* buf, size_t size); uint32_t SysGetCwd(const SYS_GET_CWD_REQUEST* request);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Console and graphics (57-61) // 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 SysScreenBlit(int x, int y, int w, int h, const uint32_t* pixels);
uint32_t SysScreenClear(uint32_t color); uint32_t SysScreenClear(uint32_t color);
uint32_t SysScreenGetSize(int* widthOut, int* heightOut); uint32_t SysScreenGetSize(int* widthOut, int* heightOut);
@@ -286,8 +344,7 @@ typedef struct SYS_SOCKET_RECV_FROM_REQUEST {
typedef struct SYS_RESOLVE_HOST_REQUEST { typedef struct SYS_RESOLVE_HOST_REQUEST {
const char* Name; SYS_STRING_VIEW Name;
size_t NameLength;
uint32_t Family; uint32_t Family;
uint32_t Flags; uint32_t Flags;
SYS_SOCKET_ADDRESS_IPV4* AddressOut; SYS_SOCKET_ADDRESS_IPV4* AddressOut;
@@ -333,7 +390,7 @@ typedef struct SYS_MEMORY_STATUS {
// Time and diagnostics // Time and diagnostics
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
uint64_t SysGetTick(void); 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 SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info);
uint32_t SysGetMemoryStatus(SYS_MEMORY_STATUS* statusOut); uint32_t SysGetMemoryStatus(SYS_MEMORY_STATUS* statusOut);
+4 -3
View File
@@ -5,11 +5,12 @@
#include <syscall.h> #include <syscall.h>
static void print(const char* s) { 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) { 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) { static void print_u64(uint64_t v) {
@@ -63,7 +64,7 @@ static void usage(void) {
int main(void) { int main(void) {
uint16_t port = 8080; uint16_t port = 8080;
const char* cmd = SysGetPeb()->CommandLine; const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[64]; char tok[64];
cmd = next_token(cmd, tok, sizeof(tok)); // program path cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) { if (cmd) {
+4 -3
View File
@@ -5,11 +5,12 @@
#include <syscall.h> #include <syscall.h>
static void print(const char* s) { 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) { 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) { static void print_u64(uint64_t v) {
@@ -211,7 +212,7 @@ static int run_listen(const char* cmd) {
} }
int main(void) { int main(void) {
const char* cmd = SysGetPeb()->CommandLine; const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[64]; char tok[64];
cmd = next_token(cmd, tok, sizeof(tok)); // program path cmd = next_token(cmd, tok, sizeof(tok)); // program path
cmd = next_token(cmd, tok, sizeof(tok)); // verb 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 // - HeapAlloc / HeapFree / HeapReAlloc ignore the HANDLE heap argument and
// always use the single kernel user-heap. This is fine: we don't expose // 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. // multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - GetLastError / SetLastError are per-thread on Windows. We keep a single // - Win32 per-thread state lives behind SYS_TEB::SubsystemData so the native
// global for now; enough for Bink and similar single-threaded guests. // TEB ABI stays subsystem-neutral.
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE / // - 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 // SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
// SysCloseFile). // 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) { static BOOL KiFileExistsA(const char* path) {
if (!path || !*path) return FALSE; if (!path || !*path) return FALSE;
char native[MAX_PATH]; char native[MAX_PATH];
TranslatePath(path, native); TranslatePath(path, native);
uint32_t h = 0; uint32_t h = 0;
if (SysOpenFile(native, &h) != 0) return FALSE; if (K32OpenFileNative(native, &h) != 0) return FALSE;
SysCloseHandle(h); SysCloseHandle(h);
return TRUE; return TRUE;
} }
@@ -268,7 +348,7 @@ __declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
// Debug output // Debug output
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
__declspec(dllexport) void OutputDebugStringA(const char* str) { __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) { if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
// Open the file via the read path (it must already exist). // Open the file via the read path (it must already exist).
uint32_t kh = 0; 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 (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
if (disposition == TRUNCATE_EXISTING && wantWrite) { 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, // Create-or-replace: try to create; if the file already exists,
// open it and truncate it to zero instead. // open it and truncate it to zero instead.
uint32_t kh = 0; uint32_t kh = 0;
uint32_t s = SysCreateFile(translatedName, &kh); uint32_t s = K32CreateFileNative(translatedName, &kh);
if (s != 0) { if (s != 0) {
// File may already exist - open and truncate. // File may already exist - open and truncate.
s = SysOpenFile(translatedName, &kh); s = K32OpenFileNative(translatedName, &kh);
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; } if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
SysTruncateFile(kh, 0); SysTruncateFile(kh, 0);
} }
@@ -542,8 +622,8 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
if (disposition == OPEN_ALWAYS && wantWrite) { if (disposition == OPEN_ALWAYS && wantWrite) {
// Open if exists, create if not. // Open if exists, create if not.
uint32_t kh = 0; uint32_t kh = 0;
if (SysOpenFile(translatedName, &kh) != 0) { if (K32OpenFileNative(translatedName, &kh) != 0) {
if (SysCreateFile(translatedName, &kh) != 0) { if (K32CreateFileNative(translatedName, &kh) != 0) {
gLastError = 5; gLastError = 5;
return INVALID_HANDLE_VALUE; 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, // Fail if the file already exists (we can't check atomically,
// but SysCreateFile returns NAME_COLLISION for existing files). // but SysCreateFile returns NAME_COLLISION for existing files).
uint32_t kh = 0; 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 if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
return KH_TO_HANDLE(kh); return KH_TO_HANDLE(kh);
} }
@@ -677,7 +757,7 @@ __declspec(dllexport) BOOL DeleteFileA(const char* lpFileName) {
char translatedPath[MAX_PATH]; char translatedPath[MAX_PATH];
TranslatePath(lpFileName, translatedPath); TranslatePath(lpFileName, translatedPath);
uint32_t s = SysDeleteFile(translatedPath); uint32_t s = K32DeleteFileNative(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; } if (s != 0) { gLastError = 2; return FALSE; }
return TRUE; return TRUE;
} }
@@ -747,15 +827,13 @@ __declspec(dllexport) HANDLE GetCurrentThread(void) {
__declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); } __declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); }
__declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); } __declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); }
// Tiny TLS table; enough for MSVCRT/TCC compatibility paths that probe TLS. // Tiny TLS index allocator; slot values are stored per thread in WIN32_THREAD_ENV.
#define WIN32_TLS_SLOTS 64
static void* gTlsSlots[WIN32_TLS_SLOTS];
static uint64_t gTlsBitmap = 1; // reserve slot 0 static uint64_t gTlsBitmap = 1; // reserve slot 0
__declspec(dllexport) DWORD TlsAlloc(void) { __declspec(dllexport) DWORD TlsAlloc(void) {
for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) { for (DWORD i = 1; i < WIN32_TLS_SLOTS; i++) {
uint64_t bit = 1ULL << 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; gLastError = 8;
return 0xFFFFFFFFUL; return 0xFFFFFFFFUL;
@@ -763,20 +841,23 @@ __declspec(dllexport) DWORD TlsAlloc(void) {
__declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) { __declspec(dllexport) BOOL TlsFree(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; } 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); gTlsBitmap &= ~(1ULL << dwTlsIndex);
return TRUE; return TRUE;
} }
__declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) { __declspec(dllexport) void* TlsGetValue(DWORD dwTlsIndex) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; } if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return NULL; }
WIN32_THREAD_ENV* env = K32ThreadEnv();
gLastError = 0; gLastError = 0;
return gTlsSlots[dwTlsIndex]; return env->TlsSlots[dwTlsIndex];
} }
__declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) { __declspec(dllexport) BOOL TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue) {
if (dwTlsIndex >= WIN32_TLS_SLOTS || !(gTlsBitmap & (1ULL << dwTlsIndex))) { gLastError = 87; return FALSE; } 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; return TRUE;
} }
@@ -797,7 +878,7 @@ static BOOL KiIsWin32DllNameA(const char* name) {
} }
static void KiDbgWriteA(const char* s) { 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) { __declspec(dllexport) HANDLE GetModuleHandleA(const char* lpModuleName) {
@@ -856,8 +937,8 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
if (!lpFilename || !nSize) return 0; if (!lpFilename || !nSize) return 0;
SYS_PEB* peb = Win32Peb(); SYS_PEB* peb = Win32Peb();
const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath) const char* src = (peb && peb->Magic == SYS_PEB_MAGIC && peb->ImagePath.Data)
? peb->ImagePath ? peb->ImagePath.Data
: ""; : "";
DWORD len = (DWORD)strlen(src); DWORD len = (DWORD)strlen(src);
@@ -898,8 +979,8 @@ __declspec(dllexport) void GetStartupInfoA(STARTUPINFOA* lpStartupInfo) {
__declspec(dllexport) char* GetCommandLineA(void) { __declspec(dllexport) char* GetCommandLineA(void) {
SYS_PEB* peb = Win32Peb(); SYS_PEB* peb = Win32Peb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine) return ""; if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
return (char*)peb->CommandLine; return (char*)peb->CommandLine.Data;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -1025,11 +1106,11 @@ __declspec(dllexport) BOOL CreateProcessA(
if (lpCurrentDirectory && lpCurrentDirectory[0]) { if (lpCurrentDirectory && lpCurrentDirectory[0]) {
char translatedCwd[MAX_PATH]; 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; } if (gs != 0) { gLastError = 3; return FALSE; }
TranslatePath(lpCurrentDirectory, translatedCwd); TranslatePath(lpCurrentDirectory, translatedCwd);
gs = SysSetCwd(translatedCwd); gs = K32SetCwdNative(translatedCwd);
if (gs != 0) { gLastError = 3; return FALSE; } if (gs != 0) { gLastError = 3; return FALSE; }
changedCwd = true; changedCwd = true;
} }
@@ -1038,7 +1119,7 @@ __declspec(dllexport) BOOL CreateProcessA(
// Call the syscall.dll stub. The kernel snapshots the current CWD into // Call the syscall.dll stub. The kernel snapshots the current CWD into
// the child process, so the temporary lpCurrentDirectory switch can be // the child process, so the temporary lpCurrentDirectory switch can be
// restored immediately after creation. // restored immediately after creation.
uint32_t s = SysCreateProcess(translatedPath, &kh, cmdline); uint32_t s = K32CreateProcessNative(translatedPath, &kh, cmdline);
if (changedCwd) { if (changedCwd) {
if (savedCwd[0]) { if (savedCwd[0]) {
@@ -1048,9 +1129,9 @@ __declspec(dllexport) BOOL CreateProcessA(
if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4; if (rlen + 4 > sizeof(restoreCwd)) rlen = sizeof(restoreCwd) - 4;
memcpy(restoreCwd + 3, savedCwd, rlen); memcpy(restoreCwd + 3, savedCwd, rlen);
restoreCwd[3 + rlen] = '\0'; restoreCwd[3 + rlen] = '\0';
SysSetCwd(restoreCwd); K32SetCwdNative(restoreCwd);
} else { } else {
SysSetCwd("0:/"); K32SetCwdNative("0:/");
} }
} }
@@ -1166,7 +1247,7 @@ __declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput,
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0; if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0;
return TRUE; return TRUE;
} }
SysWriteConsole((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite); K32WriteConsoleSpan((const char*)lpBuffer, (size_t)nNumberOfCharsToWrite);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite; if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE; return TRUE;
} }
@@ -1306,7 +1387,7 @@ __declspec(dllexport) HANDLE FindFirstFileA(const char* lpFileName,
// Open the kernel directory iterator // Open the kernel directory iterator
uint32_t kh = 0; 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) { if (s != 0 || kh == 0) {
gLastError = 2; // ERROR_FILE_NOT_FOUND gLastError = 2; // ERROR_FILE_NOT_FOUND
return INVALID_HANDLE_VALUE; return INVALID_HANDLE_VALUE;
@@ -1387,7 +1468,7 @@ __declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
char translatedPath[MAX_PATH]; char translatedPath[MAX_PATH];
TranslatePath(lpPathName, translatedPath); TranslatePath(lpPathName, translatedPath);
uint32_t s = SysSetCwd(translatedPath); uint32_t s = K32SetCwdNative(translatedPath);
if (s != 0) { gLastError = 2; return FALSE; } if (s != 0) { gLastError = 2; return FALSE; }
return TRUE; 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. // Kernel returns native "N:/path". Present it as a Win32 drive letter.
char raw[512]; 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; } if (s != 0) { gLastError = 1; return 0; }
char full[515]; char full[515];
+76 -69
View File
@@ -1,69 +1,76 @@
// user32.c - Win32 User Interface // user32.c - Win32 User Interface
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <syscall.h> #include <syscall.h>
typedef void* HWND; typedef void* HWND;
typedef void* HDC; typedef void* HDC;
typedef long LONG; typedef long LONG;
typedef int BOOL; typedef int BOOL;
typedef struct _RECT { LONG left, top, right, bottom; } RECT; typedef struct _RECT { LONG left, top, right, bottom; } RECT;
typedef struct tagPAINTSTRUCT { typedef struct tagPAINTSTRUCT {
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32]; HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
} PAINTSTRUCT; } PAINTSTRUCT;
// --- Painting Functions ---
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) { static void User32DebugPrintA(const char* s) {
(void)hwnd; if (!s) return;
if (lpPaint) { SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
lpPaint->hdc = (HDC)1; SysDebugPrint(&req);
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080; }
}
return (HDC)1; // --- Painting Functions ---
} __declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
(void)hwnd;
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; } if (lpPaint) {
lpPaint->hdc = (HDC)1;
// --- Message Loop Stubs --- lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) { }
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0; return (HDC)1;
} }
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
(void)m; (void)h; (void)f; (void)l; (void)r; __declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
return 0;
} // --- Message Loop Stubs ---
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; } __declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; } (void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
}
// --- Dialogs & UI --- __declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) { (void)m; (void)h; (void)f; (void)l; (void)r;
(void)h; (void)cap; (void)type; return 0;
SysDebugPrint("USER32: MessageBoxA: "); }
SysDebugPrint(txt); __declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
SysDebugPrint("\n"); __declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
return 1; // IDOK
} // --- Dialogs & UI ---
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
// --- Window Queries --- (void)h; (void)cap; (void)type;
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; } User32DebugPrintA("USER32: MessageBoxA: ");
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; } User32DebugPrintA(txt);
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; } User32DebugPrintA("\n");
return 1; // IDOK
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) { }
(void)hwnd; (void)cmd; return (HWND)0;
} // --- Window Queries ---
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) { __declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
(void)hwnd; (void)index; return 0; __declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
}
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) { (void)hwnd; (void)cmd; return (HWND)0;
(void)hwnd; (void)index; return 0; }
}
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) { (void)hwnd; (void)index; return 0;
(void)hwnd; }
if (pid) *pid = 1;
return 1; // Thread ID __declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
} (void)hwnd; (void)index; return 0;
}
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
(void)hwnd;
if (pid) *pid = 1;
return 1; // Thread ID
}
+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 - higher-half x86-64 kernel
- physical memory manager - physical memory manager
- virtual memory manager with kernel pool and user heap - virtual memory manager with kernel pool and user heap
- W^X-style user page protections
- single address-space design with user/supervisor page permissions - single address-space design with user/supervisor page permissions
- HAL for screen, keyboard, mouse, timer, PCI, APIC, HPET, AHCI, and IDE - 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 - 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 - events, semaphores, mutexes, queues, and alertable waits
- IRQL model: `PASSIVE_LEVEL`, `APC_LEVEL`, `DISPATCH_LEVEL`, and device IRQLs - IRQL model: `PASSIVE_LEVEL`, `APC_LEVEL`, `DISPATCH_LEVEL`, and device IRQLs
- interrupt-exit scheduling instead of context-switching inside hard IRQ handlers - 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`) - lwIP network stack as a runtime driver (`lwip.sys`)
- RTL8139 and RTL816x ethernet drivers - RTL8139 and RTL816x ethernet drivers
- native BSD-style socket syscalls: `SysSocket`, `SysBind`, `SysConnect`, `SysSend`, `SysRecv`, `SysPoll`, `SysResolveHost`, and friends - 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 - INI-based configuration
- PE import resolution and dynamic `.sys` driver loading - PE import resolution and dynamic `.sys` driver loading
- partial Win32 personality - partial Win32 personality
- partial (stub/microsoft-level) POSIX personality - partial (stub/microsoft-level) POSIX personality
- self-tests for APCs, async disk I/O, async file I/O, trap frames, and cancellation - self-tests for APCs, async disk I/O, async file I/O, trap frames, and cancellation
- fully GDPR-compliant - fully GDPR-compliant
- Doom, because priorities matter
## architecture notes ## 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? 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. 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.
Is it very convenient while building? Absolutely.
### execution model ### 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. 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`. 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). 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. 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 ## how to build
- open the VS x64 build tools as administrator - open the VS x64 build tools as administrator
@@ -260,11 +267,14 @@ todo
## faq ## faq
Q: Why? Q: Why?
A: Learning. Fun. Spite. A: Reasons.
Q: Does it run Doom? Q: Does it run Doom?
A: `doom.exe` 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? Q: Can you read and write files?
A: As far as I'm aware. 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. A: Depends how you frame it. Some apps will run, most will not.
Q: Are processes isolated? 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? Q: How often does it crash?
A: Less often than before. A: Like Windows 11.
Q: Can it recover from a crash? Q: Can it recover from a crash?
A: Yes. Unless the kernel faulted. A: Yes. Unless the kernel faulted.