duct taped posix.dll (just like microsoft), new cursor, more info in the fault handlers, processes now have a parse-it-yourself cmdline, add SYS_WAIT_THREAD and SYS_GET_PROCESS_PID, writing still doesn't work
This commit is contained in:
1 parent
a37f5b1581
commit
b5c831f593
26 files changed
+1107
-56
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -13,6 +13,7 @@ mkdir "obj\kernel" 2>nul
|
|||||||
mkdir "obj\hal" 2>nul
|
mkdir "obj\hal" 2>nul
|
||||||
mkdir "obj\syscall" 2>nul
|
mkdir "obj\syscall" 2>nul
|
||||||
mkdir "obj\win32" 2>nul
|
mkdir "obj\win32" 2>nul
|
||||||
|
mkdir "obj\posix" 2>nul
|
||||||
mkdir "obj\drivers" 2>nul
|
mkdir "obj\drivers" 2>nul
|
||||||
mkdir "obj\shell" 2>nul
|
mkdir "obj\shell" 2>nul
|
||||||
mkdir "obj\video" 2>nul
|
mkdir "obj\video" 2>nul
|
||||||
@@ -80,6 +81,14 @@ for %%f in (%SRC%\win32\*.c) do (
|
|||||||
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^
|
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^
|
||||||
/OUT:"bin\win32.dll" bin\syscall.lib obj\libc.obj obj\win32\*.obj || exit /b 1
|
/OUT:"bin\win32.dll" bin\syscall.lib obj\libc.obj obj\win32\*.obj || exit /b 1
|
||||||
|
|
||||||
|
:: ---- Compile posix.dll ----
|
||||||
|
echo Compiling posix.dll...
|
||||||
|
for %%f in (%SRC%\posix\*.c) do (
|
||||||
|
cl %CFLAGS% /c /Fo"obj\posix\%%~nf.obj" "%%f" || exit /b 1
|
||||||
|
)
|
||||||
|
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL ^
|
||||||
|
/OUT:"bin\posix.dll" bin\syscall.lib obj\libc.obj obj\posix\*.obj || exit /b 1
|
||||||
|
|
||||||
:: ---- Compile drivers ----
|
:: ---- Compile drivers ----
|
||||||
echo Compiling drivers...
|
echo Compiling drivers...
|
||||||
cl %CFLAGS% /I"%SRC%\hal" /c /Fo"obj\drivers\ide.obj" "%SRC%\drivers\ide\ide.c" || exit /b 1
|
cl %CFLAGS% /I"%SRC%\hal" /c /Fo"obj\drivers\ide.obj" "%SRC%\drivers\ide\ide.c" || exit /b 1
|
||||||
@@ -129,6 +138,7 @@ copy /y "bin\kernel.exe" "%DEST%\colonel.exe"
|
|||||||
copy /y "bin\hal.dll" "%DEST%\"
|
copy /y "bin\hal.dll" "%DEST%\"
|
||||||
copy /y "bin\syscall.dll" "%DEST%\"
|
copy /y "bin\syscall.dll" "%DEST%\"
|
||||||
copy /y "bin\win32.dll" "%DEST%\"
|
copy /y "bin\win32.dll" "%DEST%\"
|
||||||
|
copy /y "bin\posix.dll" "%DEST%\"
|
||||||
copy /y "bin\ide.sys" "%DEST%\drivers\"
|
copy /y "bin\ide.sys" "%DEST%\drivers\"
|
||||||
copy /y "bin\ahci.sys" "%DEST%\drivers\"
|
copy /y "bin\ahci.sys" "%DEST%\drivers\"
|
||||||
copy /y "bin\fat32.sys" "%DEST%\drivers\"
|
copy /y "bin\fat32.sys" "%DEST%\drivers\"
|
||||||
|
|||||||
+56
-26
@@ -149,29 +149,37 @@ static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
|
|||||||
// 0 = transparent 1 = black outline 2 = white fill
|
// 0 = transparent 1 = black outline 2 = white fill
|
||||||
// The hotspot is at (0,0) (top-left pixel = tip of the arrow).
|
// The hotspot is at (0,0) (top-left pixel = tip of the arrow).
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#define CURSOR_W 12
|
#define CURSOR_W 16
|
||||||
#define CURSOR_H 19
|
#define CURSOR_H 25
|
||||||
|
|
||||||
static const uint8_t gCursorBitmap[CURSOR_H][CURSOR_W] = {
|
// Each entry is 0xAARRGGBB stored as a uint32_t.
|
||||||
{1,0,0,0,0,0,0,0,0,0,0,0},
|
// Pixels with brightness < 24 are fully transparent.
|
||||||
{1,1,0,0,0,0,0,0,0,0,0,0},
|
static const unsigned int gCursorPixels[CURSOR_H][CURSOR_W] = {
|
||||||
{1,2,1,0,0,0,0,0,0,0,0,0},
|
{ 0x7F575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,1,0,0,0,0,0,0,0,0},
|
{ 0xFFFAFAFA, 0x855D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,1,0,0,0,0,0,0,0},
|
{ 0xFFFEFEFE, 0xFFFFFFFF, 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,1,0,0,0,0,0,0},
|
{ 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFFFFFF, 0x8A626262, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,2,1,0,0,0,0,0},
|
{ 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8D656565, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,2,2,1,0,0,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8E666666, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,2,2,2,1,0,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x91696969, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,2,2,2,2,1,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x946C6C6C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,2,2,2,2,2,1,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x956D6D6D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,2,2,1,1,1,1,1,1},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,2,1,2,2,1,0,0,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9B737373, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,2,1,0,1,2,2,1,0,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9D757575, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,2,1,0,0,0,1,2,2,1,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0xA0787878, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
{1,1,0,0,0,0,0,1,2,2,1,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFF9F9F9, 0xFFFFFFFF, 0xA1797979, 0x00000000, 0x00000000 },
|
||||||
{1,0,0,0,0,0,0,0,1,2,1,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xAE868686, 0x00000000 },
|
||||||
{0,0,0,0,0,0,0,0,0,1,1,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFFFFFF, 0xFDD5D5D5, 0xF4CCCCCC, 0xF4CCCCCC, 0xF4CCCCCC, 0xF3CBCBCB, 0xF1C9C9C9, 0xFAD2D2D2, 0x7E565656 },
|
||||||
{0,0,0,0,0,0,0,0,0,0,0,0},
|
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFEFEFEF, 0xFFFCFCFC, 0xFFFFFFFF, 0x9A727272, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x7F575757, 0x8F676767, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE0E0E0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0xFFEBEBEB, 0xFFFFFFFF, 0xFFFFFFFF, 0x81595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0xFFFDFDFD, 0xFFFFFFFF, 0x8C646464, 0x00000000, 0x00000000, 0x00000000, 0xB78F8F8F, 0xFFFFFFFF, 0xFFFEFEFE, 0xECC4C4C4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50282828, 0xFFF9F9F9, 0xFFFFFFFF, 0xFFFFFFFF, 0x623A3A3A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xD8B0B0B0, 0xFFFFFFFF, 0xFFFFFFFF, 0xCDA5A5A5, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6C444444, 0xFFFFFFFF, 0xFFFCFCFC, 0xFFF4F4F4, 0x4D252525, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF2CACACA, 0xFFFFFFFF, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
|
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8E666666, 0xEFC7C7C7, 0x7D555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Saved framebuffer pixels beneath the current cursor position.
|
// Saved framebuffer pixels beneath the current cursor position.
|
||||||
@@ -205,6 +213,28 @@ static void CursorRestoreBg(int x, int y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alpha-blend a cursor pixel over a framebuffer pixel.
|
||||||
|
// Framebuffer format: 0x00RRGGBB (high byte ignored / always 0).
|
||||||
|
// Cursor pixel format: 0xAARRGGBB.
|
||||||
|
static inline uint32_t BlendPixel(uint32_t dst, uint32_t src) {
|
||||||
|
uint8_t a = (uint8_t)(src >> 24);
|
||||||
|
if (a == 0) return dst;
|
||||||
|
if (a == 255) return src & 0x00FFFFFFu;
|
||||||
|
|
||||||
|
uint8_t sr = (uint8_t)(src >> 16);
|
||||||
|
uint8_t sg = (uint8_t)(src >> 8);
|
||||||
|
uint8_t sb = (uint8_t)(src );
|
||||||
|
uint8_t dr = (uint8_t)(dst >> 16);
|
||||||
|
uint8_t dg = (uint8_t)(dst >> 8);
|
||||||
|
uint8_t db = (uint8_t)(dst );
|
||||||
|
|
||||||
|
uint8_t na = 255 - a;
|
||||||
|
uint8_t or_ = (uint8_t)((a * sr + na * dr) >> 8);
|
||||||
|
uint8_t og = (uint8_t)((a * sg + na * dg) >> 8);
|
||||||
|
uint8_t ob = (uint8_t)((a * sb + na * db) >> 8);
|
||||||
|
return ((uint32_t)or_ << 16) | ((uint32_t)og << 8) | ob;
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the arrow sprite at (x,y), saving the background first.
|
// Draw the arrow sprite at (x,y), saving the background first.
|
||||||
// Called from hal_mouse.c via extern declaration.
|
// Called from hal_mouse.c via extern declaration.
|
||||||
void HalScreenDrawCursor(int x, int y) {
|
void HalScreenDrawCursor(int x, int y) {
|
||||||
@@ -214,12 +244,12 @@ void HalScreenDrawCursor(int x, int y) {
|
|||||||
for (int row = 0; row < CURSOR_H; row++) {
|
for (int row = 0; row < CURSOR_H; row++) {
|
||||||
int fy = y + row;
|
int fy = y + row;
|
||||||
for (int col = 0; col < CURSOR_W; col++) {
|
for (int col = 0; col < CURSOR_W; col++) {
|
||||||
uint8_t p = gCursorBitmap[row][col];
|
uint32_t src = gCursorPixels[row][col];
|
||||||
if (!p) continue;
|
if ((src >> 24) == 0) continue; // fully transparent, skip
|
||||||
int fx = x + col;
|
int fx = x + col;
|
||||||
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) {
|
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) {
|
||||||
uint32_t color = (p == 1) ? 0x000000u : 0xFFFFFFu;
|
uint32_t* dst = &gFb[fy * gStride + fx];
|
||||||
gFb[fy * gStride + fx] = color;
|
*dst = BlendPixel(*dst, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ typedef struct PROCESS {
|
|||||||
uint32_t ProcessId;
|
uint32_t ProcessId;
|
||||||
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
|
char Name[PROCESS_NAME_MAX]; /* UTF-8 */
|
||||||
|
|
||||||
|
// Windows-style flat command line, stored at launch by SysCreateProcess.
|
||||||
|
// The initial thread receives this pointer as its UserArg so _start can
|
||||||
|
// parse it into argc/argv without an extra syscall.
|
||||||
|
char Cmdline[512];
|
||||||
|
|
||||||
char CurrentDirectory[512];
|
char CurrentDirectory[512];
|
||||||
SPINLOCK CwdLock;
|
SPINLOCK CwdLock;
|
||||||
|
|
||||||
|
|||||||
+73
-16
@@ -37,35 +37,92 @@ KeSyscallEntry PROC
|
|||||||
KeSyscallEntry ENDP
|
KeSyscallEntry ENDP
|
||||||
|
|
||||||
; ---------------------------------------------------------------------------
|
; ---------------------------------------------------------------------------
|
||||||
; Fault stubs - print a message and halt. A real kernel would do a bugcheck.
|
; Fault stubs - print a message and halt.
|
||||||
; ---------------------------------------------------------------------------
|
; ---------------------------------------------------------------------------
|
||||||
EXTERN KePanic:PROC
|
EXTERN KePanic:PROC
|
||||||
|
|
||||||
FaultMsg DB "KERNEL FAULT: vector %d at RIP %016llx", 0Ah, 0
|
.data
|
||||||
|
; We define separate strings to maximize the info we can print
|
||||||
|
StrKernel DB "Kernel Mode", 0
|
||||||
|
StrUser DB "User Mode", 0
|
||||||
|
|
||||||
|
MsgFaultDE DB "PANIC: Divide Error (#0) | RIP: %016llx | Mode: %s", 0Ah, 0
|
||||||
|
MsgFaultGP DB "PANIC: General Protection (#13) | RIP: %016llx | Code: %04x | Mode: %s", 0Ah, 0
|
||||||
|
MsgFaultPF DB "PANIC: Page Fault (#14) | RIP: %016llx | Code: %04x | CR2: %016llx | %s", 0Ah, 0
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
; ---------------------------------------------------------
|
||||||
|
; Divide Error (#0) - NO Error Code pushed by CPU
|
||||||
|
; Stack: [rsp] = RIP, [rsp+8] = CS, [rsp+16] = RFLAGS
|
||||||
|
; ---------------------------------------------------------
|
||||||
KeFaultDE PROC
|
KeFaultDE PROC
|
||||||
sub rsp, 28h
|
mov rdx, [rsp] ; Arg 2: RIP
|
||||||
lea rcx, FaultMsg
|
|
||||||
mov rdx, 0
|
; Determine User/Kernel mode from CS
|
||||||
pop r8 ; RIP from the fault frame
|
mov rax, [rsp + 8] ; Read CS
|
||||||
|
and rax, 3 ; Isolate the bottom 2 bits (RPL)
|
||||||
|
lea r8, StrKernel
|
||||||
|
lea r9, StrUser
|
||||||
|
test rax, rax ; If 0, it's Kernel. If >0, it's User.
|
||||||
|
cmovnz r8, r9 ; Arg 3: Mode String (moves r9 into r8 if Not Zero)
|
||||||
|
|
||||||
|
lea rcx, MsgFaultDE ; Arg 1: Format string
|
||||||
|
|
||||||
|
; Safely align stack and allocate shadow space for the call
|
||||||
|
and rsp, -16 ; Force 16-byte ABI stack alignment
|
||||||
|
sub rsp, 20h ; Allocate 32 bytes of shadow space
|
||||||
call KePanic
|
call KePanic
|
||||||
KeFaultDE ENDP
|
KeFaultDE ENDP
|
||||||
|
|
||||||
|
; ---------------------------------------------------------
|
||||||
|
; General Protection Fault (#13) - Error Code PUSHED
|
||||||
|
; Stack: [rsp] = ErrCode, [rsp+8] = RIP, [rsp+16] = CS
|
||||||
|
; ---------------------------------------------------------
|
||||||
KeFaultGP PROC
|
KeFaultGP PROC
|
||||||
sub rsp, 28h
|
mov r8, [rsp] ; Arg 3: Error Code
|
||||||
lea rcx, FaultMsg
|
mov rdx, [rsp + 8] ; Arg 2: RIP
|
||||||
mov rdx, 13
|
|
||||||
pop rax ; error code
|
; Determine User/Kernel mode from CS
|
||||||
pop r8 ; RIP
|
mov rax, [rsp + 16] ; Read CS
|
||||||
|
and rax, 3
|
||||||
|
lea r9, StrKernel
|
||||||
|
lea r10, StrUser
|
||||||
|
test rax, rax
|
||||||
|
cmovnz r9, r10 ; Arg 4: Mode String
|
||||||
|
|
||||||
|
lea rcx, MsgFaultGP ; Arg 1: Format string
|
||||||
|
|
||||||
|
and rsp, -16 ; Align stack
|
||||||
|
sub rsp, 20h ; 32 bytes shadow space
|
||||||
call KePanic
|
call KePanic
|
||||||
KeFaultGP ENDP
|
KeFaultGP ENDP
|
||||||
|
|
||||||
|
; ---------------------------------------------------------
|
||||||
|
; Page Fault (#14) - Error Code PUSHED
|
||||||
|
; Stack: [rsp] = ErrCode, [rsp+8] = RIP, [rsp+16] = CS
|
||||||
|
; ---------------------------------------------------------
|
||||||
KeFaultPF PROC
|
KeFaultPF PROC
|
||||||
sub rsp, 28h
|
mov r8, [rsp] ; Arg 3: Error Code
|
||||||
lea rcx, FaultMsg
|
mov rdx, [rsp + 8] ; Arg 2: RIP
|
||||||
mov rdx, 14
|
mov r9, cr2 ; Arg 4: CR2 (The faulting virtual address)
|
||||||
pop rax ; error code
|
|
||||||
pop r8 ; RIP
|
; Determine User/Kernel mode from CS
|
||||||
|
mov rax, [rsp + 16] ; Read CS
|
||||||
|
and rax, 3
|
||||||
|
lea r10, StrKernel
|
||||||
|
lea r11, StrUser
|
||||||
|
test rax, rax
|
||||||
|
cmovnz r10, r11 ; r10 now holds the Mode String
|
||||||
|
|
||||||
|
lea rcx, MsgFaultPF ; Arg 1: Format string
|
||||||
|
|
||||||
|
; We have 5 arguments for PF! (rcx, rdx, r8, r9, and the stack)
|
||||||
|
; x64 ABI dictates the 5th argument goes on the stack just above the shadow space.
|
||||||
|
and rsp, -16 ; Align stack
|
||||||
|
sub rsp, 30h ; 32 bytes shadow space + 16 bytes for 5th arg and padding
|
||||||
|
mov [rsp + 20h], r10 ; Arg 5: Mode String (placed exactly after shadow space)
|
||||||
|
|
||||||
call KePanic
|
call KePanic
|
||||||
KeFaultPF ENDP
|
KeFaultPF ENDP
|
||||||
|
|
||||||
|
|||||||
@@ -683,14 +683,15 @@ static uint64_t SyscallDebugPrint(uint64_t a0, uint64_t a1, uint64_t a2,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// SYS_CREATE_PROCESS (40) a0=*path a1=*handleOut
|
// SYS_CREATE_PROCESS (40) a0=*path a1=*handleOut a2=*cmdline (optional)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||||
uint64_t a3, uint64_t a4, uint64_t a5) {
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||||
(void)a2; (void)a3; (void)a4; (void)a5;
|
(void)a3; (void)a4; (void)a5;
|
||||||
|
|
||||||
const char* path = (const char*)(uintptr_t)a0;
|
const char* path = (const char*)(uintptr_t)a0;
|
||||||
uint32_t* handleOut = (uint32_t*)(uintptr_t)a1;
|
uint32_t* handleOut = (uint32_t*)(uintptr_t)a1;
|
||||||
|
const char* cmdline = (const char*)(uintptr_t)a2; // NULL = no args
|
||||||
if (!path || !handleOut) return STATUS_INVALID_PARAMETER;
|
if (!path || !handleOut) return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Read the executable from disk.
|
// Read the executable from disk.
|
||||||
@@ -712,16 +713,33 @@ static uint64_t SyscallCreateProcess(uint64_t a0, uint64_t a1, uint64_t a2,
|
|||||||
s = KeCreateProcess(path, &proc);
|
s = KeCreateProcess(path, &proc);
|
||||||
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
|
if (!KSUCCESS(s)) { MmFreeUserMemory(mem, size); return s; }
|
||||||
|
|
||||||
|
// Store the command line in the process so _start can pick it up.
|
||||||
|
// If no cmdline was supplied default to just the image path.
|
||||||
|
if (cmdline && cmdline[0])
|
||||||
|
strncpy(proc->Cmdline, cmdline, sizeof(proc->Cmdline) - 1);
|
||||||
|
else
|
||||||
|
strncpy(proc->Cmdline, path, sizeof(proc->Cmdline) - 1);
|
||||||
|
proc->Cmdline[sizeof(proc->Cmdline) - 1] = '\0';
|
||||||
|
|
||||||
|
// proc->Cmdline is kernel pool - CPL=3 code can't read it.
|
||||||
|
// Copy into user memory so the initial thread can access it directly.
|
||||||
|
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
|
||||||
|
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
|
||||||
|
if (!userCmdline) { ObDereferenceObject(&proc->Header); return STATUS_NO_MEMORY; }
|
||||||
|
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
|
||||||
|
|
||||||
// Map the PE image into memory.
|
// Map the PE image into memory.
|
||||||
uintptr_t imageBase = 0;
|
uintptr_t imageBase = 0;
|
||||||
s = LdrLoadImage(mem, size, &imageBase, true);
|
s = LdrLoadImage(mem, size, &imageBase, true);
|
||||||
MmFreeUserMemory(mem, size);
|
MmFreeUserMemory(mem, size);
|
||||||
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
||||||
|
|
||||||
// Spin up the initial thread at the PE entry point.
|
// Spin up the initial thread. Pass userCmdline as UserArg so _start
|
||||||
|
// receives it directly in RCX without an extra syscall.
|
||||||
THREAD* thread;
|
THREAD* thread;
|
||||||
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
uintptr_t entryPoint = LdrGetEntryPoint(imageBase);
|
||||||
s = KeCreateThread(proc, entryPoint, 0, PriorityNormal, &thread);
|
s = KeCreateThread(proc, entryPoint, (uintptr_t)userCmdline,
|
||||||
|
PriorityNormal, &thread);
|
||||||
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
if (!KSUCCESS(s)) { ObDereferenceObject(&proc->Header); return s; }
|
||||||
|
|
||||||
// Give the caller a handle to the new process so they can wait on it.
|
// Give the caller a handle to the new process so they can wait on it.
|
||||||
@@ -850,6 +868,69 @@ static uint64_t SyscallGetCwd(uint64_t a0, uint64_t a1, uint64_t a2,
|
|||||||
return IoGetCwd(buf, size);
|
return IoGetCwd(buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// SYS_WAIT_THREAD (53) a0=tid
|
||||||
|
// Blocks the caller until the thread with the given TID exits.
|
||||||
|
// Searches the current process's thread list; cross-process join is not
|
||||||
|
// supported in v1 (POSIX pthread_join is always intra-process).
|
||||||
|
// Returns STATUS_NOT_FOUND if the TID is unknown (already reaped or never
|
||||||
|
// existed) - treat the same as a successful join at the POSIX layer.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
static uint64_t SyscallWaitThread(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;
|
||||||
|
uint32_t targetTid = (uint32_t)a0;
|
||||||
|
|
||||||
|
PROCESS* proc = KeGetCurrentProcess();
|
||||||
|
if (!proc) return STATUS_FAILURE;
|
||||||
|
|
||||||
|
// Walk the thread list under the spinlock to find the target, then bump
|
||||||
|
// its ExitEvent refcount so the event survives even if the thread exits
|
||||||
|
// and is destroyed while we wait.
|
||||||
|
EVENT* exitEvent = (EVENT*)0;
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&proc->HandleLock);
|
||||||
|
LIST_ENTRY* entry = proc->ThreadList.Fwd;
|
||||||
|
while (entry != &proc->ThreadList) {
|
||||||
|
THREAD* t = CONTAINING_RECORD(entry, THREAD, ProcessLink);
|
||||||
|
if (t->ThreadId == targetTid && t->ExitEvent) {
|
||||||
|
exitEvent = t->ExitEvent;
|
||||||
|
ObReferenceObject(&exitEvent->Header); // keep event alive
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entry = entry->Fwd;
|
||||||
|
}
|
||||||
|
KeReleaseSpinlock(&proc->HandleLock, flags);
|
||||||
|
|
||||||
|
if (!exitEvent) {
|
||||||
|
// TID not found - either already exited and reaped, or invalid.
|
||||||
|
// Treat as success at the POSIX layer (join on an already-dead thread).
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSTATUS s = KeWaitForEvent(exitEvent);
|
||||||
|
ObDereferenceObject(&exitEvent->Header);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// SYS_GET_PROCESS_PID (54) a0=procHandle a1=*pidOut
|
||||||
|
// Returns the numeric PID of a process identified by handle.
|
||||||
|
// Used by posix_spawn to map the kernel handle back to a pid_t for waitpid.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
static uint64_t SyscallGetProcessPid(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 = (PROCESS*)HandleToObject((uint32_t)a0, ObjectTypeProcess);
|
||||||
|
uint32_t* pidOut = (uint32_t*)(uintptr_t)a1;
|
||||||
|
if (!proc) return STATUS_INVALID_HANDLE;
|
||||||
|
if (!pidOut) { ObDereferenceObject(&proc->Header); return STATUS_INVALID_PARAMETER; }
|
||||||
|
|
||||||
|
*pidOut = proc->ProcessId;
|
||||||
|
ObDereferenceObject(&proc->Header);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Dispatch table - index must match SYS_* in syscall_nr.h exactly.
|
// Dispatch table - index must match SYS_* in syscall_nr.h exactly.
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -910,6 +991,8 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
|
|||||||
/* 50 SYS_READ_DIR */ SyscallReadDir,
|
/* 50 SYS_READ_DIR */ SyscallReadDir,
|
||||||
/* 51 SYS_SET_CWD */ SyscallSetCwd,
|
/* 51 SYS_SET_CWD */ SyscallSetCwd,
|
||||||
/* 52 SYS_GET_CWD */ SyscallGetCwd,
|
/* 52 SYS_GET_CWD */ SyscallGetCwd,
|
||||||
|
/* 53 SYS_WAIT_THREAD */ SyscallWaitThread,
|
||||||
|
/* 54 SYS_GET_PROCESS_PID */ SyscallGetProcessPid,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#include <ke_thread.h>
|
#include <ke_thread.h>
|
||||||
#include <ke_sched.h>
|
#include <ke_sched.h>
|
||||||
|
#include <ke_event.h>
|
||||||
#include <mm_virt.h>
|
#include <mm_virt.h>
|
||||||
#include <ob_object.h>
|
#include <ob_object.h>
|
||||||
|
|
||||||
@@ -12,6 +13,11 @@ extern void KeThreadEntry(void);
|
|||||||
static void ThreadDestroy(OBJECT* obj) {
|
static void ThreadDestroy(OBJECT* obj) {
|
||||||
THREAD* thread = CONTAINING_RECORD(obj, THREAD, Header);
|
THREAD* thread = CONTAINING_RECORD(obj, THREAD, Header);
|
||||||
|
|
||||||
|
if (thread->ExitEvent) {
|
||||||
|
ObDereferenceObject(&thread->ExitEvent->Header);
|
||||||
|
thread->ExitEvent = (EVENT*)0;
|
||||||
|
}
|
||||||
|
|
||||||
if (thread->Owner) {
|
if (thread->Owner) {
|
||||||
uintptr_t flags = KeAcquireSpinlock(&thread->Owner->HandleLock);
|
uintptr_t flags = KeAcquireSpinlock(&thread->Owner->HandleLock);
|
||||||
ListRemove(&thread->ProcessLink);
|
ListRemove(&thread->ProcessLink);
|
||||||
@@ -100,6 +106,19 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
|
|||||||
ObReferenceObject(&owner->Header);
|
ObReferenceObject(&owner->Header);
|
||||||
thread->Owner = owner;
|
thread->Owner = owner;
|
||||||
|
|
||||||
|
// Allocate the exit event before the thread becomes visible. Manual-reset
|
||||||
|
// so multiple joiners (if any) all wake up when the thread exits.
|
||||||
|
EVENT* exitEvent;
|
||||||
|
KSTATUS s = KeEventCreate(EventManualReset, 0, &exitEvent);
|
||||||
|
if (!KSUCCESS(s)) {
|
||||||
|
MmFreeUserMemory(ustack, USTACK_SIZE);
|
||||||
|
MmFreePool(kstack);
|
||||||
|
ObDereferenceObject(&owner->Header);
|
||||||
|
ObDereferenceObject(&thread->Header);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
thread->ExitEvent = exitEvent;
|
||||||
|
|
||||||
uintptr_t flags = KeAcquireSpinlock(&owner->HandleLock);
|
uintptr_t flags = KeAcquireSpinlock(&owner->HandleLock);
|
||||||
ListInsertTail(&owner->ThreadList, &thread->ProcessLink);
|
ListInsertTail(&owner->ThreadList, &thread->ProcessLink);
|
||||||
owner->ThreadCount++;
|
owner->ThreadCount++;
|
||||||
@@ -114,6 +133,11 @@ __declspec(dllexport) void KeExitThread(THREAD* thread, int code) {
|
|||||||
(void)code;
|
(void)code;
|
||||||
if (!thread) return;
|
if (!thread) return;
|
||||||
|
|
||||||
|
// Wake any pthread_join waiters before the thread is marked dead and
|
||||||
|
// potentially freed. Manual-reset means all concurrent waiters unblock.
|
||||||
|
if (thread->ExitEvent)
|
||||||
|
KeSignalEvent(thread->ExitEvent);
|
||||||
|
|
||||||
thread->State = ThreadDead;
|
thread->State = ThreadDead;
|
||||||
|
|
||||||
KeYield();
|
KeYield();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <ob_object.h>
|
#include <ob_object.h>
|
||||||
#include <ke_list.h>
|
#include <ke_list.h>
|
||||||
#include <ke_process.h>
|
#include <ke_process.h>
|
||||||
|
#include <ke_event.h>
|
||||||
|
|
||||||
#define KSTACK_SIZE (64 * 1024) /* 64 KB kernel stack */
|
#define KSTACK_SIZE (64 * 1024) /* 64 KB kernel stack */
|
||||||
#define USTACK_SIZE (512 * 1024) /* 512 KB user stack */
|
#define USTACK_SIZE (512 * 1024) /* 512 KB user stack */
|
||||||
@@ -67,6 +68,11 @@ typedef struct THREAD {
|
|||||||
|
|
||||||
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
|
uintptr_t SchedLockFlags; // Stores RFLAGS while thread is context switched out
|
||||||
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
|
void* UserStackBase; // Tracks user stack allocation to prevent memory leaks
|
||||||
|
|
||||||
|
// Signaled (manual-reset) when the thread exits.
|
||||||
|
// Allocated in KeCreateThread, signaled in KeExitThread before teardown.
|
||||||
|
// Waiters block on this via SYS_WAIT_THREAD -> KeWaitForEvent.
|
||||||
|
EVENT* ExitEvent;
|
||||||
} THREAD;
|
} THREAD;
|
||||||
|
|
||||||
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
|
__declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, uintptr_t arg,
|
||||||
|
|||||||
+13
-1
@@ -238,6 +238,18 @@ static PROCESS* LaunchUserProcess(const char* filePath) {
|
|||||||
}
|
}
|
||||||
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
|
DbgPrint("Created process (PID %u) with command line: %s\n", proc->ProcessId, filePath);
|
||||||
|
|
||||||
|
strncpy(proc->Cmdline, filePath, sizeof(proc->Cmdline) - 1);
|
||||||
|
|
||||||
|
// proc->Cmdline is kernel pool (ffffc000...) - not readable at CPL=3.
|
||||||
|
// Copy into user memory before handing the pointer to the initial thread.
|
||||||
|
size_t cmdlineLen = strlen(proc->Cmdline) + 1;
|
||||||
|
char* userCmdline = (char*)MmAllocateUserMemory(cmdlineLen);
|
||||||
|
if (!userCmdline) {
|
||||||
|
DbgPrint("LaunchUserProcess: Failed to allocate user cmdline buffer.\n");
|
||||||
|
return (PROCESS*)0;
|
||||||
|
}
|
||||||
|
memcpy(userCmdline, proc->Cmdline, cmdlineLen);
|
||||||
|
|
||||||
// Parse the raw PE file and map it into executable memory
|
// Parse the raw PE file and map it into executable memory
|
||||||
uintptr_t imageBase = 0;
|
uintptr_t imageBase = 0;
|
||||||
s = LdrLoadImage(mem, size, &imageBase, true);
|
s = LdrLoadImage(mem, size, &imageBase, true);
|
||||||
@@ -248,7 +260,7 @@ static PROCESS* LaunchUserProcess(const char* filePath) {
|
|||||||
DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint);
|
DbgPrint("LaunchUserProcess: PE Image mapped! Base 0x%llx, Entry 0x%llX\n", imageBase, entryPoint);
|
||||||
|
|
||||||
// Create the main thread, pointing it to the PE Entry Point
|
// Create the main thread, pointing it to the PE Entry Point
|
||||||
s = KeCreateThread(proc, (uintptr_t)entryPoint, 0, PriorityNormal, &thread);
|
s = KeCreateThread(proc, (uintptr_t)entryPoint, (uintptr_t)userCmdline, PriorityNormal, &thread);
|
||||||
if (!KSUCCESS(s)) {
|
if (!KSUCCESS(s)) {
|
||||||
DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
DbgPrint("LaunchUserProcess: Failed to create thread. Status: %d\n", s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,7 +130,19 @@
|
|||||||
#define SYS_SET_CWD 51 // (const char* path) -> KSTATUS
|
#define SYS_SET_CWD 51 // (const char* path) -> KSTATUS
|
||||||
#define SYS_GET_CWD 52 // (char* buf, size_t size) -> KSTATUS
|
#define SYS_GET_CWD 52 // (char* buf, size_t size) -> KSTATUS
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Thread join
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
#define SYS_WAIT_THREAD 53 // (uint32_t tid) -> KSTATUS
|
||||||
|
// Blocks until the thread with that TID exits.
|
||||||
|
// Returns STATUS_NOT_FOUND if TID unknown.
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Process PID query (needed by posix_spawn -> waitpid)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
#define SYS_GET_PROCESS_PID 54 // (uint32_t procHandle, uint32_t* pidOut) -> KSTATUS
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Sentinel
|
// Sentinel
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#define SYS_MAX 53
|
#define SYS_MAX 55
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
// posix.h - POSIX type and constant definitions for posix.dll
|
||||||
|
// Covers just what open/read/write/close/lseek/spawn/pthread/chdir need.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// errno
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
#ifdef errno
|
||||||
|
#undef errno
|
||||||
|
#endif
|
||||||
|
#ifdef EAGAIN
|
||||||
|
#undef EAGAIN
|
||||||
|
#endif
|
||||||
|
#ifdef ENOSYS
|
||||||
|
#undef ENOSYS
|
||||||
|
#endif
|
||||||
|
extern int errno; // defined in posix_fd.c; single global in v1
|
||||||
|
|
||||||
|
#define EPERM 1
|
||||||
|
#define ENOENT 2
|
||||||
|
#define ESRCH 3
|
||||||
|
#define EINTR 4
|
||||||
|
#define EIO 5
|
||||||
|
#define EBADF 9
|
||||||
|
#define ENOMEM 12
|
||||||
|
#define EACCES 13
|
||||||
|
#define EEXIST 17
|
||||||
|
#define ENODEV 19
|
||||||
|
#define ENOTDIR 20
|
||||||
|
#define EINVAL 22
|
||||||
|
#define EMFILE 24
|
||||||
|
#define ENOSPC 28
|
||||||
|
#define ERANGE 34
|
||||||
|
#define EAGAIN 35
|
||||||
|
#define ENOSYS 38
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// open() flags
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
#define O_RDONLY 0x0000
|
||||||
|
#define O_WRONLY 0x0001
|
||||||
|
#define O_RDWR 0x0002
|
||||||
|
#define O_CREAT 0x0040
|
||||||
|
#define O_TRUNC 0x0200
|
||||||
|
#define O_APPEND 0x0400
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// lseek() whence
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
#define SEEK_SET 0
|
||||||
|
#define SEEK_CUR 1
|
||||||
|
#define SEEK_END 2
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Scalar types
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
typedef int32_t pid_t;
|
||||||
|
typedef int32_t off_t;
|
||||||
|
typedef uint32_t mode_t;
|
||||||
|
typedef int64_t ssize_t;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// pthread types
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
typedef struct { uint32_t Tid; } pthread_t;
|
||||||
|
typedef struct { uint32_t Handle; } pthread_mutex_t;
|
||||||
|
typedef void* pthread_attr_t; // unused in v1
|
||||||
|
typedef void* pthread_mutexattr_t;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// posix_spawn types (file_actions and attrp both ignored in v1)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
typedef void* posix_spawn_file_actions_t;
|
||||||
|
typedef void* posix_spawnattr_t;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// dirent (used by posix_dir.c)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
#define NAME_MAX 259
|
||||||
|
|
||||||
|
typedef struct dirent {
|
||||||
|
char d_name[NAME_MAX + 1];
|
||||||
|
unsigned char d_type; // DT_REG or DT_DIR
|
||||||
|
} dirent;
|
||||||
|
|
||||||
|
#define DT_UNKNOWN 0
|
||||||
|
#define DT_REG 8
|
||||||
|
#define DT_DIR 4
|
||||||
|
|
||||||
|
typedef struct DIR {
|
||||||
|
uint32_t KHandle; // kernel DIR_OBJECT handle
|
||||||
|
dirent Current;
|
||||||
|
bool Valid;
|
||||||
|
} DIR;
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
// posix_dir.c - chdir, getcwd, opendir, readdir, closedir
|
||||||
|
#include <string.h>
|
||||||
|
#include "posix.h"
|
||||||
|
#include "posix_path.h"
|
||||||
|
|
||||||
|
extern uint32_t SysGetCurrentPid(void);
|
||||||
|
extern uint32_t SysSetCwd(const char* path);
|
||||||
|
extern uint32_t SysGetCwd(char* buf, size_t size);
|
||||||
|
extern uint32_t SysOpenDir(const char* path, uint32_t* handleOut);
|
||||||
|
extern uint32_t SysReadDir(uint32_t handle, void* entryOut);
|
||||||
|
extern void SysCloseHandle(uint32_t handle);
|
||||||
|
extern void* SysAlloc(size_t);
|
||||||
|
extern void SysFree(void*, size_t);
|
||||||
|
|
||||||
|
// KDIR_ENTRY layout mirrors io_file.h - duplicated here to avoid
|
||||||
|
// pulling kernel headers into posix.dll
|
||||||
|
#define KDIR_NAME_MAX 260
|
||||||
|
typedef struct {
|
||||||
|
char Name[KDIR_NAME_MAX];
|
||||||
|
uint8_t Attr;
|
||||||
|
uint32_t FileSize;
|
||||||
|
} KDIR_ENTRY;
|
||||||
|
|
||||||
|
#define KATTR_DIR 0x10
|
||||||
|
|
||||||
|
int chdir(const char* path) {
|
||||||
|
char native[512];
|
||||||
|
PosixTranslatePath(path, native, sizeof(native));
|
||||||
|
uint32_t s = SysSetCwd(native);
|
||||||
|
if (s != 0) { errno = ENOENT; return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* getcwd(char* buf, size_t size) {
|
||||||
|
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
|
||||||
|
char native[512];
|
||||||
|
uint32_t s = SysGetCwd(native, sizeof(native));
|
||||||
|
if (s != 0) { errno = ERANGE; return (char*)0; }
|
||||||
|
NativeToPosix(native, buf, size);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIR* opendir(const char* path) {
|
||||||
|
char native[512];
|
||||||
|
PosixTranslatePath(path, native, sizeof(native));
|
||||||
|
uint32_t kh;
|
||||||
|
uint32_t s = SysOpenDir(native, &kh);
|
||||||
|
if (s != 0) { errno = ENOENT; return (DIR*)0; }
|
||||||
|
|
||||||
|
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
|
||||||
|
DIR* d = (DIR*)SysAlloc(sizeof(DIR));
|
||||||
|
if (!d) { SysCloseHandle(kh); errno = ENOMEM; return (DIR*)0; }
|
||||||
|
d->KHandle = kh;
|
||||||
|
d->Valid = false;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent* readdir(DIR* d) {
|
||||||
|
if (!d) { errno = EBADF; return (struct dirent*)0; }
|
||||||
|
KDIR_ENTRY ke;
|
||||||
|
uint32_t s = SysReadDir(d->KHandle, &ke);
|
||||||
|
if (s != 0) return (struct dirent*)0; // end of directory or error
|
||||||
|
|
||||||
|
size_t len = strlen(ke.Name);
|
||||||
|
if (len > NAME_MAX) len = NAME_MAX;
|
||||||
|
memcpy(d->Current.d_name, ke.Name, len);
|
||||||
|
d->Current.d_name[len] = '\0';
|
||||||
|
d->Current.d_type = (ke.Attr & KATTR_DIR) ? DT_DIR : DT_REG;
|
||||||
|
d->Valid = true;
|
||||||
|
return &d->Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
int closedir(DIR* d) {
|
||||||
|
if (!d) { errno = EBADF; return -1; }
|
||||||
|
SysCloseHandle(d->KHandle);
|
||||||
|
SysFree(d, sizeof(DIR));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// posix_fd.c - file descriptor table
|
||||||
|
#include "posix.h"
|
||||||
|
#include "posix_fd.h"
|
||||||
|
|
||||||
|
// yeah
|
||||||
|
#ifdef errno
|
||||||
|
#undef errno
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int errno = 0;
|
||||||
|
|
||||||
|
static FD_ENTRY gFdTable[FD_TABLE_PIDS][FD_MAX];
|
||||||
|
|
||||||
|
// Syscall stub declarations (from syscall.dll)
|
||||||
|
extern uint32_t SysGetCurrentPid(void);
|
||||||
|
|
||||||
|
void PosixFdInit(void) {
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||||
|
|
||||||
|
// Clear this process's slice (handles inherited state from a previous
|
||||||
|
// process that happened to land on the same slot - unlikely but safe).
|
||||||
|
for (int i = 0; i < FD_MAX; i++)
|
||||||
|
gFdTable[slot][i] = (FD_ENTRY){ FD_FREE, 0, false };
|
||||||
|
|
||||||
|
gFdTable[slot][0] = (FD_ENTRY){ FD_CONSOLE_R, 0, true };
|
||||||
|
gFdTable[slot][1] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
|
||||||
|
gFdTable[slot][2] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
|
||||||
|
}
|
||||||
|
|
||||||
|
int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) {
|
||||||
|
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||||
|
for (int fd = 3; fd < FD_MAX; fd++) {
|
||||||
|
if (!gFdTable[slot][fd].Used) {
|
||||||
|
gFdTable[slot][fd] = (FD_ENTRY){ type, khandle, true };
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errno = EMFILE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_ENTRY* FdGet(uint32_t pid, int fd) {
|
||||||
|
if (fd < 0 || fd >= FD_MAX) { errno = EBADF; return (FD_ENTRY*)0; }
|
||||||
|
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||||
|
FD_ENTRY* e = &gFdTable[slot][fd];
|
||||||
|
if (!e->Used) { errno = EBADF; return (FD_ENTRY*)0; }
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FdFree(uint32_t pid, int fd) {
|
||||||
|
if (fd < 0 || fd >= FD_MAX) return;
|
||||||
|
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||||
|
gFdTable[slot][fd] = (FD_ENTRY){ FD_FREE, 0, false };
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
// posix_fd.h - file descriptor table (internal to posix.dll)
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FD_FREE,
|
||||||
|
FD_FILE,
|
||||||
|
FD_CONSOLE_R, // fd 0 - reads from keyboard via SysKbReadChar
|
||||||
|
FD_CONSOLE_W, // fd 1, 2 - writes via SysWriteConsole
|
||||||
|
FD_DEVNULL, // /dev/null
|
||||||
|
FD_DEVZERO, // /dev/zero
|
||||||
|
} FD_TYPE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FD_TYPE Type;
|
||||||
|
uint32_t KHandle; // kernel handle; 0 for console/devnull/devzero
|
||||||
|
bool Used;
|
||||||
|
} FD_ENTRY;
|
||||||
|
|
||||||
|
#define FD_MAX 64
|
||||||
|
#define FD_TABLE_PIDS 32 // circular, indexed by pid % FD_TABLE_PIDS
|
||||||
|
|
||||||
|
// Called from _start before main(); sets up fds 0/1/2 for this process.
|
||||||
|
void PosixFdInit(void);
|
||||||
|
|
||||||
|
// Allocate the lowest free fd >= 3. Returns -1 and sets errno=EMFILE on failure.
|
||||||
|
int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle);
|
||||||
|
|
||||||
|
// Look up an fd. Returns NULL and sets errno=EBADF if invalid.
|
||||||
|
FD_ENTRY* FdGet(uint32_t pid, int fd);
|
||||||
|
|
||||||
|
// Release an fd slot (does NOT close the kernel handle).
|
||||||
|
void FdFree(uint32_t pid, int fd);
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// posix_io.c - open, read, write, close, lseek
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "posix.h"
|
||||||
|
#include "posix_fd.h"
|
||||||
|
#include "posix_path.h"
|
||||||
|
|
||||||
|
extern uint32_t SysGetCurrentPid(void);
|
||||||
|
extern uint32_t SysOpenFile (const char* path, uint32_t* handleOut);
|
||||||
|
extern uint32_t SysCreateFile (const char* path, uint32_t* handleOut);
|
||||||
|
extern uint32_t SysTruncateFile(uint32_t handle, uint64_t newSize);
|
||||||
|
extern uint32_t SysReadFile (uint32_t handle, void* buf, size_t size, size_t* bytesRead);
|
||||||
|
extern uint32_t SysWriteFile (uint32_t handle, const void* buf, size_t size, size_t* written);
|
||||||
|
extern uint32_t SysSeekFile (uint32_t handle, uint64_t offset);
|
||||||
|
extern uint64_t SysGetFilePos (uint32_t handle);
|
||||||
|
extern uint64_t SysGetFileSize(uint32_t handle);
|
||||||
|
extern void SysCloseHandle(uint32_t handle);
|
||||||
|
extern void SysWriteConsole(const char* buf, size_t len);
|
||||||
|
extern char SysKbReadChar(void);
|
||||||
|
|
||||||
|
// KSTATUS -> errno
|
||||||
|
static int KsToErrno(uint32_t s) {
|
||||||
|
switch (s) {
|
||||||
|
case 0x0000: return 0; // STATUS_SUCCESS
|
||||||
|
case 0x0022: return ENOENT; // STATUS_NOT_FOUND
|
||||||
|
case 0x0021: return EACCES; // STATUS_ACCESS_DENIED
|
||||||
|
case 0x0020: return ENOMEM; // STATUS_NO_MEMORY
|
||||||
|
case 0x0023: return EEXIST; // STATUS_ALREADY_EXISTS
|
||||||
|
case 0x0010: return EINVAL; // STATUS_INVALID_PARAMETER
|
||||||
|
case 0x0012: return ERANGE; // STATUS_BUFFER_TOO_SMALL
|
||||||
|
case 0x0002: return ENOSYS; // STATUS_NOT_SUPPORTED
|
||||||
|
case 0x0031: return EAGAIN; // STATUS_QUEUE_FULL
|
||||||
|
default: return EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int open(const char* path, int flags, ...) {
|
||||||
|
char native[512];
|
||||||
|
const char* p = PosixTranslatePath(path, native, sizeof(native));
|
||||||
|
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
FD_TYPE type = FD_FILE;
|
||||||
|
|
||||||
|
// Synthetic devices
|
||||||
|
if (strcmp(p, SYNTH_DEVNULL) == 0) {
|
||||||
|
int fd = FdAlloc(pid, FD_DEVNULL, 0);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(p, SYNTH_DEVZERO) == 0) {
|
||||||
|
int fd = FdAlloc(pid, FD_DEVZERO, 0);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t kh = 0;
|
||||||
|
uint32_t s;
|
||||||
|
if (flags & O_WRONLY) {
|
||||||
|
s = SysCreateFile(p, &kh);
|
||||||
|
} else {
|
||||||
|
s = SysOpenFile(p, &kh);
|
||||||
|
}
|
||||||
|
if (s != 0) { errno = KsToErrno(s); return -1; }
|
||||||
|
|
||||||
|
if ((flags & O_TRUNC) && (flags & O_WRONLY))
|
||||||
|
SysTruncateFile(kh, 0);
|
||||||
|
|
||||||
|
int fd = FdAlloc(pid, type, kh);
|
||||||
|
if (fd < 0) {
|
||||||
|
SysCloseHandle(kh); // FdAlloc already set errno
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t read(int fd, void* buf, size_t count) {
|
||||||
|
if (count == 0) return 0;
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
FD_ENTRY* e = FdGet(pid, fd);
|
||||||
|
if (!e) return -1;
|
||||||
|
|
||||||
|
switch (e->Type) {
|
||||||
|
case FD_CONSOLE_R: {
|
||||||
|
// Read up to count chars from keyboard, blocking per char.
|
||||||
|
char* dst = (char*)buf;
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
dst[i] = SysKbReadChar();
|
||||||
|
if (dst[i] == '\n' || dst[i] == '\r') return (ssize_t)(i + 1);
|
||||||
|
}
|
||||||
|
return (ssize_t)count;
|
||||||
|
}
|
||||||
|
case FD_DEVNULL:
|
||||||
|
return 0;
|
||||||
|
case FD_DEVZERO:
|
||||||
|
memset(buf, 0, count);
|
||||||
|
return (ssize_t)count;
|
||||||
|
case FD_FILE: {
|
||||||
|
size_t got = 0;
|
||||||
|
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
|
||||||
|
if (s != 0 && got == 0) { errno = KsToErrno(s); return -1; }
|
||||||
|
return (ssize_t)got;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t write(int fd, const void* buf, size_t count) {
|
||||||
|
if (count == 0) return 0;
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
FD_ENTRY* e = FdGet(pid, fd);
|
||||||
|
if (!e) return -1;
|
||||||
|
|
||||||
|
switch (e->Type) {
|
||||||
|
case FD_CONSOLE_W:
|
||||||
|
SysWriteConsole((const char*)buf, count);
|
||||||
|
return (ssize_t)count;
|
||||||
|
case FD_DEVNULL:
|
||||||
|
case FD_DEVZERO:
|
||||||
|
return (ssize_t)count;
|
||||||
|
case FD_FILE: {
|
||||||
|
size_t written = 0;
|
||||||
|
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
|
||||||
|
if (s != 0 && written == 0) { errno = KsToErrno(s); return -1; }
|
||||||
|
return (ssize_t)written;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int close(int fd) {
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
FD_ENTRY* e = FdGet(pid, fd);
|
||||||
|
if (!e) return -1;
|
||||||
|
if (e->Type == FD_FILE)
|
||||||
|
SysCloseHandle(e->KHandle);
|
||||||
|
FdFree(pid, fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t lseek(int fd, off_t offset, int whence) {
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
FD_ENTRY* e = FdGet(pid, fd);
|
||||||
|
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
|
||||||
|
|
||||||
|
uint64_t target;
|
||||||
|
switch (whence) {
|
||||||
|
case SEEK_SET: target = (uint64_t)offset; break;
|
||||||
|
case SEEK_CUR: target = SysGetFilePos(e->KHandle) + (uint64_t)offset; break;
|
||||||
|
case SEEK_END: target = SysGetFileSize(e->KHandle) + (uint64_t)offset; break;
|
||||||
|
default: errno = EINVAL; return -1;
|
||||||
|
}
|
||||||
|
uint32_t s = SysSeekFile(e->KHandle, target);
|
||||||
|
if (s != 0) { errno = KsToErrno(s); return -1; }
|
||||||
|
return (off_t)target;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// posix_path.c - POSIX <-> native path translation
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "posix_path.h"
|
||||||
|
|
||||||
|
// The drive letter used as the POSIX filesystem root.
|
||||||
|
// A future SYS_GET_BOOT_DRIVE could populate this; hardcoded for v1.
|
||||||
|
static const char gRootDrive = 'C';
|
||||||
|
|
||||||
|
static void FlipSlashes(char* s) {
|
||||||
|
for (; *s; s++) if (*s == '/') *s = '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* PosixTranslatePath(const char* in, char* out, size_t outSize) {
|
||||||
|
if (!in || !out || outSize < 4) return out;
|
||||||
|
|
||||||
|
// Synthetic devices
|
||||||
|
if (strcmp(in, "/dev/null") == 0) return SYNTH_DEVNULL;
|
||||||
|
if (strcmp(in, "/dev/zero") == 0) return SYNTH_DEVZERO;
|
||||||
|
|
||||||
|
// /proc/self/* - read-only synthesized files; flag with a sentinel so
|
||||||
|
// posix_io.c can intercept them without opening a real file.
|
||||||
|
// (In v1 only /proc/self/pid is handled; others fall through to ENOENT.)
|
||||||
|
|
||||||
|
if (in[0] == '/') {
|
||||||
|
// Absolute POSIX path -> C:\...
|
||||||
|
// outSize must fit "C:\" + rest
|
||||||
|
out[0] = gRootDrive;
|
||||||
|
out[1] = ':';
|
||||||
|
out[2] = '\\';
|
||||||
|
size_t rest = strlen(in + 1);
|
||||||
|
if (rest + 4 > outSize) rest = outSize - 4;
|
||||||
|
memcpy(out + 3, in + 1, rest);
|
||||||
|
out[3 + rest] = '\0';
|
||||||
|
} else {
|
||||||
|
// Relative - pass through with slash flip only
|
||||||
|
size_t len = strlen(in);
|
||||||
|
if (len + 1 > outSize) len = outSize - 1;
|
||||||
|
memcpy(out, in, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
FlipSlashes(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeToPosix(const char* in, char* out, size_t outSize) {
|
||||||
|
if (!in || !out || outSize == 0) return;
|
||||||
|
|
||||||
|
// Strip "X:\" prefix if present
|
||||||
|
const char* src = in;
|
||||||
|
if (src[0] != '\0' && src[1] == ':' && (src[2] == '\\' || src[2] == '/'))
|
||||||
|
src += 2; // skip "C:", leave the leading backslash
|
||||||
|
|
||||||
|
size_t len = strlen(src);
|
||||||
|
if (len + 1 > outSize) len = outSize - 1;
|
||||||
|
memcpy(out, src, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
|
||||||
|
// Backslash -> forward slash
|
||||||
|
for (char* p = out; *p; p++) if (*p == '\\') *p = '/';
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
// posix_path.h - POSIX <-> native path translation
|
||||||
|
|
||||||
|
// SYNTHETIC sentinel returned from PosixTranslatePath for /dev/null and
|
||||||
|
// /dev/zero; the caller must handle these before passing to any syscall.
|
||||||
|
#define SYNTH_DEVNULL "\x01<devnull>"
|
||||||
|
#define SYNTH_DEVZERO "\x01<devzero>"
|
||||||
|
|
||||||
|
// Translate a POSIX path to a native kernel path.
|
||||||
|
// Returns a pointer to 'out' on success, or SYNTH_DEVNULL / SYNTH_DEVZERO
|
||||||
|
// for those synthetic devices. 'out' must be at least 512 bytes.
|
||||||
|
const char* PosixTranslatePath(const char* in, char* out, size_t outSize);
|
||||||
|
|
||||||
|
// Translate a native kernel path back to a POSIX-style path.
|
||||||
|
// Strips the drive letter prefix and flips backslashes.
|
||||||
|
void NativeToPosix(const char* in, char* out, size_t outSize);
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// posix_proc.c - posix_spawn, waitpid, getpid
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "posix.h"
|
||||||
|
#include "posix_path.h"
|
||||||
|
|
||||||
|
extern uint32_t SysGetCurrentPid(void);
|
||||||
|
extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut,
|
||||||
|
const char* cmdline);
|
||||||
|
extern uint32_t SysWaitForProcess(uint32_t handle);
|
||||||
|
extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
|
||||||
|
extern uint32_t SysGetProcessPid(uint32_t handle, uint32_t* pidOut);
|
||||||
|
extern void SysCloseHandle(uint32_t handle);
|
||||||
|
|
||||||
|
// pid -> proc handle table (user-mode side, for waitpid)
|
||||||
|
#define PROC_TABLE_SIZE 32
|
||||||
|
typedef struct {
|
||||||
|
pid_t pid;
|
||||||
|
uint32_t handle;
|
||||||
|
bool used;
|
||||||
|
} ProcEntry;
|
||||||
|
|
||||||
|
static ProcEntry gProcTable[PROC_TABLE_SIZE];
|
||||||
|
|
||||||
|
static void ProcTableInsert(pid_t pid, uint32_t handle) {
|
||||||
|
for (int i = 0; i < PROC_TABLE_SIZE; i++) {
|
||||||
|
if (!gProcTable[i].used) {
|
||||||
|
gProcTable[i] = (ProcEntry){ pid, handle, true };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SysCloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t ProcTableFind(pid_t pid) {
|
||||||
|
for (int i = 0; i < PROC_TABLE_SIZE; i++)
|
||||||
|
if (gProcTable[i].used && gProcTable[i].pid == pid)
|
||||||
|
return gProcTable[i].handle;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ProcTableRemove(pid_t pid) {
|
||||||
|
for (int i = 0; i < PROC_TABLE_SIZE; i++)
|
||||||
|
if (gProcTable[i].used && gProcTable[i].pid == pid)
|
||||||
|
gProcTable[i].used = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a flat Windows-style cmdline from a NULL-terminated argv array.
|
||||||
|
// Each token that contains a space is wrapped in double-quotes.
|
||||||
|
// argv[0] is included (it becomes the executable token visible to _start).
|
||||||
|
static void BuildCmdline(char* const argv[], char* out, size_t outSize) {
|
||||||
|
size_t pos = 0;
|
||||||
|
for (int i = 0; argv && argv[i]; i++) {
|
||||||
|
if (i > 0 && pos < outSize - 1) out[pos++] = ' ';
|
||||||
|
const char* tok = argv[i];
|
||||||
|
bool needQuote = false;
|
||||||
|
for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; }
|
||||||
|
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||||
|
for (; *tok && pos < outSize - 2; tok++) {
|
||||||
|
if (*tok == '"') { out[pos++] = '\\'; }
|
||||||
|
out[pos++] = *tok;
|
||||||
|
}
|
||||||
|
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||||
|
}
|
||||||
|
if (pos < outSize) out[pos] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// POSIX API
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
pid_t getpid(void) {
|
||||||
|
return (pid_t)SysGetCurrentPid();
|
||||||
|
}
|
||||||
|
|
||||||
|
int posix_spawn(pid_t* pidOut,
|
||||||
|
const char* path,
|
||||||
|
const posix_spawn_file_actions_t* file_actions, // ignored v1
|
||||||
|
const posix_spawnattr_t* attrp, // ignored v1
|
||||||
|
char* const argv[],
|
||||||
|
char* const envp[]) // ignored v1
|
||||||
|
{
|
||||||
|
(void)file_actions; (void)attrp; (void)envp;
|
||||||
|
|
||||||
|
char native[512];
|
||||||
|
PosixTranslatePath(path, native, sizeof(native));
|
||||||
|
|
||||||
|
char cmdline[512];
|
||||||
|
BuildCmdline(argv, cmdline, sizeof(cmdline));
|
||||||
|
// If argv was NULL or empty, fall back to just the path so _start still
|
||||||
|
// gets a meaningful argv[0].
|
||||||
|
if (cmdline[0] == '\0') {
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1;
|
||||||
|
memcpy(cmdline, path, len);
|
||||||
|
cmdline[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t procHandle;
|
||||||
|
uint32_t s = SysCreateProcess(native, &procHandle, cmdline);
|
||||||
|
if (s != 0) { errno = ENOENT; return -1; }
|
||||||
|
|
||||||
|
uint32_t kpid = 0;
|
||||||
|
SysGetProcessPid(procHandle, &kpid);
|
||||||
|
ProcTableInsert((pid_t)kpid, procHandle);
|
||||||
|
if (pidOut) *pidOut = (pid_t)kpid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// options: WNOHANG not supported in v1 (always blocks).
|
||||||
|
pid_t waitpid(pid_t pid, int* status, int options) {
|
||||||
|
(void)options;
|
||||||
|
|
||||||
|
uint32_t handle = ProcTableFind(pid);
|
||||||
|
if (!handle) { errno = ESRCH; return -1; }
|
||||||
|
|
||||||
|
SysWaitForProcess(handle);
|
||||||
|
|
||||||
|
int exitCode = 0;
|
||||||
|
SysGetProcessExitCode(handle, &exitCode);
|
||||||
|
if (status) *status = exitCode << 8; // WEXITSTATUS convention
|
||||||
|
|
||||||
|
SysCloseHandle(handle);
|
||||||
|
ProcTableRemove(pid);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_*
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "posix.h"
|
||||||
|
|
||||||
|
extern uint32_t SysGetCurrentPid(void);
|
||||||
|
extern uint32_t SysGetCurrentTid(void);
|
||||||
|
extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg,
|
||||||
|
uint32_t priority, uint32_t* tidOut);
|
||||||
|
extern uint32_t SysWaitThread(uint32_t tid);
|
||||||
|
extern void SysExitThread(int code);
|
||||||
|
extern void* SysAlloc(size_t size);
|
||||||
|
extern void SysFree(void* ptr, size_t size);
|
||||||
|
extern uint32_t SysMutexCreate(uint32_t* handleOut);
|
||||||
|
extern uint32_t SysMutexAcquire(uint32_t handle);
|
||||||
|
extern uint32_t SysMutexTryAcquire(uint32_t handle);
|
||||||
|
extern uint32_t SysMutexRelease(uint32_t handle);
|
||||||
|
extern void SysCloseHandle(uint32_t handle);
|
||||||
|
|
||||||
|
// Thread return-value table (PID-keyed like the fd table)
|
||||||
|
#define PTHREAD_TABLE_PIDS 32
|
||||||
|
#define PTHREAD_TABLE_SIZE 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t Tid;
|
||||||
|
void* RetVal;
|
||||||
|
bool Used;
|
||||||
|
} PTHREAD_SLOT;
|
||||||
|
|
||||||
|
static PTHREAD_SLOT gPthreadTable[PTHREAD_TABLE_PIDS][PTHREAD_TABLE_SIZE];
|
||||||
|
|
||||||
|
static PTHREAD_SLOT* PthreadSlotAlloc(uint32_t pid, uint32_t tid) {
|
||||||
|
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
||||||
|
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++) {
|
||||||
|
if (!table[i].Used) {
|
||||||
|
table[i] = (PTHREAD_SLOT){ tid, (void*)0, true };
|
||||||
|
return &table[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (PTHREAD_SLOT*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PTHREAD_SLOT* PthreadSlotFind(uint32_t pid, uint32_t tid) {
|
||||||
|
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
||||||
|
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++)
|
||||||
|
if (table[i].Used && table[i].Tid == tid)
|
||||||
|
return &table[i];
|
||||||
|
return (PTHREAD_SLOT*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Trampoline - this is what actually runs as the new thread
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
typedef struct {
|
||||||
|
void* (*Fn)(void*);
|
||||||
|
void* Arg;
|
||||||
|
uint32_t Pid; // owner pid, for the retval table
|
||||||
|
uint32_t Tid; // filled in by pthread_create after KeCreateThread returns
|
||||||
|
} PTHREAD_TRAMP;
|
||||||
|
|
||||||
|
static void PthreadTrampoline(uintptr_t raw) {
|
||||||
|
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)raw;
|
||||||
|
void* (*fn)(void*) = tr->Fn;
|
||||||
|
void* arg = tr->Arg;
|
||||||
|
uint32_t pid = tr->Pid;
|
||||||
|
uint32_t tid = tr->Tid;
|
||||||
|
SysFree(tr, sizeof(PTHREAD_TRAMP));
|
||||||
|
|
||||||
|
void* retval = fn(arg);
|
||||||
|
|
||||||
|
// Store retval for pthread_join. If the slot isn't there (join was called
|
||||||
|
// before the thread was even created, which shouldn't happen) just drop it.
|
||||||
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||||
|
if (slot) slot->RetVal = retval;
|
||||||
|
|
||||||
|
SysExitThread(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// pthread API
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int pthread_create(pthread_t* t, const pthread_attr_t* attr,
|
||||||
|
void* (*fn)(void*), void* arg) {
|
||||||
|
(void)attr;
|
||||||
|
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
|
||||||
|
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)SysAlloc(sizeof(PTHREAD_TRAMP));
|
||||||
|
if (!tr) return ENOMEM;
|
||||||
|
tr->Fn = fn;
|
||||||
|
tr->Arg = arg;
|
||||||
|
tr->Pid = pid;
|
||||||
|
tr->Tid = 0; // filled below
|
||||||
|
|
||||||
|
uint32_t tid = 0;
|
||||||
|
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
|
||||||
|
(uint64_t)tr, 1 /*PriorityNormal*/, &tid);
|
||||||
|
if (s != 0) {
|
||||||
|
SysFree(tr, sizeof(PTHREAD_TRAMP));
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
tr->Tid = tid; // safe: trampoline reads Tid after SysCreateThread returns
|
||||||
|
|
||||||
|
// Reserve a retval slot before the trampoline can exit
|
||||||
|
PthreadSlotAlloc(pid, tid);
|
||||||
|
|
||||||
|
t->Tid = tid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_join(pthread_t t, void** retval) {
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
uint32_t s = SysWaitThread(t.Tid);
|
||||||
|
// STATUS_NOT_FOUND (0x22) means already dead - treat as success
|
||||||
|
if (s != 0 && s != 0x0022) return ESRCH;
|
||||||
|
|
||||||
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
|
||||||
|
if (retval) *retval = slot ? slot->RetVal : (void*)0;
|
||||||
|
if (slot) slot->Used = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pthread_exit(void* retval) {
|
||||||
|
uint32_t pid = SysGetCurrentPid();
|
||||||
|
uint32_t tid = SysGetCurrentTid();
|
||||||
|
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||||
|
if (slot) slot->RetVal = retval;
|
||||||
|
SysExitThread(0);
|
||||||
|
while (1); // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) {
|
||||||
|
(void)attr;
|
||||||
|
uint32_t h;
|
||||||
|
uint32_t s = SysMutexCreate(&h);
|
||||||
|
if (s != 0) return ENOMEM;
|
||||||
|
m->Handle = h;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_mutex_destroy(pthread_mutex_t* m) {
|
||||||
|
SysCloseHandle(m->Handle);
|
||||||
|
m->Handle = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_mutex_lock(pthread_mutex_t* m) {
|
||||||
|
uint32_t s = SysMutexAcquire(m->Handle);
|
||||||
|
return s == 0 ? 0 : EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_mutex_trylock(pthread_mutex_t* m) {
|
||||||
|
uint32_t s = SysMutexTryAcquire(m->Handle);
|
||||||
|
return s == 0 ? 0 : EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_mutex_unlock(pthread_mutex_t* m) {
|
||||||
|
uint32_t s = SysMutexRelease(m->Handle);
|
||||||
|
return s == 0 ? 0 : EINVAL;
|
||||||
|
}
|
||||||
+3
-1
@@ -357,12 +357,14 @@ static int runCommand(const char* cmd) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Entry point
|
// Entry point
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
int main() {
|
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("> ");
|
||||||
|
|||||||
@@ -86,5 +86,11 @@ SYS_READ_DIR EQU 50
|
|||||||
SYS_SET_CWD EQU 51
|
SYS_SET_CWD EQU 51
|
||||||
SYS_GET_CWD EQU 52
|
SYS_GET_CWD EQU 52
|
||||||
|
|
||||||
|
; Thread join
|
||||||
|
SYS_WAIT_THREAD EQU 53 ; a0=tid -> KSTATUS
|
||||||
|
|
||||||
|
; Process PID query
|
||||||
|
SYS_GET_PROCESS_PID EQU 54 ; a0=procHandle a1=*pidOut -> KSTATUS
|
||||||
|
|
||||||
; Sentinel
|
; Sentinel
|
||||||
SYS_MAX EQU 53
|
SYS_MAX EQU 55
|
||||||
@@ -166,4 +166,14 @@ SYSCALL_STUB2 ReadDir, SYS_READ_DIR
|
|||||||
SYSCALL_STUB2 SetCwd, SYS_SET_CWD
|
SYSCALL_STUB2 SetCwd, SYS_SET_CWD
|
||||||
SYSCALL_STUB2 GetCwd, SYS_GET_CWD
|
SYSCALL_STUB2 GetCwd, SYS_GET_CWD
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; Thread join (used by pthread_join in posix.dll)
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
SYSCALL_STUB2 WaitThread, SYS_WAIT_THREAD ; a0=tid -> KSTATUS
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
; Process PID query (used by posix_spawn -> waitpid in posix.dll)
|
||||||
|
; ---------------------------------------------------------------------------
|
||||||
|
SYSCALL_STUB2 GetProcessPid, SYS_GET_PROCESS_PID ; a0=procHandle a1=*pidOut -> KSTATUS
|
||||||
|
|
||||||
END
|
END
|
||||||
+4
-1
@@ -76,10 +76,13 @@ int StretchDIBits(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight,
|
|||||||
UINT iUsage, DWORD rop);
|
UINT iUsage, DWORD rop);
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main(const char* cmdline) {
|
||||||
void* h = GetStdHandle(STD_OUTPUT_HANDLE);
|
void* h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
|
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
|
||||||
|
|
||||||
|
WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 0, 0);
|
||||||
|
WriteConsoleA(h, "\n", 1, 0, 0);
|
||||||
|
|
||||||
void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
|
void* f = CreateFileA("video.bk2", 0, 0, 0, 3, 0, 0);
|
||||||
if (!f) {
|
if (!f) {
|
||||||
WriteConsoleA(h, "video: failed to open video.bk2\n", 32, 0, 0);
|
WriteConsoleA(h, "video: failed to open video.bk2\n", 32, 0, 0);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ extern uint32_t SysCreateThread(uint64_t entry, uint64_t arg,
|
|||||||
uint32_t priority, uint32_t* tidOut);
|
uint32_t priority, uint32_t* tidOut);
|
||||||
extern uint32_t SysGetCurrentTid(void);
|
extern uint32_t SysGetCurrentTid(void);
|
||||||
extern uint32_t SysGetCurrentPid(void);
|
extern uint32_t SysGetCurrentPid(void);
|
||||||
extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut);
|
extern uint32_t SysCreateProcess(const char* path, uint32_t* handleOut, const char* cmdline);
|
||||||
extern uint32_t SysWaitForProcess(uint32_t handle);
|
extern uint32_t SysWaitForProcess(uint32_t handle);
|
||||||
extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
|
extern uint32_t SysGetProcessExitCode(uint32_t handle, int* exitCodeOut);
|
||||||
extern char SysKbReadChar(void);
|
extern char SysKbReadChar(void);
|
||||||
@@ -608,9 +608,12 @@ __declspec(dllexport) BOOL CreateProcessA(
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the cmdline: prefer lpCommandLine if given, otherwise fall back to path
|
||||||
|
const char* cmdline = lpCommandLine ? lpCommandLine : targetPath;
|
||||||
|
|
||||||
uint32_t kh = 0;
|
uint32_t kh = 0;
|
||||||
// Call the syscall.dll stub
|
// Call the syscall.dll stub
|
||||||
uint32_t s = SysCreateProcess(targetPath, &kh);
|
uint32_t s = SysCreateProcess(targetPath, &kh, cmdline);
|
||||||
if (s != 0 || kh == 0) {
|
if (s != 0 || kh == 0) {
|
||||||
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
gLastError = 2; // ERROR_FILE_NOT_FOUND
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ It boots into a shell.
|
|||||||
- VFS with IRPs
|
- VFS with IRPs
|
||||||
- drive-letter paths (`0:\apps\shell.exe`)
|
- drive-letter paths (`0:\apps\shell.exe`)
|
||||||
- INI-based boot and driver configuration
|
- INI-based boot and driver configuration
|
||||||
- partial Win32 layer (kernel32 and user32, capable of running simple windows apps)
|
|
||||||
- PE import resolution and dynamic driver loading
|
- PE import resolution and dynamic driver loading
|
||||||
|
- Win32 personality (partial)
|
||||||
|
- POSIX personality (partial)
|
||||||
|
|
||||||
## how 2 build
|
## how 2 build
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ currently shipping: AHCI (interrupts), IDE (PIO/DMA) and FAT32.
|
|||||||
"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. A drive only works if a filesystem registered on it.
|
||||||
|
|
||||||
## screenshots
|
## screenshots
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user