From 7eb6db3530ddbc4443e92ffc0e1e9d5a50acee47 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Mon, 30 Jan 2012 13:32:56 +0800 Subject: [PATCH] Kernel - Implementing SysSpawn - Working on proper VFS node reference counting - Cleanups in binary.c related to changes --- Kernel/arch/armv7/proc.c | 4 +- Kernel/arch/x86/include/mm_virt.h | 2 +- Kernel/arch/x86/mm_virt.c | 4 +- Kernel/arch/x86/proc.asm | 2 + Kernel/arch/x86/proc.c | 12 +- Kernel/arch/x86_64/mm_virt.c | 7 ++ Kernel/arch/x86_64/proc.c | 8 +- Kernel/binary.c | 186 ++++++++++++++++++++---------- Kernel/include/acess.h | 8 ++ Kernel/include/hal_proc.h | 2 +- Kernel/include/vfs_threads.h | 19 +++ Kernel/syscalls.c | 5 +- Kernel/vfs/handle.c | 125 ++++++++++++++++++++ 13 files changed, 308 insertions(+), 76 deletions(-) create mode 100644 Kernel/include/vfs_threads.h diff --git a/Kernel/arch/armv7/proc.c b/Kernel/arch/armv7/proc.c index c61590e7..cd998f2b 100644 --- a/Kernel/arch/armv7/proc.c +++ b/Kernel/arch/armv7/proc.c @@ -62,11 +62,11 @@ tThread *Proc_GetCurThread(void) return gpCurrentThread; } -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize) +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) { Uint32 *usr_sp; int i; - char **envp; + const char **envp; tVAddr delta; // Log_Debug("Proc", "Proc_StartUser: (Entrypoint=%p, Base=%p, ArgC=%i, ArgV=%p, DataSize=0x%x)", diff --git a/Kernel/arch/x86/include/mm_virt.h b/Kernel/arch/x86/include/mm_virt.h index 31642c64..d84c9650 100644 --- a/Kernel/arch/x86/include/mm_virt.h +++ b/Kernel/arch/x86/include/mm_virt.h @@ -46,7 +46,7 @@ // === FUNCTIONS === extern void MM_FinishVirtualInit(void); extern void MM_SetCR3(Uint CR3); -extern tPAddr MM_Clone(void); +extern tPAddr MM_Clone(int bCloneUser); extern tVAddr MM_NewKStack(void); extern tVAddr MM_NewWorkerStack(Uint *InitialStack, size_t StackSize); diff --git a/Kernel/arch/x86/mm_virt.c b/Kernel/arch/x86/mm_virt.c index d384142b..bd7b1dff 100644 --- a/Kernel/arch/x86/mm_virt.c +++ b/Kernel/arch/x86/mm_virt.c @@ -574,7 +574,7 @@ void MM_ClearSpace(Uint32 CR3) * \fn tPAddr MM_Clone(void) * \brief Clone the current address space */ -tPAddr MM_Clone(void) +tPAddr MM_Clone(int bNoUserCopy) { Uint i, j; tPAddr ret; @@ -593,7 +593,7 @@ tPAddr MM_Clone(void) INVLPG( gaTmpDir ); memsetd( gaTmpDir, 0, 1024 ); - if( Threads_GetPID() != 0 ) + if( Threads_GetPID() != 0 && !bNoUserCopy ) { // Copy Tables for( i = 0; i < 768; i ++) diff --git a/Kernel/arch/x86/proc.asm b/Kernel/arch/x86/proc.asm index 3de4e0a3..8da79711 100644 --- a/Kernel/arch/x86/proc.asm +++ b/Kernel/arch/x86/proc.asm @@ -31,7 +31,9 @@ Proc_CloneInt: ; Save RSP mov eax, [esp+0x20+4] mov [eax], esp + push DWORD [esp+0x20+12] call MM_Clone + add esp, 4 ; Save CR3 mov esi, [esp+0x20+8] mov [esi], eax diff --git a/Kernel/arch/x86/proc.c b/Kernel/arch/x86/proc.c index dd7027ea..82a3f409 100644 --- a/Kernel/arch/x86/proc.c +++ b/Kernel/arch/x86/proc.c @@ -45,7 +45,7 @@ extern void APStartup(void); // 16-bit AP startup code extern Uint GetEIP(void); // start.asm extern Uint GetEIP_Sched(void); // proc.asm extern void NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...); // Actually takes cdecl args -extern Uint Proc_CloneInt(Uint *ESP, Uint32 *CR3); +extern Uint Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone); extern Uint32 gaInitPageDir[1024]; // start.asm extern char Kernel_Stack_Top[]; extern int giNumCPUs; @@ -640,7 +640,7 @@ tPID Proc_Clone(Uint Flags) newThread->KernelStack = cur->KernelStack; // Clone state - eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3); + eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER); if( eip == 0 ) { return 0; } @@ -727,16 +727,16 @@ Uint Proc_MakeUserStack(void) return base + USER_STACK_SZ; } -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize) +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) { Uint *stack; int i; - char **envp = NULL; + const char **envp = NULL; Uint16 ss, cs; // Copy data to the user stack and free original buffer stack = (void*)Proc_MakeUserStack(); - stack -= DataSize/sizeof(*stack); + stack -= (DataSize+sizeof(*stack)-1)/sizeof(*stack); memcpy( stack, ArgV, DataSize ); free(ArgV); @@ -744,7 +744,7 @@ void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataS if( DataSize ) { Uint delta = (Uint)stack - (Uint)ArgV; - ArgV = (char**)stack; + ArgV = (const char**)stack; for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; envp = &ArgV[i+1]; for( i = 0; envp[i]; i++ ) envp[i] += delta; diff --git a/Kernel/arch/x86_64/mm_virt.c b/Kernel/arch/x86_64/mm_virt.c index 79513b94..89aeaa15 100644 --- a/Kernel/arch/x86_64/mm_virt.c +++ b/Kernel/arch/x86_64/mm_virt.c @@ -924,6 +924,13 @@ tPAddr MM_Clone(void) MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK ); } } + else + { + for( i = 0; i < 256; i ++ ) + { + TMPMAPLVL4(i) = 0; + } + } // #4 Map in kernel pages for( i = 256; i < 512; i ++ ) diff --git a/Kernel/arch/x86_64/proc.c b/Kernel/arch/x86_64/proc.c index a8928cac..5f71a45f 100644 --- a/Kernel/arch/x86_64/proc.c +++ b/Kernel/arch/x86_64/proc.c @@ -586,10 +586,12 @@ Uint Proc_MakeUserStack(void) if(i != -1) return 0; // Allocate Stack - Allocate incrementally to clean up MM_Dump output + // - Most of the user stack is the zero page for( i = 0; i < (USER_STACK_SZ-USER_STACK_PREALLOC)/0x1000; i++ ) { MM_AllocateZero( base + (i<<12) ); } + // - but the top USER_STACK_PREALLOC pages are actually allocated for( ; i < USER_STACK_SZ/0x1000; i++ ) { tPAddr alloc = MM_Allocate( base + (i<<12) ); @@ -606,11 +608,11 @@ Uint Proc_MakeUserStack(void) return base + USER_STACK_SZ; } -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize) +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) { Uint *stack; int i; - char **envp = NULL; + const char **envp = NULL; Uint16 ss, cs; @@ -628,7 +630,7 @@ void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataS if(DataSize) { Uint delta = (Uint)stack - (Uint)ArgV; - ArgV = (char**)stack; + ArgV = (const char**)stack; for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; envp = &ArgV[i+1]; for( i = 0; envp[i]; i++ ) envp[i] += delta; diff --git a/Kernel/binary.c b/Kernel/binary.c index 20da5c4e..a14bd4a9 100644 --- a/Kernel/binary.c +++ b/Kernel/binary.c @@ -7,6 +7,7 @@ #include #include #include +#include // === CONSTANTS === #define BIN_LOWEST MM_USER_MIN // 1MiB @@ -30,7 +31,7 @@ extern tKernelSymbol gKernelSymbolsEnd[]; extern tBinaryType gELF_Info; // === PROTOTYPES === - int Proc_Execve(const char *File, const char **ArgV, const char **EnvP); + int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer); tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint); tBinary *Binary_GetInfo(tMount MountID, tInode InodeID); tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax); @@ -78,18 +79,128 @@ int Proc_Spawn(const char *Path) LOG("stackPath = '%s'", stackPath); - if(Proc_Clone(CLONE_VM) == 0) + if(Proc_Clone(CLONE_VM|CLONE_NOUSER) == 0) { // CHILD const char *args[2] = {stackPath, NULL}; LOG("stackPath = '%s'", stackPath); - Proc_Execve(stackPath, args, &args[1]); + Proc_Execve(stackPath, args, &args[1], 0); for(;;); } LEAVE('i', 0); return 0; } +/** + * \todo Document + */ +int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer) +{ + int size, argc=0, envc=0; + int i; + char *strbuf; + const char **arrays; + + // Calculate size + size = 0; + if( ArgV && *ArgV ) + { + const char **argv = *ArgV; + for( argc = 0; argv[argc]; argc ++ ) + size += strlen( argv[argc] ) + 1; + } + if( EnvP && *EnvP ) + { + const char **envp = *EnvP; + for( envc = 0; envp[envc]; envc ++ ) + size += strlen( envp[envc] ) + 1; + } + size = (size + sizeof(void*)-1) & ~(sizeof(void*)-1); // Word align + size += (argc+1+envc+1)*sizeof(void*); // Arrays + if( Path ) + { + size += strlen( *Path ) + 1; + } + + if( DestBuffer ) + { + arrays = DestBuffer; + strbuf = (void*)&arrays[argc+1+envc+1]; + + // Fill ArgV + if( ArgV && *ArgV ) + { + const char **argv = *ArgV; + for( i = 0; argv[i]; i ++ ) + { + arrays[i] = strbuf; + strcpy(strbuf, argv[i]); + strbuf += strlen( argv[i] ) + 1; + } + *ArgV = arrays; + arrays += i; + } + *arrays++ = NULL; + // Fill EnvP + if( EnvP && *EnvP ) + { + const char **envp = *EnvP; + for( i = 0; envp[i]; i ++ ) + { + arrays[i] = strbuf; + strcpy(strbuf, envp[i]); + strbuf += strlen( envp[i] ) + 1; + } + *EnvP = arrays; + arrays += i; + } + *arrays++ = NULL; + // Fill path + if( Path ) + { + strcpy(strbuf, *Path); + *Path = strbuf; + } + } + + return size; +} + +/** + * \brief Create a new process with the specified set of file descriptors + */ +int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs) +{ + void *handles; + void *cachebuf; + int size; + tPID ret; + + // --- Save File, ArgV and EnvP + size = Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, NULL ); + cachebuf = alloca( size ); + Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, cachebuf ); + + // Cache the VFS handles + handles = VFS_SaveHandles(nFD, FDs); + + // Create new process + ret = Proc_Clone(CLONE_VM|CLONE_NOUSER); + if( ret == 0 ) + { + VFS_RestoreHandles(nFD, handles); + VFS_FreeSavedHandles(nFD, handles); + Proc_Execve(Binary, ArgV, EnvP, size); + for(;;); + } + if( ret < 0 ) + { + VFS_FreeSavedHandles(nFD, handles); + } + + return ret; +} + /** * \brief Replace the current user image with another * \param File File to load as the next image @@ -97,61 +208,25 @@ int Proc_Spawn(const char *Path) * \param EnvP User's environment * \note Called Proc_ for historical reasons */ -int Proc_Execve(const char *File, const char **ArgV, const char **EnvP) +int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize) { - int argc, envc, i; - int argenvBytes; - char **argenvBuf, *strBuf; - char **argvSaved, **envpSaved; - char *savedFile; + void *cachebuf; tVAddr entry; Uint base; // Uint because Proc_StartUser wants it + int argc; ENTER("sFile pArgV pEnvP", File, ArgV, EnvP); - // --- Save File, ArgV and EnvP (also get argc) - - // Count Arguments, Environment Variables and total string sizes - argenvBytes = 0; - for( argc = 0; ArgV && ArgV[argc]; argc++ ) - argenvBytes += strlen(ArgV[argc])+1; - for( envc = 0; EnvP && EnvP[envc]; envc++ ) - argenvBytes += strlen(EnvP[envc])+1; - LOG("argc = %i, envc = %i", envc); - argenvBytes = (argenvBytes + sizeof(void*)-1) & ~(sizeof(void*)-1); - argenvBytes += (argc+1)*sizeof(void*) + (envc+1)*sizeof(void*); - - // Allocate - argenvBuf = malloc(argenvBytes); - if(argenvBuf == NULL) { - Log_Error("Binary", "Proc_Execve - What the hell? The kernel is out of heap space"); - LEAVE('i', 0); - return 0; - } - strBuf = (char*)argenvBuf + (argc+1)*sizeof(void*) + (envc+1)*sizeof(void*); - - // Populate - argvSaved = argenvBuf; - for( i = 0; i < argc; i++ ) - { - argvSaved[i] = strBuf; - strcpy(argvSaved[i], ArgV[i]); - LOG("argv[%i] = '%s'", i, strBuf); - strBuf += strlen(ArgV[i])+1; - } - argvSaved[i] = NULL; - envpSaved = &argvSaved[i+1]; - for( i = 0; i < envc; i++ ) + // --- Save File, ArgV and EnvP + if( DataSize == 0 ) { - envpSaved[i] = strBuf; - LOG("envp[%i] = '%s'", i, strBuf); - strcpy(envpSaved[i], EnvP[i]); - strBuf += strlen(EnvP[i])+1; + DataSize = Binary_int_CacheArgs( &File, &ArgV, &EnvP, NULL ); + cachebuf = malloc( DataSize ); + Binary_int_CacheArgs( &File, &ArgV, &EnvP, cachebuf ); } - envpSaved[i] = NULL; - - savedFile = malloc(strlen(File)+1); - strcpy(savedFile, File); + + // --- Get argc + for( argc = 0; ArgV && ArgV[argc]; argc ++ ); // --- Set Process Name Threads_SetName(File); @@ -160,24 +235,19 @@ int Proc_Execve(const char *File, const char **ArgV, const char **EnvP) MM_ClearUser(); // --- Load new binary - base = Binary_Load(savedFile, &entry); - free(savedFile); + base = Binary_Load(File, &entry); if(base == 0) { - free(argvSaved); - Log_Warning("Binary", "Proc_Execve - Unable to load '%s'", Threads_GetName(-1)); + Log_Warning("Binary", "Proc_Execve - Unable to load '%s'", File); LEAVE('-'); Threads_Exit(0, -10); for(;;); } LOG("entry = 0x%x, base = 0x%x", entry, base); - -// MM_DumpTables(0, KERNEL_BASE); - LEAVE('-'); // --- And... Jump to it - Proc_StartUser(entry, base, argc, argvSaved, argenvBytes); + Proc_StartUser(entry, base, argc, ArgV, DataSize); for(;;); // Tell GCC that we never return } diff --git a/Kernel/include/acess.h b/Kernel/include/acess.h index df360602..de758043 100644 --- a/Kernel/include/acess.h +++ b/Kernel/include/acess.h @@ -88,6 +88,8 @@ extern const char gsGitHash[]; */ //! Clone the entire process #define CLONE_VM 0x10 +//! Don't copy user pages +#define CLONE_NOUSER 0x20 /** * \} */ @@ -443,6 +445,10 @@ extern int CallWithArgArray(void *Function, int NArgs, Uint *Args); // --- Heap --- #include +/** + * \brief Magic heap allocation function + */ +extern void *alloca(size_t Size); // --- Modules --- /** @@ -498,6 +504,8 @@ extern void Time_Delay(int Delay); */ extern int Proc_SpawnWorker(void (*Fcn)(void*), void *Data); extern int Proc_Spawn(const char *Path); +extern int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs); +extern int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize); extern void Threads_Exit(int TID, int Status); extern void Threads_Yield(void); extern void Threads_Sleep(void); diff --git a/Kernel/include/hal_proc.h b/Kernel/include/hal_proc.h index 3a22e5ea..0f21a1c7 100644 --- a/Kernel/include/hal_proc.h +++ b/Kernel/include/hal_proc.h @@ -58,7 +58,7 @@ extern tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr ); * \param DataSize Size of the \a ArgV buffer in bytes * \note This function should free \a ArgV */ -extern void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize) NORETURN; +extern void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) NORETURN; /** * \brief Call the fault handler for a thread * \param Thread Thread that is at fault :) diff --git a/Kernel/include/vfs_threads.h b/Kernel/include/vfs_threads.h new file mode 100644 index 00000000..95ec2278 --- /dev/null +++ b/Kernel/include/vfs_threads.h @@ -0,0 +1,19 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * include/vfs_threads.h + * - Handle maintainance functions for the VFS used by threading code + */ +#ifndef _VFS_THREADS_H_ +#define _VFS_THREADS_H_ + +// === FUNCTIONS === +extern void VFS_ReferenceUserHandles(void); +extern void VFS_CloseAllUserHandles(void); + +extern void *VFS_SaveHandles(int NumFDs, int *FDs); +extern void VFS_RestoreHandles(int NumFDs, void *Handles); +extern void VFS_FreeSavedHandles(int NumFDs, void *Handles); + +#endif diff --git a/Kernel/syscalls.c b/Kernel/syscalls.c index 4e4527e5..e62ab067 100644 --- a/Kernel/syscalls.c +++ b/Kernel/syscalls.c @@ -22,7 +22,6 @@ if(!(v)||!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;} // === IMPORTS === -extern int Proc_Execve(char *File, char **ArgV, char **EnvP); extern Uint Binary_Load(const char *file, Uint *entryPoint); // === PROTOTYPES === @@ -178,8 +177,8 @@ void SyscallHandler(tSyscallRegs *Regs) } } LEAVE('s', "Assuming 0"); - // Path, **Argv, **Envp - ret = Proc_Execve( (char*)Regs->Arg1, (char**)Regs->Arg2, (char**)Regs->Arg3 ); + // Path, **Argv, **Envp, DataSize (=0 to tell it to create a copy) + ret = Proc_Execve( (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3, 0 ); break; // -- Load a binary into the current process case SYS_LOADBIN: diff --git a/Kernel/vfs/handle.c b/Kernel/vfs/handle.c index 42a8fc4b..ae8b058f 100644 --- a/Kernel/vfs/handle.c +++ b/Kernel/vfs/handle.c @@ -9,6 +9,7 @@ #include "vfs_int.h" #include "vfs_ext.h" #include // GetMaxFD +#include // Handle maintainance // === CONSTANTS === #define MAX_KERNEL_FILES 128 @@ -113,3 +114,127 @@ int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode) return -1; } + +void VFS_ReferenceUserHandles(void) +{ + int i; + int max_handles = *Threads_GetMaxFD(); + + for( i = 0; i < max_handles; i ++ ) + { + tVFS_Handle *h; + h = &gaUserHandles[i]; + if( h->Node->Type && h->Node->Type->Reference ) + h->Node->Type->Reference( h->Node ); + } +} + +void VFS_CloseAllUserHandles(void) +{ + int i; + int max_handles = *Threads_GetMaxFD(); + + for( i = 0; i < max_handles; i ++ ) + { + tVFS_Handle *h; + h = &gaUserHandles[i]; + if( h->Node->Type && h->Node->Type->Close ) + h->Node->Type->Close( h->Node ); + } +} + +/** + * \brief Take a backup of a set of file descriptors + */ +void *VFS_SaveHandles(int NumFDs, int *FDs) +{ + tVFS_Handle *ret; + int i; + + // Check if this process has any handles + if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) + return NULL; + + // Allocate + ret = malloc( NumFDs * sizeof(tVFS_Handle) ); + if( !ret ) + return NULL; + + // Take copies of the handles + for( i = 0; i < NumFDs; i ++ ) + { + tVFS_Handle *h; + h = VFS_GetHandle(FDs[i] & (VFS_KERNEL_FLAG - 1)); + if(!h) { + Log_Warning("VFS", "VFS_SaveHandles - Invalid FD %i", + FDs[i] & (VFS_KERNEL_FLAG - 1) ); + free(ret); + return NULL; + } + memcpy( &ret[i], h, sizeof(tVFS_Handle) ); + + // Reference node + if( h->Node->Type && h->Node->Type->Reference ) + h->Node->Type->Reference( h->Node ); + } + + return ret; +} + +void VFS_RestoreHandles(int NumFDs, void *Handles) +{ + tVFS_Handle *handles = Handles; + int i; + + // NULL = nothing to do + if( !Handles ) + return ; + + // Check if there is already a set of handles + if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) != 0 ) + return ; + + + // Allocate user handle area + { + Uint addr, size; + int max_handles = *Threads_GetMaxFD(); + size = max_handles * sizeof(tVFS_Handle); + for(addr = 0; addr < size; addr += 0x1000) + { + if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) ) + { + Warning("OOM - VFS_AllocHandle"); + Threads_Exit(0, 0xFF); // Terminate user + } + } + memset( gaUserHandles, 0, size ); + } + + // Restore handles + memcpy( gaUserHandles, handles, NumFDs * sizeof(tVFS_Handle) ); + // Reference when copied + for( i = 0; i < NumFDs; i ++ ) + { + tVFS_Handle *h = &handles[i]; + + if( h->Node->Type && h->Node->Type->Reference ) + h->Node->Type->Reference( h->Node ); + } +} + +void VFS_FreeSavedHandles(int NumFDs, void *Handles) +{ + tVFS_Handle *handles = Handles; + int i; + + // Dereference all saved nodes + for( i = 0; i < NumFDs; i ++ ) + { + tVFS_Handle *h = &handles[i]; + + if( h->Node->Type && h->Node->Type->Close ) + h->Node->Type->Close( h->Node ); + } + free( Handles ); +} -- 2.20.1