rewrite init to restart failed processes, add file renaming, directory creation, directory renaming, directory removal, add process killing, unify shells, readme changes.

This commit is contained in:
maksw committed 2026-05-13 10:41:48 +02:00
1 parent f80e66ecde
commit ddb074f0e0
16 files changed
+439 -604

No files matched your search

Binary file not shown.
+16 -2
View File
@@ -53,13 +53,15 @@ void _start(const char* args)
uint32_t h = 0; uint32_t h = 0;
uint32_t s = 0; uint32_t s = 0;
for (;;) {
SysDebugPrint("[init] starting\n"); SysDebugPrint("[init] starting\n");
s = SysCreateProcess(args, &h, NULL); s = SysCreateProcess(args, &h, NULL);
if (s != STATUS_SUCCESS) { if (s != STATUS_SUCCESS) {
SysDebugPrint("[init] FATAL: failed to start!\n"); SysDebugPrint("[init] FATAL: failed to start!\n");
char buffer[12]; char buffer[12];
itoa(s, buffer); itoa(s, buffer);
SysDebugPrint(buffer); SysDebugPrint("\n"); SysDebugPrint(buffer);
SysDebugPrint("\n");
SysExitProcess(1); SysExitProcess(1);
} }
@@ -67,9 +69,21 @@ void _start(const char* args)
s = SysWaitForProcess(h); s = SysWaitForProcess(h);
if (s != STATUS_SUCCESS) if (s != STATUS_SUCCESS)
SysDebugPrint("[init] WARNING: SysWaitForProcess returned error\n"); SysDebugPrint("[init] WARNING: SysWaitForProcess returned error\n");
int exitCode = 0;
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
SysDebugPrint("[init] WARNING: failed to read exit code\n");
exitCode = 1;
}
SysCloseHandle(h); SysCloseHandle(h);
SysDebugPrint("[init] done\n"); if (exitCode == 0)
break;
SysDebugPrint("[init] child crashed, restarting\n");
}
SysDebugPrint("[init] done\n");
SysExitProcess(0); SysExitProcess(0);
} }
+42
View File
@@ -289,6 +289,48 @@ __declspec(dllexport) KSTATUS IoDeleteFile(const char* path) {
return fs->Delete(fs, relPath); return fs->Delete(fs, relPath);
} }
__declspec(dllexport) KSTATUS IoRenameFile(const char* oldPath, const char* newPath) {
if (!oldPath || !newPath) return STATUS_INVALID_PARAMETER;
char oldRel[CWD_MAX];
char newRel[CWD_MAX];
uint8_t oldDrive = ResolvePath(oldPath, oldRel, sizeof(oldRel));
uint8_t newDrive = ResolvePath(newPath, newRel, sizeof(newRel));
if (oldDrive != newDrive) return STATUS_NOT_SUPPORTED;
IO_FILESYSTEM* fs = gVolumes[oldDrive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->Rename) return STATUS_NOT_SUPPORTED;
return fs->Rename(fs, oldRel, newRel);
}
__declspec(dllexport) KSTATUS IoCreateDir(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
char relPath[CWD_MAX];
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->CreateDir) return STATUS_NOT_SUPPORTED;
return fs->CreateDir(fs, relPath);
}
__declspec(dllexport) KSTATUS IoRemoveDir(const char* path) {
if (!path) return STATUS_INVALID_PARAMETER;
char relPath[CWD_MAX];
uint8_t drive = ResolvePath(path, relPath, sizeof(relPath));
IO_FILESYSTEM* fs = gVolumes[drive].Fs;
if (!fs) return STATUS_NOT_FOUND;
if (!fs->RemoveDir) return STATUS_NOT_SUPPORTED;
return fs->RemoveDir(fs, relPath);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// IoTruncateFile - resize the file; updates the cached Size on success // IoTruncateFile - resize the file; updates the cached Size on success
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+11
View File
@@ -45,6 +45,14 @@ typedef struct IO_FILESYSTEM {
// Delete the file at 'path'. // Delete the file at 'path'.
KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, const char* path); KSTATUS (*Delete)(struct IO_FILESYSTEM* fs, const char* path);
// Rename or move a file/directory within the same filesystem.
KSTATUS (*Rename)(struct IO_FILESYSTEM* fs,
const char* oldPath, const char* newPath);
// Create or remove directories.
KSTATUS (*CreateDir)(struct IO_FILESYSTEM* fs, const char* path);
KSTATUS (*RemoveDir)(struct IO_FILESYSTEM* fs, const char* path);
// Truncate or extend the file to exactly 'newSize' bytes. // Truncate or extend the file to exactly 'newSize' bytes.
KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx, KSTATUS (*Truncate)(struct IO_FILESYSTEM* fs, void* fileCtx,
uint64_t newSize); uint64_t newSize);
@@ -119,6 +127,9 @@ __declspec(dllexport) KSTATUS IoWriteFile (FILE_OBJECT* file, const void* buf,
size_t size, size_t* bytesWritten); size_t size, size_t* bytesWritten);
__declspec(dllexport) KSTATUS IoCreateFile (const char* path, FILE_OBJECT** out); __declspec(dllexport) KSTATUS IoCreateFile (const char* path, FILE_OBJECT** out);
__declspec(dllexport) KSTATUS IoDeleteFile (const char* path); __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 IoTruncateFile(FILE_OBJECT* file, uint64_t newSize); __declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize);
__declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset); __declspec(dllexport) KSTATUS IoSeekFile(FILE_OBJECT* file, uint64_t offset);
__declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file); __declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file);
+17
View File
@@ -25,6 +25,7 @@ static void ProcessInit(void) {
} }
LIST_ENTRY* KeGetProcessList(void) { LIST_ENTRY* KeGetProcessList(void) {
ProcessInit();
return &gProcessList; return &gProcessList;
} }
@@ -36,6 +37,21 @@ PROCESS* KeGetCurrentProcess(void) {
return NULL; return NULL;
} }
size_t KeCopyProcessIds(uint32_t* pidsOut, size_t maxCount) {
ProcessInit();
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
size_t count = 0;
LIST_ENTRY* head = &gProcessList;
for (LIST_ENTRY* node = head->Fwd; node != head; node = node->Fwd) {
PROCESS* p = CONTAINING_RECORD(node, PROCESS, GlobalLink);
if (pidsOut && count < maxCount) pidsOut[count] = p->ProcessId;
count++;
}
KeReleaseSpinlock(&gProcessListLock, flags);
return count;
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Destructor (called by ObDereferenceObject when RefCount hits zero) // Destructor (called by ObDereferenceObject when RefCount hits zero)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -206,6 +222,7 @@ OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle) {
} }
PROCESS* KeGetProcessById(uint32_t pid) { PROCESS* KeGetProcessById(uint32_t pid) {
ProcessInit();
uintptr_t flags = KeAcquireSpinlock(&gProcessListLock); uintptr_t flags = KeAcquireSpinlock(&gProcessListLock);
LIST_ENTRY* head = &gProcessList; LIST_ENTRY* head = &gProcessList;
LIST_ENTRY* node = head->Fwd; LIST_ENTRY* node = head->Fwd;
+2
View File
@@ -77,5 +77,7 @@ OBJECT* KeLookupHandle(PROCESS* process, uint32_t handle);
/* Return the global process list head (kernel-internal). */ /* Return the global process list head (kernel-internal). */
LIST_ENTRY* KeGetProcessList(void); LIST_ENTRY* KeGetProcessList(void);
PROCESS* KeGetCurrentProcess(void); PROCESS* KeGetCurrentProcess(void);
/* Copy a snapshot of process IDs into caller-provided storage. */
size_t KeCopyProcessIds(uint32_t* pidsOut, size_t maxCount);
/* Find a live process by its ProcessId. Bumps refcount; caller must ObDereferenceObject. */ /* Find a live process by its ProcessId. Bumps refcount; caller must ObDereferenceObject. */
PROCESS* KeGetProcessById(uint32_t pid); PROCESS* KeGetProcessById(uint32_t pid);
+71
View File
@@ -1177,6 +1177,72 @@ static uint64_t SyscallNamedQueueOwnerPid(uint64_t a0, uint64_t a1, uint64_t a2,
return STATUS_NOT_FOUND; return STATUS_NOT_FOUND;
} }
static uint64_t SyscallKillProcess(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
PROCESS* proc = KeGetProcessById((uint32_t)a0);
if (!proc) return STATUS_NOT_FOUND;
THREAD* self = KeGetCurrentThread();
int exitCode = (int)a1;
if (self && self->Owner == proc) {
KeExitProcess(proc, exitCode);
ObDereferenceObject(&proc->Header);
KeExitThread(self, exitCode);
while (true) ;
}
KeExitProcess(proc, exitCode);
ObDereferenceObject(&proc->Header);
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_GET_PROCESS_LIST (65) a0=*pidsOut a1=maxCount a2=*countOut
// ---------------------------------------------------------------------------
static uint64_t SyscallGetProcessList(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a3; (void)a4; (void)a5;
uint32_t* pidsOut = (uint32_t*)(uintptr_t)a0;
size_t maxCount = (size_t)a1;
size_t* countOut = (size_t*)(uintptr_t)a2;
size_t count = KeCopyProcessIds(pidsOut, maxCount);
if (countOut) *countOut = count;
return STATUS_SUCCESS;
}
// ---------------------------------------------------------------------------
// SYS_RENAME_FILE (66) a0=*oldPath a1=*newPath
// ---------------------------------------------------------------------------
static uint64_t SyscallRenameFile(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a2; (void)a3; (void)a4; (void)a5;
const char* oldPath = (const char*)(uintptr_t)a0;
const char* newPath = (const char*)(uintptr_t)a1;
if (!oldPath || !newPath) return STATUS_INVALID_PARAMETER;
return IoRenameFile(oldPath, newPath);
}
// ---------------------------------------------------------------------------
// SYS_CREATE_DIR (67) a0=*path
// ---------------------------------------------------------------------------
static uint64_t SyscallCreateDir(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
const char* path = (const char*)(uintptr_t)a0;
if (!path) return STATUS_INVALID_PARAMETER;
return IoCreateDir(path);
}
// ---------------------------------------------------------------------------
// SYS_REMOVE_DIR (68) a0=*path
// ---------------------------------------------------------------------------
static uint64_t SyscallRemoveDir(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5) {
(void)a1; (void)a2; (void)a3; (void)a4; (void)a5;
const char* path = (const char*)(uintptr_t)a0;
if (!path) return STATUS_INVALID_PARAMETER;
return IoRemoveDir(path);
}
// =========================================================================== // ===========================================================================
// Dispatch table - index must match SYS_* in syscall_nr.h exactly. // Dispatch table - index must match SYS_* in syscall_nr.h exactly.
// =========================================================================== // ===========================================================================
@@ -1248,6 +1314,11 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
/* 61 SYS_OPEN_NAMED_QUEUE */ SyscallOpenNamedQueue, /* 61 SYS_OPEN_NAMED_QUEUE */ SyscallOpenNamedQueue,
/* 62 SYS_QUEUE_DUPLICATE */ SyscallQueueDuplicate, /* 62 SYS_QUEUE_DUPLICATE */ SyscallQueueDuplicate,
/* 63 SYS_NAMED_QUEUE_OWNER_PID */ SyscallNamedQueueOwnerPid, /* 63 SYS_NAMED_QUEUE_OWNER_PID */ SyscallNamedQueueOwnerPid,
/* 64 SYS_KILL_PROCESS */ SyscallKillProcess,
/* 65 SYS_GET_PROCESS_LIST */ SyscallGetProcessList,
/* 66 SYS_RENAME_FILE */ SyscallRenameFile,
/* 67 SYS_CREATE_DIR */ SyscallCreateDir,
/* 68 SYS_REMOVE_DIR */ SyscallRemoveDir,
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+18 -24
View File
@@ -303,21 +303,6 @@ static PROCESS* LaunchUserProcess(const char* filePath, const char* args) {
// Caller owns the reference returned by KeCreateProcess. // Caller owns the reference returned by KeCreateProcess.
return proc; return proc;
} }
// ---------------------------------------------------------------------------
// Kernel thread: wait for a process to exit, then log a message and shut down.
// Takes ownership of the PROCESS* reference passed as arg; releases it on exit.
// ---------------------------------------------------------------------------
static void ProcessWatcherThread(void* arg) {
PROCESS* proc = (PROCESS*)arg;
KeWaitForEvent(proc->ExitEvent);
DbgPrint("ProcessWatcherThread: Process \"%s\" (PID %u) exited with code %d.\n",
proc->Name, proc->ProcessId, proc->ExitCode);
ObDereferenceObject(&proc->Header);
DbgPrint("ProcessWatcherThread: Shutting down.\n");
gKmainHal->Power.Shutdown(); // does not return
}
static void PrintProcessList() { static void PrintProcessList() {
LIST_ENTRY* listHead = KeGetProcessList(); LIST_ENTRY* listHead = KeGetProcessList();
LIST_ENTRY* current; LIST_ENTRY* current;
@@ -354,20 +339,29 @@ void SystemInitThread(void* arg) {
DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f)); DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f));
DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f)); DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
// Launch User-Mode Shell (this will trigger AHCI & FAT32 which CAN safely block now) // Launch User-Mode init.exe and restart it until it exits cleanly.
while (true) {
PROCESS* shellProc = LaunchUserProcess("system/init.exe", args->bootInfo->CommandLine); PROCESS* shellProc = LaunchUserProcess("system/init.exe", args->bootInfo->CommandLine);
if (!shellProc) {
if (shellProc) { DbgPrint("SystemInitThread: Failed to start system/init.exe; retrying.\n");
KSTATUS ws = KeCreateKernelThread((uintptr_t)ProcessWatcherThread, KeYield();
(uintptr_t)shellProc, continue;
PriorityNormal, (THREAD**)0);
if (!KSUCCESS(ws)) {
DbgPrint("SystemInitThread: Failed to create watcher thread (%d) -- leaking proc ref.\n", ws);
}
} }
KeWaitForEvent(shellProc->ExitEvent);
int exitCode = shellProc->ExitCode;
DbgPrint("SystemInitThread: init.exe exited with code %d.\n", exitCode);
ObDereferenceObject(&shellProc->Header);
if (exitCode == 0) break;
DbgPrint("SystemInitThread: Restarting init.exe.\n");
}
DbgPrint("SystemInitThread: init.exe completed successfully; shutting down.\n");
gKmainHal->Power.Shutdown();
MmFreePool(args); MmFreePool(args);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Main Kernel Entry // Main Kernel Entry
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+14 -1
View File
@@ -162,4 +162,17 @@
#define SYS_QUEUE_DUPLICATE 62 // (uint32_t handle, uint32_t targetPid, uint32_t* handleOut) -> KSTATUS #define SYS_QUEUE_DUPLICATE 62 // (uint32_t handle, uint32_t targetPid, uint32_t* handleOut) -> KSTATUS
#define SYS_NAMED_QUEUE_OWNER_PID 63 // (const char* name, uint32_t* pidOut) -> KSTATUS #define SYS_NAMED_QUEUE_OWNER_PID 63 // (const char* name, uint32_t* pidOut) -> KSTATUS
#define SYS_MAX 64 // ---------------------------------------------------------------------------
// Process utilities
// ---------------------------------------------------------------------------
#define SYS_KILL_PROCESS 64 // (uint32_t pid, int exitCode) -> KSTATUS
#define SYS_GET_PROCESS_LIST 65 // (uint32_t* pidsOut, size_t maxCount, size_t* countOut) -> KSTATUS
// ---------------------------------------------------------------------------
// Filesystem mutations
// ---------------------------------------------------------------------------
#define SYS_RENAME_FILE 66 // (const char* oldPath, const char* newPath) -> KSTATUS
#define SYS_CREATE_DIR 67 // (const char* path) -> KSTATUS
#define SYS_REMOVE_DIR 68 // (const char* path) -> KSTATUS
#define SYS_MAX 69
+55 -140
View File
@@ -5,67 +5,37 @@
#include <syscall.h> #include <syscall.h>
#include <io_file.h> #include <io_file.h>
#define MAX_CMD 256
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Output helpers // Output helpers
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void print(const char* s) { static void print(const char* s) { SysWriteConsole(s, strlen(s)); }
SysWriteConsole(s, strlen(s));
}
static void printUInt(uint32_t n) { static void printUInt(uint32_t n) {
char buf[12]; char buf[12]; int i = 0;
int i = 0;
if (n == 0) { print("0"); return; } if (n == 0) { print("0"); return; }
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; } while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
char rev[12]; while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
int j = 0;
while (i > 0) rev[j++] = buf[--i];
rev[j] = '\0';
print(rev);
} }
static void printInt(int32_t n) { static void printInt(int32_t n) {
if (n < 0) { if (n < 0) { print("-"); printUInt((uint32_t)-(n + 1) + 1); }
print("-"); else { printUInt((uint32_t)n); }
printUInt((uint32_t)-(n + 1) + 1);
} else {
printUInt((uint32_t)n);
}
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Line editor // Line editor
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#define MAX_CMD 256
static void readLine(char* buf, int len) { static void readLine(char* buf, int len) {
int pos = 0; int pos = 0;
while (true) { while (true) {
char c = SysKbReadChar(); char c = SysKbReadChar();
if (!c) { if (!c) { SysYield(); continue; }
SysYield(); if (c == '\r' || c == '\n') { print("\n"); break; }
continue; if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
} if (c < 0x20 || c == 127) continue;
if (pos < len - 1) { buf[pos++] = c; char echo[2] = {c, '\0'}; print(echo); }
if (c == '\r' || c == '\n') {
print("\n");
break;
}
if ((c == '\b' || c == 127) && pos > 0) {
pos--;
print("\b \b");
continue;
}
if (c < 0x20 || c == 127)
continue;
if (pos < len - 1) {
buf[pos++] = c;
char echo[2] = { c, '\0' };
print(echo);
}
} }
buf[pos] = '\0'; buf[pos] = '\0';
} }
@@ -84,86 +54,73 @@ static char* trim(char* s) {
static void cmd_help() { static void cmd_help() {
print("Available commands:\n" print("Available commands:\n"
" help - Show this list\n" " help - Show this list\n"
" dir - List files\n" " dir [path] - List directory contents\n"
" cd <path> - Change directory\n" " cd <path> - Change directory\n"
" type <file> - Display file contents\n" " type <file> - Display file contents\n"
" del <file> - Delete a file\n" " del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n" " write <file> <tx> - Create a file with text\n"
" clear - Clear the screen\n"
" exit - Close the shell\n"); " exit - Close the shell\n");
} }
static void cmd_write(const char* args) { static void cmd_write(const char* args) {
char filename[MAX_CMD]; char filename[MAX_CMD]; int i = 0;
int i = 0;
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) { while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
filename[i] = args[i]; filename[i] = args[i]; i++;
i++;
} }
filename[i] = '\0'; filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
if (args[i] == '\0' || args[i+1] == '\0') {
print("write: usage: write <filename> <text>\n");
return;
}
uint32_t hFile; uint32_t hFile;
if (SysCreateFile(filename, &hFile) != 0) { if (SysCreateFile(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
print("write: could not create file\n");
return;
}
const char* text = args + i + 1;
size_t written; size_t written;
SysWriteFile(hFile, text, strlen(text), &written); SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written);
SysCloseHandle(hFile); SysCloseHandle(hFile);
print("File written successfully.\n"); print("File written successfully.\n");
} }
static void cmd_dir(const char* path) { static void cmd_dir(const char* path) {
uint32_t hDir; uint32_t hDir;
const char* target = (path && *path != '\0') ? path : ".";
// Validate input if (SysOpenDir(target, &hDir) != 0) {
if (path == NULL || path[0] == '\0') { print("dir: failed to open directory\n");
path = "."; // Default to current directory if empty
}
// SysOpenDir (Syscall 54) handles the const char* path directly
if (SysOpenDir(path, &hDir) != 0) {
SysDebugPrint("dir: failed to open directory\n");
return; return;
} }
// Assuming KDIR_ENTRY is your structure for directory entries
KDIR_ENTRY ke; KDIR_ENTRY ke;
// SysReadDir (Syscall 55) populates the buffer with entry data
// It typically returns 0 on success and non-zero (or error) when done
while (SysReadDir(hDir, &ke) == 0) { while (SysReadDir(hDir, &ke) == 0) {
// We assume ke.Name is a null-terminated string within the struct // Skip hidden/system markers if desired (e.g., those starting with .)
print(ke.Name); if (ke.Name[0] == '.') continue;
print("\n");
// Corrected Attribute Check: 0x10 is the Directory bit in FAT
if (ke.Attr & 0x10) {
print("<DIR> ");
} else {
print(" ");
} }
// Always close the handle (Syscall 26) to prevent leaks print(ke.Name);
// Optional: print file size for non-directories
if (!(ke.Attr & 0x10)) {
print(" (");
printUInt(ke.FileSize);
print(" bytes)");
}
print("\n");
}
SysCloseHandle(hDir); SysCloseHandle(hDir);
} }
static void cmd_type(const char* filename) { static void cmd_type(const char* filename) {
uint32_t hFile; uint32_t hFile;
if (SysOpenFile(filename, &hFile) != 0) { if (SysOpenFile(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
print("type: error opening file\n"); char buffer[512]; size_t bytesRead;
return;
}
char buffer[512];
size_t bytesRead;
while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) { while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) {
buffer[bytesRead] = '\0'; buffer[bytesRead] = '\0'; print(buffer);
print(buffer);
} }
SysCloseHandle(hFile); print("\n");
SysCloseHandle(hFile);
print("\n");
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -171,76 +128,34 @@ static void cmd_type(const char* filename) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static int runCommand(const char* cmd) { static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return 0; if (!cmd || *cmd == '\0') return 0;
if (strcmp(cmd, "exit") == 0) return 1; if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "clear") == 0) { SysScreenClear(0); return 0; }
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
cmd_dir("."); if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
return 0; if (strncmp(cmd, "cd ", 3) == 0) { SysSetCwd(cmd + 3); return 0; }
} if (strncmp(cmd, "del ", 4) == 0) { SysDeleteFile(cmd + 4); return 0; }
if (strncmp(cmd, "dir ", 4) == 0) { if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
cmd_dir(cmd + 4); if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
return 0;
}
if (strcmp(cmd, "clear") == 0) {
SysScreenClear(0x00000000);
return 0;
}
if (strncmp(cmd, "cd ", 3) == 0) {
SysSetCwd(cmd + 3);
return 0;
}
if (strncmp(cmd, "del ", 4) == 0) {
SysDeleteFile(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;
}
// --- External Process Execution ---
uint32_t hProc; uint32_t hProc;
if (SysCreateProcess(cmd, &hProc, cmd) == 0) { if (SysCreateProcess(cmd, &hProc, cmd) == 0) {
SysWaitForProcess(hProc); SysWaitForProcess(hProc);
int exitCode = 0;
SysGetProcessExitCode(hProc, &exitCode);
SysCloseHandle(hProc); SysCloseHandle(hProc);
while (SysKbPoll()) { while (SysKbPoll()) (void)SysKbReadChar();
(void)SysKbReadChar(); } else { print("shell: launch failed\n"); }
}
} else {
print("shell: launch failed\n");
}
return 0; return 0;
} }
// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------
int main(int argc, char** argv) { int main(int argc, char** argv) {
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: ready\n"); print("shell: ready\n");
while (true) { while (true) {
print("> "); print("> ");
readLine(lineBuf, MAX_CMD); readLine(lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf))) if (runCommand(trim(lineBuf))) break;
break;
} }
print("shell: goodbye\n"); print("shell: goodbye\n");
SysExitProcess(0); SysExitProcess(0);
return 0; return 0;
+34 -124
View File
@@ -3,18 +3,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#define MAX_CMD 256
#define NAME_MAX 259 #define NAME_MAX 259
typedef struct dirent { typedef struct dirent { char d_name[NAME_MAX + 1]; unsigned char d_type; } dirent;
char d_name[NAME_MAX + 1]; typedef struct DIR { uint32_t KHandle; dirent Current; bool Valid; } DIR;
unsigned char d_type; // DT_REG or DT_DIR
} dirent;
typedef struct DIR {
uint32_t KHandle; // kernel DIR_OBJECT handle
dirent Current;
bool Valid;
} DIR;
extern int read(int fd, void *buf, size_t count); extern int read(int fd, void *buf, size_t count);
extern int write(int fd, const void *buf, size_t count); extern int write(int fd, const void *buf, size_t count);
@@ -25,36 +18,21 @@ extern DIR* opendir(const char* path);
extern dirent* readdir(DIR* d); extern dirent* readdir(DIR* d);
extern int closedir(DIR* d); extern int closedir(DIR* d);
extern int unlink(const char *pathname); extern int unlink(const char *pathname);
extern int posix_spawn(int *pid, const char *path, const void *file_act, const void *attr, char *const argv[], char *const envp[]); extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]);
extern int waitpid(int pid, int *status, int options); extern int waitpid(int pid, int *status, int options);
extern void exit(int status); extern void exit(int status);
extern int ClearConsoleScreenBuffer(void* h);
extern void PosixFdInit(void); extern void PosixFdInit(void);
extern int ClearConsoleScreenBuffer(void* hConsole);
// --------------------------------------------------------------------------- static int hIn = 0; static int hOut = 1;
// Global handles
// ---------------------------------------------------------------------------
static int hIn = 0; // stdin
static int hOut = 1; // stdout
// --------------------------------------------------------------------------- static void print(const char* s) { write(hOut, s, strlen(s)); }
// Output helpers
// ---------------------------------------------------------------------------
static void print(const char* s) {
write(hOut, s, strlen(s));
}
static void printUInt(uint32_t n) { static void printUInt(uint32_t n) {
char buf[12]; char buf[12]; int i = 0;
int i = 0;
if (n == 0) { print("0"); return; } if (n == 0) { print("0"); return; }
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; } while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
char rev[12]; while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
int j = 0;
while (i > 0) rev[j++] = buf[--i];
rev[j] = '\0';
print(rev);
} }
static void printInt(int32_t n) { static void printInt(int32_t n) {
@@ -62,28 +40,14 @@ static void printInt(int32_t n) {
printUInt((uint32_t)n); printUInt((uint32_t)n);
} }
// ---------------------------------------------------------------------------
// Line editor
// ---------------------------------------------------------------------------
#define MAX_CMD 256
static void readLine(char* buf, int len) { static void readLine(char* buf, int len) {
int pos = 0; int pos = 0;
while (true) { while (true) {
char c; char c; if (read(hIn, &c, 1) <= 0) break;
if (read(hIn, &c, 1) <= 0) break;
if (c == '\r' || c == '\n') { print("\n"); break; } if (c == '\r' || c == '\n') { print("\n"); break; }
if ((c == '\b' || c == 127) && pos > 0) { if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
pos--;
print("\b \b");
continue;
}
if (c < 0x20 || c == 127) continue; if (c < 0x20 || c == 127) continue;
if (pos < len - 1) { if (pos < len - 1) { buf[pos++] = c; char echo[2] = {c, '\0'}; print(echo); }
buf[pos++] = c;
char echo[2] = { c, '\0' };
print(echo);
}
} }
buf[pos] = '\0'; buf[pos] = '\0';
} }
@@ -96,9 +60,6 @@ static char* trim(char* s) {
return s; return s;
} }
// ---------------------------------------------------------------------------
// Built-in commands
// ---------------------------------------------------------------------------
static void cmd_help() { static void cmd_help() {
print("Available commands:\n" print("Available commands:\n"
" help - Show this list\n" " help - Show this list\n"
@@ -107,125 +68,74 @@ static void cmd_help() {
" type <file> - Display file contents\n" " type <file> - Display file contents\n"
" del <file> - Delete a file\n" " del <file> - Delete a file\n"
" write <file> <txt> - Create a file with text\n" " write <file> <txt> - Create a file with text\n"
" clear - Clear the screen\n"
" exit - Close the shell\n"); " exit - Close the shell\n");
} }
static void cmd_write(const char* args) { static void cmd_write(const char* args) {
char filename[MAX_CMD]; char filename[MAX_CMD]; int i = 0;
int i = 0;
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) { while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
filename[i] = args[i]; filename[i] = args[i]; i++;
i++;
} }
filename[i] = '\0'; filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') return; if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
int fd = open(filename, 0x0241, 0644); // O_WRONLY | O_CREAT | O_TRUNC
// Open with O_WRONLY | O_CREAT | O_TRUNC (typical flags: 0x0001 | 0x0040 | 0x0200) if (fd < 0) { print("write: could not create file\n"); return; }
int fd = open(filename, 0x0241, 0644);
if (fd < 0) return;
write(fd, args + i + 1, strlen(args + i + 1)); write(fd, args + i + 1, strlen(args + i + 1));
close(fd); close(fd);
print("File written successfully.\n");
} }
static void cmd_dir(const char* path) { static void cmd_dir(const char* path) {
// If no path is provided, default to current directory
const char* target = (path && *path != '\0') ? path : "."; const char* target = (path && *path != '\0') ? path : ".";
DIR* d = opendir(target); DIR* d = opendir(target);
if (!d) { if (!d) { print("dir: cannot access directory\n"); return; }
print("dir: cannot access '");
print(target);
print("'\n");
return;
}
dirent* entry; dirent* entry;
while ((entry = readdir(d)) != NULL) { while ((entry = readdir(d)) != NULL) {
// Filter out the current and parent directory markers if (entry->d_name[0] == '.') continue;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { print(entry->d_type == 1 ? "<DIR> " : " ");
continue; print(entry->d_name); print("\n");
} }
// Display type (Assuming 1=DIR, 2=REG based on common custom POSIX layouts)
if (entry->d_type == 1) {
print("<DIR> ");
} else {
print(" ");
}
print(entry->d_name);
print("\n");
}
closedir(d); closedir(d);
} }
static void cmd_type(const char* filename) { static void cmd_type(const char* filename) {
int fd = open(filename, 0); // O_RDONLY int fd = open(filename, 0); // O_RDONLY
if (fd < 0) return; if (fd < 0) { print("type: cannot open file\n"); return; }
char buffer[512]; char buffer[512]; int n;
int n; while ((n = read(fd, buffer, 511)) > 0) { buffer[n] = '\0'; print(buffer); }
while ((n = read(fd, buffer, 511)) > 0) { close(fd); print("\n");
buffer[n] = '\0';
print(buffer);
}
close(fd);
print("\n");
} }
// ---------------------------------------------------------------------------
// Run one command.
// ---------------------------------------------------------------------------
static int runCommand(const char* cmd) { static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') return 0; if (!cmd || *cmd == '\0') return 0;
if (strcmp(cmd, "exit") == 0) return 1; if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(0); return 0; }
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
if (strcmp(cmd, "dir") == 0) { if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
cmd_dir(".");
return 0;
}
if (strncmp(cmd, "dir ", 4) == 0) {
cmd_dir(cmd + 4);
return 0;
}
if (strcmp(cmd, "clear") == 0) {
ClearConsoleScreenBuffer(0);
return 0;
}
if (strncmp(cmd, "cd ", 3) == 0) { chdir(cmd + 3); return 0; } if (strncmp(cmd, "cd ", 3) == 0) { chdir(cmd + 3); return 0; }
if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; } if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; }
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; } if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; } if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
// --- External Process Execution (No Fork) --- int pid; char *argv[] = {(char*)cmd, NULL};
int pid;
char *argv[] = {(char*)cmd, NULL};
if (posix_spawn(&pid, cmd, NULL, NULL, argv, NULL) == 0) { if (posix_spawn(&pid, cmd, NULL, NULL, argv, NULL) == 0) {
int status; int status; waitpid(pid, &status, 0);
waitpid(pid, &status, 0); } else { print("shell: launch failed\n"); }
} else {
print("shell: error launching\n");
}
return 0; return 0;
} }
int main(const char* args) { int main() {
PosixFdInit(); PosixFdInit();
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: ready\n"); print("shell: ready\n");
while (true) { while (true) {
print("> "); print("> ");
readLine(lineBuf, MAX_CMD); readLine(lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf))) break; if (runCommand(trim(lineBuf))) break;
} }
print("shell: goodbye\n"); print("shell: goodbye\n");
exit(0); exit(0);
} }
+56 -251
View File
@@ -4,85 +4,42 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
// !Made-up! clear console api. #define MAX_CMD 256
BOOL ClearConsoleScreenBuffer(HANDLE hConsole);
// ---------------------------------------------------------------------------
// Global handles
// ---------------------------------------------------------------------------
static HANDLE hIn = INVALID_HANDLE_VALUE; static HANDLE hIn = INVALID_HANDLE_VALUE;
static HANDLE hOut = INVALID_HANDLE_VALUE; static HANDLE hOut = INVALID_HANDLE_VALUE;
// --------------------------------------------------------------------------- // External/Custom API
// Output helpers BOOL ClearConsoleScreenBuffer(HANDLE hConsole);
// ---------------------------------------------------------------------------
// --- Output Helpers ---
static void print(const char* s) { static void print(const char* s) {
DWORD written; DWORD written;
// Write to the actual console window if (hOut != INVALID_HANDLE_VALUE) WriteConsoleA(hOut, s, (DWORD)strlen(s), &written, 0);
if (hOut != INVALID_HANDLE_VALUE) { else OutputDebugStringA(s);
WriteConsoleA(hOut, s, (DWORD)strlen(s), &written, 0);
} else {
OutputDebugStringA(s);
}
} }
static void printUInt(uint32_t n) { static void printUInt(uint32_t n) {
char buf[12]; char buf[12]; int i = 0;
int i = 0;
if (n == 0) { print("0"); return; } if (n == 0) { print("0"); return; }
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; } while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
char rev[12]; while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
int j = 0;
while (i > 0) rev[j++] = buf[--i];
rev[j] = '\0';
print(rev);
} }
static void printInt(int32_t n) { static void printInt(int32_t n) {
char buf[12]; if (n < 0) { print("-"); n = -n; }
int i = 0; printUInt((uint32_t)n);
uint32_t val = (n < 0) ? (print("-"), (uint32_t)-(n + 1) + 1) : (uint32_t)n;
if (val == 0) { print("0"); return; }
while (val > 0) {
buf[i++] = (char)('0' + (val % 10));
val /= 10;
}
while (i > 0) {
char c[2] = { buf[--i], '\0' };
print(c); // Assumes print() handles strings
}
} }
// --------------------------------------------------------------------------- // --- Line Editor ---
// Line editor
// ---------------------------------------------------------------------------
#define MAX_CMD 256
static void readLine(char* buf, int len) { static void readLine(char* buf, int len) {
int pos = 0; int pos = 0;
while (true) { while (true) {
char c; char c; DWORD nRead;
DWORD nRead;
ReadConsoleA(hIn, &c, 1, &nRead, 0); ReadConsoleA(hIn, &c, 1, &nRead, 0);
if (!nRead) continue; if (!nRead) continue;
if (c == '\r' || c == '\n') { print("\n"); break; }
if (c == '\r' || c == '\n') { if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
print("\n"); if (c < 0x20 || c == 127) continue;
break;
}
if ((c == '\b' || c == 127) && pos > 0) {
pos--;
print("\b \b");
continue;
}
if (c < 0x20 || c == 127)
continue;
if (pos < len - 1) { if (pos < len - 1) {
buf[pos++] = c; buf[pos++] = c;
char echo[2] = { c, '\0' }; char echo[2] = { c, '\0' };
@@ -100,253 +57,101 @@ static char* trim(char* s) {
return s; return s;
} }
// --------------------------------------------------------------------------- // --- Built-in Commands ---
// Built-in commands
// ---------------------------------------------------------------------------
static void cmd_help() { static void cmd_help() {
print("Available commands:\n" print("Available commands:\n"
" help - Show this list\n" " help - Show this list\n"
" dir - List files in current directory\n" " dir [path] - List directory contents\n"
" cd <path> - Change directory\n" " cd <path> - Change directory\n"
" type <file> - Display file contents\n" " type <file> - Display file contents\n"
" del <file> - Delete a file\n" " del <file> - Delete a file\n"
" write <file> <tx> - Create a file with text\n" " write <file> <tx> - Create a file with text\n"
" clear - Clear the screen\n"
" exit - Close the shell\n"); " exit - Close the shell\n");
} }
static void cmd_write(const char* args) { static void cmd_write(const char* args) {
char filename[MAX_PATH]; char filename[MAX_PATH]; int i = 0;
int i = 0;
// Parse filename (up to first space)
while (args[i] != ' ' && args[i] != '\0' && i < MAX_PATH - 1) { while (args[i] != ' ' && args[i] != '\0' && i < MAX_PATH - 1) {
filename[i] = args[i]; filename[i] = args[i]; i++;
i++;
} }
filename[i] = '\0'; filename[i] = '\0';
if (args[i] == '\0' || args[i+1] == '\0') { if (args[i] == '\0' || args[i+1] == '\0') {
print("write: usage: write <filename> <text>\n"); print("write: usage: write <filename> <text>\n");
return; return;
} }
const char* text = args + i + 1; // Content starts after the space
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) { if (hFile == INVALID_HANDLE_VALUE) { print("write: could not create file\n"); return; }
print("write: could not create file\n");
return;
}
DWORD written; DWORD written;
if (!WriteFile(hFile, text, (DWORD)strlen(text), &written, 0)) { WriteFile(hFile, args + i + 1, (DWORD)strlen(args + i + 1), &written, 0);
print("write: error writing to file\n");
} else {
print("File written successfully.\n");
}
CloseHandle(hFile); CloseHandle(hFile);
print("File written successfully.\n");
} }
static void cmd_dir(const char* path) { static void cmd_dir(const char* path) {
WIN32_FIND_DATAA fd; WIN32_FIND_DATAA fd;
char search_path[MAX_PATH]; char search_path[MAX_PATH];
const char* target = (path && *path != '\0') ? path : ".";
size_t path_len = 0; size_t len = strlen(target);
while (path[path_len] != '\0' && path_len < MAX_PATH) { if (len + 3 > MAX_PATH) { print("dir: path too long\n"); return; }
path_len++; strcpy(search_path, target);
} strcat(search_path, "\\*");
// Ensure we have room for the path + "\*" + null terminator
if (path_len + 3 > MAX_PATH) {
print("dir: path too long\n");
return;
}
// Manual concatenation
memcpy(search_path, path, path_len);
// Append the wildcard pattern
search_path[path_len] = '\\';
search_path[path_len + 1] = '*';
search_path[path_len + 2] = '\0';
HANDLE hFind = FindFirstFileA(search_path, &fd); HANDLE hFind = FindFirstFileA(search_path, &fd);
if (hFind == INVALID_HANDLE_VALUE) { print("dir: failed to read directory\n"); return; }
if (hFind == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
return;
}
print("dir: failed to read directory\n");
return;
}
do { do {
// Skip the "." and ".." entries to keep output clean if (fd.cFileName[0] == '.') continue; // Skip . and ..
if (path_len > 0 && fd.cFileName[0] == '.') { print(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
if (fd.cFileName[1] == '\0' || (fd.cFileName[1] == '.' && fd.cFileName[2] == '\0')) { print(fd.cFileName); print("\n");
continue;
}
}
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
print("<DIR> ");
} else {
print(" ");
}
print(fd.cFileName);
print("\n");
} while (FindNextFileA(hFind, &fd)); } while (FindNextFileA(hFind, &fd));
FindClose(hFind); FindClose(hFind);
} }
static void cmd_type(const char* filename) { static void cmd_type(const char* filename) {
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) { if (hFile == INVALID_HANDLE_VALUE) { print("type: cannot open file\n"); return; }
print("type: could not open '"); char buffer[512]; DWORD bytesRead;
print(filename);
print("'\n");
return;
}
char buffer[512];
DWORD bytesRead;
while (ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, 0) && bytesRead > 0) { while (ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, 0) && bytesRead > 0) {
buffer[bytesRead] = '\0'; buffer[bytesRead] = '\0'; print(buffer);
print(buffer);
} }
CloseHandle(hFile); print("\n");
CloseHandle(hFile);
print("\n");
} }
// ---------------------------------------------------------------------------
// Run one command. Returns 1 if the shell should exit.
// ---------------------------------------------------------------------------
static int runCommand(const char* cmd) { static int runCommand(const char* cmd) {
if (!cmd || *cmd == '\0') if (!cmd || *cmd == '\0') return 0;
return 0; if (strcmp(cmd, "exit") == 0) return 1;
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
// --- Built-in commands --- if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(hOut); return 0; }
if (strcmp(cmd, "exit") == 0) { if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
return 1; 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) { SetCurrentDirectoryA(cmd + 3); return 0; }
if (strcmp(cmd, "help") == 0) { if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return 0; }
cmd_help(); if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
return 0; if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
}
if (strcmp(cmd, "crash") == 0) {
*(int*)0 = 42;
return 0;
}
if (strcmp(cmd, "clear") == 0) {
ClearConsoleScreenBuffer(hOut);
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) {
const char* path = cmd + 3;
if (!SetCurrentDirectoryA(path)) {
print("cd: failed to change directory to '");
print(path);
print("'\n");
}
return 0;
}
if (strncmp(cmd, "del ", 4) == 0) {
const char* file = cmd + 4;
if (!DeleteFileA(file)) {
print("del: failed to delete '");
print(file);
print("'\n");
}
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;
}
// --- External Process Execution ---
STARTUPINFOA si;
PROCESS_INFORMATION pi;
unsigned char* p = (unsigned char*)&si;
for (size_t i = 0; i < sizeof(si); i++) p[i] = 0;
si.cb = (DWORD)sizeof(si);
// CreateProcessA requires a mutable buffer for the command line argument
char cmdBuf[MAX_CMD];
strncpy(cmdBuf, cmd, MAX_CMD - 1);
cmdBuf[MAX_CMD - 1] = '\0';
if (!CreateProcessA(0, cmdBuf, 0, 0, 0, 0, 0, 0, &si, &pi)) {
print("shell: could not launch '");
print(cmd);
print("', error code: ");
printUInt(GetLastError());
print("\n");
return 0;
}
STARTUPINFOA si = { sizeof(si) }; PROCESS_INFORMATION pi;
char cmdBuf[MAX_CMD]; strncpy(cmdBuf, cmd, MAX_CMD-1);
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE); WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
FlushConsoleInputBuffer(hOut); } else {
print("shell: launch failed\n");
DWORD exitCode = 0;
GetExitCodeProcess(pi.hProcess, &exitCode);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (exitCode != 0) {
print("shell: '");
print(cmd);
print("' exited with code ");
printInt(exitCode);
print("\n");
} }
return 0; return 0;
} }
// --------------------------------------------------------------------------- int main() {
// Entry point
// ---------------------------------------------------------------------------
int main(const char* cmdline) {
hIn = GetStdHandle(STD_INPUT_HANDLE); hIn = GetStdHandle(STD_INPUT_HANDLE);
hOut = GetStdHandle(STD_OUTPUT_HANDLE); hOut = GetStdHandle(STD_OUTPUT_HANDLE);
char lineBuf[MAX_CMD]; char lineBuf[MAX_CMD];
print("shell: ready\n"); print("shell: ready\n");
print(cmdline);
print("\n");
while (true) { while (true) {
print("> "); print("> ");
readLine(lineBuf, MAX_CMD); readLine(lineBuf, MAX_CMD);
if (runCommand(trim(lineBuf))) if (runCommand(trim(lineBuf))) break;
break;
} }
print("shell: goodbye\n"); print("shell: goodbye\n");
ExitProcess(0); ExitProcess(0);
} }
+11 -2
View File
@@ -82,7 +82,7 @@ uint64_t SysMouseRead(int* dx, int* dy, uint8_t* buttons); // 46
void SysMouseGetPos(int* x, int* y); // 47 void SysMouseGetPos(int* x, int* y); // 47
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// System Info & Process Management (48-53) // System Info & Process Management (48-65)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
uint64_t SysGetTick(void); // 48 uint64_t SysGetTick(void); // 48
uint32_t SysDebugPrint(const char* str); // 49 uint32_t SysDebugPrint(const char* str); // 49
@@ -100,7 +100,7 @@ uint32_t SysSetCwd(const char* path); // 56
uint32_t SysGetCwd(char* buf, size_t size); // 57 uint32_t SysGetCwd(char* buf, size_t size); // 57
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Process/Thread Helpers & Named Queues (58-63) // Process/Thread Helpers & Named Queues (58-65)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
uint32_t SysWaitThread(uint32_t tid); // 58 uint32_t SysWaitThread(uint32_t tid); // 58
uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut); // 59 uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut); // 59
@@ -108,3 +108,12 @@ uint32_t SysPublishQueue(const char* name, uint32_t handle); // 60
uint32_t SysOpenNamedQueue(const char* name, uint32_t* handleOut); // 61 uint32_t SysOpenNamedQueue(const char* name, uint32_t* handleOut); // 61
uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut); // 62 uint32_t SysQueueDuplicate(uint32_t handle, uint32_t targetPid, uint32_t* handleOut); // 62
uint32_t SysNamedQueueOwnerPid(const char* name, uint32_t* pidOut); // 63 uint32_t SysNamedQueueOwnerPid(const char* name, uint32_t* pidOut); // 63
uint32_t SysKillProcess(uint32_t pid, int exitCode); // 64
uint32_t SysGetProcessList(uint32_t* pidsOut, size_t maxCount, size_t* countOut); // 65
// ---------------------------------------------------------------------------
// Filesystem mutations (66-68)
// ---------------------------------------------------------------------------
uint32_t SysRenameFile(const char* oldPath, const char* newPath); // 66
uint32_t SysCreateDir(const char* path); // 67
uint32_t SysRemoveDir(const char* path); // 68
+6 -1
View File
@@ -64,5 +64,10 @@ SYS_PUBLISH_QUEUE EQU 60
SYS_OPEN_NAMED_QUEUE EQU 61 SYS_OPEN_NAMED_QUEUE EQU 61
SYS_QUEUE_DUPLICATE EQU 62 SYS_QUEUE_DUPLICATE EQU 62
SYS_NAMED_QUEUE_OWNER_PID EQU 63 SYS_NAMED_QUEUE_OWNER_PID EQU 63
SYS_KILL_PROCESS EQU 64
SYS_GET_PROCESS_LIST EQU 65
SYS_RENAME_FILE EQU 66
SYS_CREATE_DIR EQU 67
SYS_REMOVE_DIR EQU 68
SYS_MAX EQU 64 SYS_MAX EQU 69
+13
View File
@@ -199,4 +199,17 @@ SYSCALL_STUB2 OpenNamedQueue, SYS_OPEN_NAMED_QUEUE ; (const char* name, ui
SYSCALL_STUB2 QueueDuplicate, SYS_QUEUE_DUPLICATE ; (uint32_t handle, uint32_t targetPid, uint32_t* handleOut) -> KSTATUS SYSCALL_STUB2 QueueDuplicate, SYS_QUEUE_DUPLICATE ; (uint32_t handle, uint32_t targetPid, uint32_t* handleOut) -> KSTATUS
SYSCALL_STUB2 NamedQueueOwnerPid, SYS_NAMED_QUEUE_OWNER_PID ; (const char* name, uint32_t* pidOut) -> KSTATUS SYSCALL_STUB2 NamedQueueOwnerPid, SYS_NAMED_QUEUE_OWNER_PID ; (const char* name, uint32_t* pidOut) -> KSTATUS
; ---------------------------------------------------------------------------
; Process utilities
; ---------------------------------------------------------------------------
SYSCALL_STUB2 KillProcess, SYS_KILL_PROCESS ; (uint32_t pid, int exitCode) -> KSTATUS
SYSCALL_STUB2 GetProcessList, SYS_GET_PROCESS_LIST ; (uint32_t* pidsOut, size_t maxCount, size_t* countOut) -> KSTATUS
; ---------------------------------------------------------------------------
; Filesystem mutations
; ---------------------------------------------------------------------------
SYSCALL_STUB2 RenameFile, SYS_RENAME_FILE ; (const char* oldPath, const char* newPath) -> KSTATUS
SYSCALL_STUB2 CreateDir, SYS_CREATE_DIR ; (const char* path) -> KSTATUS
SYSCALL_STUB2 RemoveDir, SYS_REMOVE_DIR ; (const char* path) -> KSTATUS
END END
+73 -59
View File
@@ -4,88 +4,93 @@ Boots. Runs processes. Mimics Windows to some extent.
## what is it? ## what is it?
A 64-bit operating system kernel written from scratch. NT-Lite is a 64-bit operating system kernel written from scratch. It includes a UEFI bootloader, physical and virtual memory managers, a preemptive scheduler, a HAL, a VFS, a PE loader, and partial Win32 and POSIX layers. It boots into a shell.
UEFI bootloader, physical and virtual memory managers, preemptive scheduler, HAL, VFS, PE loader, partial Win32 layer.
It boots into a shell. The codebase is roughly 20k lines of C and assembly.
~20k lines of C and assembly.
## features ## features
- UEFI bootloader - UEFI bootloader
- physical memory manager - physical memory manager
- virtual memory manager with kernel pool - virtual memory manager with kernel pool
- HAL (screen, keyboard, mouse, timer, disk, PCI, APIC) - HAL for screen, keyboard, mouse, timer, disk, PCI, and APIC
- preemptive scheduler with priority levels - preemptive scheduler with priority levels
- processes and threads - processes and threads
- VFS with IRPs - VFS with IRPs
- drive-letter paths (`0:\apps\shell.exe`) - drive-letter paths such as `0:\apps\shell.exe`
- INI-based boot and driver configuration - INI-based boot and driver configuration
- PE import resolution and dynamic driver loading - PE import resolution and dynamic driver loading
- Win32 personality (partial) - partial Win32 personality
- POSIX personality (partial) - partial POSIX personality
## how 2 build ## architecture notes
* open vs x64 build tools as admin ### memory layout
* cd into this directory
* `mkimage.bat`
* `build.bat`
* `run.bat`(ide)/`rund.bat`(ide, debug)/`runa.bat`(ahci)/`runad.bat`(ahci, debug)
* enjoy
## how it looks when built NT-Lite uses a single virtual address space per booted system rather than a fully separate kernel address space and user address space. The distinction between kernel and user mode comes from page permissions and CPU privilege checks, not from having a different page table for the kernel.
I have some strong opinions on OS design. The kernel is mapped into the higher half of virtual memory and user processes only receive the regions they are explicitly granted. Kernel pages are marked supervisor-only, so user-mode code cannot read or write them directly. User pages are mapped with user permissions and are isolated from one another by the kernel’s allocator, VMM, and process creation path.
``` ### boot chain
efi/boot/
bootx64.efi
boot.ini
hal.dll
colonel.exe
drivers/
drivers.ini
ide.sys
ahci.sys
fat32.sys
syscall.dll
win32.dll
posix.dll
shell.exe
video.exe
```
## driver model The bootloader reads configuration, loads the core images, maps the higher-half layout, and hands control to `KernelMain`. After that, the kernel takes over interrupt setup, paging, scheduler bring-up, runtime driver loading, and user process launch.
drivers are PE32+ `.sys` files with an entry point preferably named DriverEntry accepting a constant pointer to HAL_API. ### drivers
drivers to be loaded have their path stored in `drivers.ini`, split into essential for boot (loaded by bootloader) and not required for boot (loaded by kernel). Drivers are PE32+ `.sys` files with a conventional `DriverEntry` export that accepts a pointer to `HAL_API`.
a disk driver fills in a `DISK_DRIVER` vtable and calls `IoRegisterDisk(index, &driver)`. Drivers are split into two groups:
a filesystem driver fills in an `IO_FILESYSTEM` vtable and calls `IoRegisterFilesystem(index, &fs, &vol)`.
currently shipping: AHCI (interrupts), IDE (PIO/DMA) and FAT32. - essential boot drivers, which are loaded before the kernel starts doing real work
- runtime drivers, which are loaded later from disk once storage and the VFS are available
The driver model is intentionally small. Disk drivers expose a `DISK_DRIVER` vtable and register with `IoRegisterDisk(index, &driver)`. Filesystem drivers expose an `IO_FILESYSTEM` vtable and register with `IoRegisterFilesystem(index, &fs, &vol)`.
Currently shipping: AHCI (interrupts), IDE (PIO/DMA), and FAT32.
### I/O and filesystems
All file access flows through the VFS layer. That keeps disk access, filesystem parsing, and path translation separate instead of spreading storage logic across the rest of the kernel.
Paths are not interpreted as Windows paths by default. A drive such as `0:\` is a mounted namespace, while the Win32 and POSIX personalities translate their own path styles into the same underlying VFS.
### processes, threads, and syscalls
User processes are launched from PE images, imports are resolved before the first thread is queued, and the syscall layer provides the boundary between user code and kernel services. `syscall.dll` is the shared entry point for the supported personalities.
The scheduler is preemptive and priority-based, so threads do not depend on cooperative yielding to keep the system responsive. Process state is tracked separately from thread state, which allows a process to exit, be reaped, or be restarted by `init` without bringing down the rest of the system.
### personalities
The Win32 personality supports programs that expect drive letters, familiar process APIs, and a Windows-style runtime. The POSIX personality maps the same kernel primitives into a tree-shaped namespace and a different userspace contract.
## how it boots ## how it boots
1. UEFI loads `boot.ini`, reads `drivers.ini` 1. UEFI loads `boot.ini` and reads `drivers.ini`
2. bootloader maps kernel, HAL, and boot drivers into the upper half 2. the bootloader maps the kernel, HAL, and boot drivers into the upper half
3. direct physical window at `0xFFFF880000000000` 3. a direct physical window is mapped at `0xFFFF880000000000`
4. jumps to `KernelMain` 4. control passes to `KernelMain`
5. PMM, VMM, GDT, IDT, HAL, IRQs, scheduler 5. PMM, VMM, GDT, IDT, HAL, IRQs, and scheduler initialization run
6. loads runtime drivers from disk 6. runtime drivers are loaded from disk
7. launches `shell.exe` 7. `shell.exe` is launched
## paths (because they're different) ## how to build
``` - open the VS x64 build tools as administrator
- change into this directory
- run `mkimage.bat`
- run `build.bat`
- use one of `run.bat` (IDE), `rund.bat` (IDE, debug), `runa.bat` (AHCI), or `runad.bat` (AHCI, debug)
## paths
```text
"apps\shell.exe" -> current working directory "apps\shell.exe" -> current working directory
"0:\apps\shell.exe" -> drive 0 "0:\apps\shell.exe" -> drive 0
"2:\data\config.ini" -> drive 2 "2:\data\config.ini" -> drive 2
``` ```
Drives 0-7. A drive only works if a filesystem registered on it. Drives 0-7 are available. A drive only works if a filesystem is registered on it. The Win32 personality translates drive letters to drive numbers, while the POSIX personality maps drives onto a tree.
The Win32 personality translates drive letters to drive numbers, the POSIX personality maps drives onto a tree.
## screenshots ## screenshots
@@ -93,7 +98,7 @@ The Win32 personality translates drive letters to drive numbers, the POSIX perso
<details><summary>Boot logs</summary> <details><summary>Boot logs</summary>
``` ```text
BdsDxe: loading Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x4,0x0)/Sata(0x0,0xFFFF,0x0) BdsDxe: loading Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x4,0x0)/Sata(0x0,0xFFFF,0x0)
BdsDxe: starting Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x4,0x0)/Sata(0x0,0xFFFF,0x0) BdsDxe: starting Boot0001 "UEFI QEMU HARDDISK QM00005 " from PciRoot(0x0)/Pci(0x4,0x0)/Sata(0x0,0xFFFF,0x0)
1 1
@@ -204,7 +209,7 @@ W_Init: Init WADfiles.
Shareware! Shareware!
=========================================================================== ===========================================================================
M_Init: Init miscellaneous info. M_Init: Init miscellaneous info.
R_Init: Init DOOM refresh daemon - [.. ] R_Init: Init Doom refresh daemon - [.. ]
InitTextures InitTextures
InitFlats........ InitFlats........
InitSprites InitSprites
@@ -289,14 +294,23 @@ A: `doom.exe`
Q: Is the Win32 layer real? Q: Is the Win32 layer real?
A: Real enough to load binaries that expect it. Not real enough to run Notepad. A: Real enough to load binaries that expect it. Not real enough to run Notepad.
Q: Is the scheduler preemptive? Q: Are processes isolated?
A: As much as it will ever be. A: No. But IPC is free.
Q: Will it corrupt my data?
A: It shouldn't.
Q: Is this finished? Q: Is this finished?
A: Not in the slightest. A: No.
Will it corrupt my data?
A: It shouldn't.
Q: Why is the path syntax so weird?
A: It makes sense to me.
Q: Why are the logs so verbose?
A: How else do I debug it?
Q: What should I read first?
A: This document.
Q: Can I use this in production? Q: Can I use this in production?
A: Don't. A: Don't.