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
+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);
|
||||
Reference in new issue
Block a user