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