It is now permission-oriented, not just user/kernel oriented. Added <sys/mman.h> with nice PROT_{READ,WRITE,EXEC,FORK} constants.
862 lines
21 KiB
C++
862 lines
21 KiB
C++
/******************************************************************************
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
|
|
|
This file is part of Sortix.
|
|
|
|
Sortix is free software: you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation, either version 3 of the License, or (at your option) any later
|
|
version.
|
|
|
|
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
process.cpp
|
|
Describes a process belonging to a subsystem.
|
|
|
|
******************************************************************************/
|
|
|
|
#include <sortix/kernel/platform.h>
|
|
#include <sortix/unistd.h>
|
|
#include <sortix/fork.h>
|
|
#include <sortix/mman.h>
|
|
#include <libmaxsi/error.h>
|
|
#include <libmaxsi/memory.h>
|
|
#include <libmaxsi/string.h>
|
|
#include <libmaxsi/sortedlist.h>
|
|
#include "thread.h"
|
|
#include "process.h"
|
|
#include "device.h"
|
|
#include "stream.h"
|
|
#include "filesystem.h"
|
|
#include "directory.h"
|
|
#include "scheduler.h"
|
|
#include <sortix/kernel/memorymanagement.h>
|
|
#include "initrd.h"
|
|
#include "elf.h"
|
|
#include "syscall.h"
|
|
|
|
using namespace Maxsi;
|
|
|
|
namespace Sortix
|
|
{
|
|
bool ProcessSegment::Intersects(ProcessSegment* segments)
|
|
{
|
|
for ( ProcessSegment* tmp = segments; tmp != NULL; tmp = tmp->next )
|
|
{
|
|
if ( tmp->position < position + size &&
|
|
position < tmp->position + tmp->size )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ( next ) { return next->Intersects(segments); }
|
|
|
|
return false;
|
|
}
|
|
|
|
ProcessSegment* ProcessSegment::Fork()
|
|
{
|
|
ProcessSegment* nextclone = NULL;
|
|
if ( next )
|
|
{
|
|
nextclone = next->Fork();
|
|
if ( nextclone == NULL ) { return NULL; }
|
|
}
|
|
|
|
ProcessSegment* clone = new ProcessSegment();
|
|
if ( clone == NULL )
|
|
{
|
|
while ( nextclone != NULL )
|
|
{
|
|
ProcessSegment* todelete = nextclone;
|
|
nextclone = nextclone->next;
|
|
delete todelete;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
next->prev = nextclone;
|
|
clone->next = nextclone;
|
|
clone->position = position;
|
|
clone->size = size;
|
|
|
|
return clone;
|
|
}
|
|
|
|
Process::Process()
|
|
{
|
|
addrspace = 0;
|
|
segments = NULL;
|
|
sigint = false;
|
|
parent = NULL;
|
|
prevsibling = NULL;
|
|
nextsibling = NULL;
|
|
firstchild = NULL;
|
|
zombiechild = NULL;
|
|
firstthread = NULL;
|
|
workingdir = NULL;
|
|
errno = NULL;
|
|
mmapfrom = 0x80000000UL;
|
|
exitstatus = -1;
|
|
pid = AllocatePID();
|
|
Put(this);
|
|
}
|
|
|
|
Process::~Process()
|
|
{
|
|
Remove(this);
|
|
|
|
ResetAddressSpace();
|
|
|
|
// Avoid memory leaks.
|
|
ASSERT(segments == NULL);
|
|
|
|
delete[] workingdir;
|
|
|
|
// TODO: Delete address space!
|
|
}
|
|
|
|
void Process::ResetAddressSpace()
|
|
{
|
|
ProcessSegment* tmp = segments;
|
|
while ( tmp != NULL )
|
|
{
|
|
Memory::UnmapRange(tmp->position, tmp->size);
|
|
ProcessSegment* todelete = tmp;
|
|
tmp = tmp->next;
|
|
delete todelete;
|
|
}
|
|
|
|
segments = NULL;
|
|
errno = NULL;
|
|
}
|
|
|
|
Process* Process::Fork()
|
|
{
|
|
ASSERT(CurrentProcess() == this);
|
|
|
|
Process* clone = new Process;
|
|
if ( !clone ) { return NULL; }
|
|
|
|
ProcessSegment* clonesegments = NULL;
|
|
|
|
// Fork the segment list.
|
|
if ( segments )
|
|
{
|
|
clonesegments = segments->Fork();
|
|
if ( clonesegments == NULL ) { delete clone; return NULL; }
|
|
}
|
|
|
|
// Fork address-space here and copy memory.
|
|
clone->addrspace = Memory::Fork();
|
|
if ( !clone->addrspace )
|
|
{
|
|
// Delete the segment list, since they are currently bogus.
|
|
ProcessSegment* tmp = clonesegments;
|
|
while ( tmp != NULL )
|
|
{
|
|
ProcessSegment* todelete = tmp;
|
|
tmp = tmp->next;
|
|
delete todelete;
|
|
}
|
|
|
|
delete clone; return NULL;
|
|
}
|
|
|
|
// Now it's too late to clean up here, if anything goes wrong, the
|
|
// cloned process should be queued for destruction.
|
|
clone->segments = clonesegments;
|
|
|
|
// Remember the relation to the child process.
|
|
clone->parent = this;
|
|
if ( firstchild )
|
|
{
|
|
firstchild->prevsibling = clone;
|
|
clone->nextsibling = firstchild;
|
|
firstchild = clone;
|
|
}
|
|
else
|
|
{
|
|
firstchild = clone;
|
|
}
|
|
|
|
// Fork the file descriptors.
|
|
if ( !descriptors.Fork(&clone->descriptors) )
|
|
{
|
|
Panic("No error handling when forking FDs fails!");
|
|
}
|
|
|
|
Thread* clonethreads = ForkThreads(clone);
|
|
if ( !clonethreads )
|
|
{
|
|
Panic("No error handling when forking threads fails!");
|
|
}
|
|
|
|
clone->firstthread = clonethreads;
|
|
|
|
// Copy variables.
|
|
clone->mmapfrom = mmapfrom;
|
|
clone->errnop = errnop;
|
|
if ( workingdir ) { clone->workingdir = String::Clone(workingdir); }
|
|
else { clone->workingdir = NULL; }
|
|
|
|
// Now that the cloned process is fully created, we need to signal to
|
|
// its threads that they should insert themselves into the scheduler.
|
|
for ( Thread* tmp = clonethreads; tmp != NULL; tmp = tmp->nextsibling )
|
|
{
|
|
tmp->Ready();
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
|
|
Thread* Process::ForkThreads(Process* processclone)
|
|
{
|
|
Thread* result = NULL;
|
|
Thread* tmpclone = NULL;
|
|
|
|
for ( Thread* tmp = firstthread; tmp != NULL; tmp = tmp->nextsibling )
|
|
{
|
|
Thread* clonethread = tmp->Fork();
|
|
if ( clonethread == NULL )
|
|
{
|
|
while ( tmpclone != NULL )
|
|
{
|
|
Thread* todelete = tmpclone;
|
|
tmpclone = tmpclone->prevsibling;
|
|
delete todelete;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
clonethread->process = processclone;
|
|
|
|
if ( result == NULL ) { result = clonethread; }
|
|
if ( tmpclone != NULL )
|
|
{
|
|
tmpclone->nextsibling = clonethread;
|
|
clonethread->prevsibling = tmpclone;
|
|
}
|
|
|
|
tmpclone = clonethread;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void Process::ResetForExecute()
|
|
{
|
|
// TODO: Delete all threads and their stacks.
|
|
|
|
ResetAddressSpace();
|
|
}
|
|
|
|
int Process::Execute(const char* programname, const byte* program,
|
|
size_t programsize, int argc, const char* const* argv,
|
|
int envc, const char* const* envp,
|
|
CPU::InterruptRegisters* regs)
|
|
{
|
|
ASSERT(CurrentProcess() == this);
|
|
|
|
addr_t entry = ELF::Construct(CurrentProcess(), program, programsize);
|
|
if ( !entry ) { return -1; }
|
|
|
|
// TODO: This may be an ugly hack!
|
|
// TODO: Move this to x86/process.cpp.
|
|
|
|
addr_t stackpos = CurrentThread()->stackpos + CurrentThread()->stacksize;
|
|
|
|
// Alright, move argv onto the new stack! First figure out exactly how
|
|
// big argv actually is.
|
|
addr_t argvpos = stackpos - sizeof(char*) * (argc+1);
|
|
char** stackargv = (char**) argvpos;
|
|
|
|
size_t argvsize = 0;
|
|
for ( int i = 0; i < argc; i++ )
|
|
{
|
|
size_t len = String::Length(argv[i]) + 1;
|
|
argvsize += len;
|
|
char* dest = ((char*) argvpos) - argvsize;
|
|
stackargv[i] = dest;
|
|
Maxsi::Memory::Copy(dest, argv[i], len);
|
|
}
|
|
|
|
stackargv[argc] = NULL;
|
|
|
|
if ( argvsize % 16UL ) { argvsize += 16 - (argvsize % 16UL); }
|
|
|
|
// And then move envp onto the stack.
|
|
addr_t envppos = argvpos - argvsize - sizeof(char*) * (envc+1);
|
|
char** stackenvp = (char**) envppos;
|
|
|
|
size_t envpsize = 0;
|
|
for ( int i = 0; i < envc; i++ )
|
|
{
|
|
size_t len = String::Length(envp[i]) + 1;
|
|
envpsize += len;
|
|
char* dest = ((char*) envppos) - envpsize;
|
|
stackenvp[i] = dest;
|
|
Maxsi::Memory::Copy(dest, envp[i], len);
|
|
}
|
|
|
|
stackenvp[envc] = NULL;
|
|
|
|
if ( envpsize % 16UL ) { envpsize += 16 - (envpsize % 16UL); }
|
|
|
|
stackpos = envppos - envpsize;
|
|
|
|
ExecuteCPU(argc, stackargv, envc, stackenvp, stackpos, entry, regs);
|
|
|
|
descriptors.OnExecute();
|
|
|
|
return 0;
|
|
}
|
|
|
|
class SysExecVEState
|
|
{
|
|
public:
|
|
char* filename;
|
|
DevBuffer* dev;
|
|
byte* buffer;
|
|
size_t count;
|
|
size_t sofar;
|
|
int argc;
|
|
int envc;
|
|
char** argv;
|
|
char** envp;
|
|
|
|
public:
|
|
SysExecVEState()
|
|
{
|
|
filename = NULL;
|
|
dev = NULL;
|
|
buffer = NULL;
|
|
count = 0;
|
|
sofar = 0;
|
|
argc = 0;
|
|
argv = NULL;
|
|
envc = 0;
|
|
envp = NULL;
|
|
}
|
|
|
|
~SysExecVEState()
|
|
{
|
|
delete[] filename;
|
|
if ( dev ) { dev->Unref(); }
|
|
delete[] buffer;
|
|
for ( int i = 0; i < argc; i++ ) { delete[] argv[i]; }
|
|
delete[] argv;
|
|
for ( int i = 0; i < envc; i++ ) { delete[] envp[i]; }
|
|
delete[] envp;
|
|
}
|
|
|
|
};
|
|
|
|
int SysExevVEStage2(SysExecVEState* state)
|
|
{
|
|
if ( !state->dev->IsReadable() ) { Error::Set(EBADF); delete state; return -1; }
|
|
|
|
byte* dest = state->buffer + state->sofar;
|
|
size_t amount = state->count - state->sofar;
|
|
ssize_t bytesread = state->dev->Read(dest, amount);
|
|
|
|
// Check for premature end-of-file.
|
|
if ( bytesread == 0 && amount != 0 )
|
|
{
|
|
Error::Set(EIO); delete state; return -1;
|
|
}
|
|
|
|
// We actually managed to read some data.
|
|
if ( 0 <= bytesread )
|
|
{
|
|
state->sofar += bytesread;
|
|
if ( state->sofar <= state->count )
|
|
{
|
|
CPU::InterruptRegisters* regs = Syscall::InterruptRegs();
|
|
Process* process = CurrentProcess();
|
|
int result = process->Execute(state->filename, state->buffer,
|
|
state->count, state->argc,
|
|
state->argv, state->envc,
|
|
state->envp, regs);
|
|
if ( result == 0 ) { Syscall::AsIs(); }
|
|
delete state;
|
|
return result;
|
|
}
|
|
|
|
return SysExevVEStage2(state);
|
|
}
|
|
|
|
if ( Error::Last() != EBLOCKING ) { delete state; return -1; }
|
|
|
|
// The stream will resume our system call once progress has been
|
|
// made. Our request is certainly not forgotten.
|
|
|
|
// Resume the system call with these parameters.
|
|
Thread* thread = CurrentThread();
|
|
thread->scfunc = (void*) SysExevVEStage2;
|
|
thread->scstate[0] = (size_t) state;
|
|
thread->scsize = sizeof(state);
|
|
|
|
// Now go do something else.
|
|
Syscall::Incomplete();
|
|
return 0;
|
|
}
|
|
|
|
DevBuffer* OpenProgramImage(const char* progname)
|
|
{
|
|
char* abs = Directory::MakeAbsolute("/", progname);
|
|
if ( !abs ) { Error::Set(ENOMEM); return NULL; }
|
|
|
|
// TODO: Use O_EXEC here!
|
|
Device* dev = FileSystem::Open(abs, O_RDONLY, 0);
|
|
delete[] abs;
|
|
|
|
if ( !dev ) { return NULL; }
|
|
if ( !dev->IsType(Device::BUFFER) ) { Error::Set(EACCES); dev->Unref(); return NULL; }
|
|
return (DevBuffer*) dev;
|
|
}
|
|
|
|
int SysExecVE(const char* filename, char* const argv[], char* const envp[])
|
|
{
|
|
// TODO: Validate that all the pointer-y parameters are SAFE!
|
|
|
|
// Use a container class to store everything and handle cleaning up.
|
|
SysExecVEState* state = new SysExecVEState;
|
|
if ( !state ) { return -1; }
|
|
|
|
// Make a copy of argv and filename as they are going to be destroyed
|
|
// when the address space is reset.
|
|
state->filename = String::Clone(filename);
|
|
if ( !state->filename ) { delete state; return -1; }
|
|
|
|
int argc; for ( argc = 0; argv && argv[argc]; argc++ );
|
|
int envc; for ( envc = 0; envp && envp[envc]; envc++ );
|
|
|
|
state->argv = new char*[argc+1];
|
|
if ( !state->argv ) { delete state; return -1; }
|
|
state->argc = argc;
|
|
Maxsi::Memory::Set(state->argv, 0, sizeof(char*) * (state->argc+1));
|
|
|
|
for ( int i = 0; i < state->argc; i++ )
|
|
{
|
|
state->argv[i] = String::Clone(argv[i]);
|
|
if ( !state->argv[i] ) { delete state; return -1; }
|
|
}
|
|
|
|
state->envp = new char*[envc+1];
|
|
if ( !state->envp ) { delete state; return -1; }
|
|
state->envc = envc;
|
|
Maxsi::Memory::Set(state->envp, 0, sizeof(char*) * (state->envc+1));
|
|
|
|
for ( int i = 0; i < state->envc; i++ )
|
|
{
|
|
state->envp[i] = String::Clone(envp[i]);
|
|
if ( !state->envp[i] ) { delete state; return -1; }
|
|
}
|
|
|
|
Process* process = CurrentProcess();
|
|
state->dev = OpenProgramImage(state->filename);
|
|
if ( !state->dev ) { delete state; return -1; }
|
|
|
|
state->dev->Refer(); // TODO: Rules of GC may change soon.
|
|
uintmax_t needed = state->dev->Size();
|
|
if ( SIZE_MAX < needed ) { Error::Set(ENOMEM); delete state; return -1; }
|
|
|
|
state->count = needed;
|
|
state->buffer = new byte[state->count];
|
|
if ( !state->buffer ) { delete state; return -1; }
|
|
|
|
return SysExevVEStage2(state);
|
|
}
|
|
|
|
pid_t SysSForkR(int flags, sforkregs_t* regs)
|
|
{
|
|
// TODO: Properly support sforkr(2).
|
|
if ( flags != SFFORK ) { Error::Set(ENOSYS); return -1; }
|
|
|
|
CPU::InterruptRegisters cpuregs;
|
|
Maxsi::Memory::Set(&cpuregs, 0, sizeof(cpuregs));
|
|
#if defined(PLATFORM_X64)
|
|
cpuregs.rip = regs->rip;
|
|
cpuregs.userrsp = regs->rsp;
|
|
cpuregs.rax = regs->rax;
|
|
cpuregs.rbx = regs->rbx;
|
|
cpuregs.rcx = regs->rcx;
|
|
cpuregs.rdx = regs->rdx;
|
|
cpuregs.rdi = regs->rdi;
|
|
cpuregs.rsi = regs->rsi;
|
|
cpuregs.rbp = regs->rbp;
|
|
cpuregs.r8 = regs->r8;
|
|
cpuregs.r9 = regs->r9;
|
|
cpuregs.r10 = regs->r10;
|
|
cpuregs.r11 = regs->r11;
|
|
cpuregs.r12 = regs->r12;
|
|
cpuregs.r13 = regs->r13;
|
|
cpuregs.r14 = regs->r14;
|
|
cpuregs.r15 = regs->r15;
|
|
cpuregs.cs = 0x18 | 0x3;
|
|
cpuregs.ds = 0x20 | 0x3;
|
|
cpuregs.ss = 0x20 | 0x3;
|
|
//cpuregs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
|
cpuregs.rflags = (1<<1) | (1<<9) | (1<<21);
|
|
#elif defined(PLATFORM_X86)
|
|
cpuregs.eip = regs->eip;
|
|
cpuregs.useresp = regs->esp;
|
|
cpuregs.eax = regs->eax;
|
|
cpuregs.ebx = regs->ebx;
|
|
cpuregs.ecx = regs->ecx;
|
|
cpuregs.edx = regs->edx;
|
|
cpuregs.edi = regs->edi;
|
|
cpuregs.esi = regs->esi;
|
|
cpuregs.ebp = regs->ebp;
|
|
cpuregs.cs = 0x18 | 0x3;
|
|
cpuregs.ds = 0x20 | 0x3;
|
|
cpuregs.ss = 0x20 | 0x3;
|
|
//cpuregs.eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
|
|
cpuregs.eflags = (1<<1) | (1<<9) | (1<<21);
|
|
#else
|
|
#error SysSForkR needs to know about your platform
|
|
#endif
|
|
|
|
CurrentThread()->SaveRegisters(&cpuregs);
|
|
|
|
Process* clone = CurrentProcess()->Fork();
|
|
if ( !clone ) { return -1; }
|
|
|
|
return clone->pid;
|
|
}
|
|
|
|
pid_t SysGetPID()
|
|
{
|
|
return CurrentProcess()->pid;
|
|
}
|
|
|
|
pid_t SysGetParentPID()
|
|
{
|
|
Process* parent = CurrentProcess()->parent;
|
|
if ( !parent ) { return -1; }
|
|
|
|
return parent->pid;
|
|
}
|
|
|
|
pid_t nextpidtoallocate;
|
|
|
|
pid_t Process::AllocatePID()
|
|
{
|
|
return nextpidtoallocate++;
|
|
}
|
|
|
|
int ProcessCompare(Process* a, Process* b)
|
|
{
|
|
if ( a->pid < b->pid ) { return -1; }
|
|
if ( a->pid > b->pid ) { return 1; }
|
|
return 0;
|
|
}
|
|
|
|
int ProcessPIDCompare(Process* a, pid_t pid)
|
|
{
|
|
if ( a->pid < pid ) { return -1; }
|
|
if ( a->pid > pid ) { return 1; }
|
|
return 0;
|
|
}
|
|
|
|
SortedList<Process*>* pidlist;
|
|
|
|
Process* Process::Get(pid_t pid)
|
|
{
|
|
size_t index = pidlist->Search(ProcessPIDCompare, pid);
|
|
if ( index == SIZE_MAX ) { return NULL; }
|
|
|
|
return pidlist->Get(index);
|
|
}
|
|
|
|
bool Process::Put(Process* process)
|
|
{
|
|
return pidlist->Add(process);
|
|
}
|
|
|
|
void Process::Remove(Process* process)
|
|
{
|
|
size_t index = pidlist->Search(process);
|
|
ASSERT(index != SIZE_MAX);
|
|
|
|
pidlist->Remove(index);
|
|
}
|
|
|
|
void Process::OnChildProcessExit(Process* process)
|
|
{
|
|
ASSERT(process->parent == this);
|
|
|
|
for ( Thread* thread = firstthread; thread; thread = thread->nextsibling )
|
|
{
|
|
if ( thread->onchildprocessexit )
|
|
{
|
|
thread->onchildprocessexit(thread, process);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Process::Exit(int status)
|
|
{
|
|
// Status codes can only contain 8 bits according to ISO C and POSIX.
|
|
status %= 256;
|
|
|
|
ASSERT(this == CurrentProcess());
|
|
|
|
Process* init = Scheduler::GetInitProcess();
|
|
|
|
if ( pid == 0 ) { Panic("System idle process exited"); }
|
|
|
|
// If the init process terminated successfully, time to halt.
|
|
if ( this == init )
|
|
{
|
|
switch ( status )
|
|
{
|
|
case 0: CPU::ShutDown();
|
|
case 1: CPU::Reboot();
|
|
default: PanicF("The init process exited abnormally with status code %u\n", status);
|
|
}
|
|
}
|
|
|
|
// Take care of the orphans, so give them to init.
|
|
while ( firstchild )
|
|
{
|
|
Process* orphan = firstchild;
|
|
firstchild = orphan->nextsibling;
|
|
if ( firstchild ) { firstchild->prevsibling = NULL; }
|
|
orphan->parent = init;
|
|
orphan->prevsibling = NULL;
|
|
orphan->nextsibling = init->firstchild;
|
|
if ( orphan->nextsibling ) { orphan->nextsibling->prevsibling = orphan; }
|
|
init->firstchild = orphan;
|
|
}
|
|
|
|
// Remove the current process from the family tree.
|
|
if ( !prevsibling )
|
|
{
|
|
parent->firstchild = nextsibling;
|
|
}
|
|
else
|
|
{
|
|
prevsibling->nextsibling = nextsibling;
|
|
}
|
|
|
|
if ( nextsibling )
|
|
{
|
|
nextsibling->prevsibling = prevsibling;
|
|
}
|
|
|
|
// Close all the file descriptors.
|
|
descriptors.Reset();
|
|
|
|
// Make all threads belonging to process unrunnable.
|
|
for ( Thread* t = firstthread; t; t = t->nextsibling )
|
|
{
|
|
Scheduler::EarlyWakeUp(t);
|
|
Scheduler::SetThreadState(t, Thread::State::NONE);
|
|
}
|
|
|
|
// Delete the threads.
|
|
while ( firstthread )
|
|
{
|
|
Thread* todelete = firstthread;
|
|
firstthread = firstthread->nextsibling;
|
|
delete todelete;
|
|
}
|
|
|
|
// Now clean up the address space.
|
|
ResetAddressSpace();
|
|
|
|
// TODO: Actually delete the address space. This is a small memory leak
|
|
// of a couple pages.
|
|
|
|
exitstatus = status;
|
|
nextsibling = parent->zombiechild;
|
|
if ( parent->zombiechild ) { parent->zombiechild->prevsibling = this; }
|
|
parent->zombiechild = this;
|
|
|
|
// Notify the parent process that the child has become a zombie.
|
|
parent->OnChildProcessExit(this);
|
|
|
|
// Now, as a final operation, get rid of the address space. This should
|
|
// return us to the original kernel address space containing nothing
|
|
// but the kernel.
|
|
Memory::DestroyAddressSpace();
|
|
}
|
|
|
|
void SysExit(int status)
|
|
{
|
|
CurrentProcess()->Exit(status);
|
|
|
|
// And so, the process had vanished from existence. But as fate would
|
|
// have it, soon a replacement took its place.
|
|
Scheduler::ProcessTerminated(Syscall::InterruptRegs());
|
|
Syscall::AsIs();
|
|
}
|
|
|
|
struct SysWait_t
|
|
{
|
|
union { size_t align1; pid_t pid; };
|
|
union { size_t align2; int* status; };
|
|
union { size_t align3; int options; };
|
|
};
|
|
|
|
STATIC_ASSERT(sizeof(SysWait_t) <= sizeof(Thread::scstate));
|
|
|
|
void SysWaitCallback(Thread* thread, Process* exitee)
|
|
{
|
|
// See if this process matches what we are looking for.
|
|
SysWait_t* state = (SysWait_t*) thread->scstate;
|
|
if ( state->pid != -1 && state->pid != exitee->pid ) { return; }
|
|
|
|
thread->onchildprocessexit = NULL;
|
|
|
|
Syscall::ScheduleResumption(thread);
|
|
}
|
|
|
|
pid_t SysWait(pid_t pid, int* status, int options)
|
|
{
|
|
Thread* thread = CurrentThread();
|
|
Process* process = thread->process;
|
|
|
|
if ( pid != -1 )
|
|
{
|
|
Process* waitingfor = Process::Get(pid);
|
|
if ( !waitingfor ) { Error::Set(ECHILD); return -1; }
|
|
if ( waitingfor->parent != process ) { Error::Set(ECHILD); return -1; }
|
|
}
|
|
|
|
// Find any zombie children matching the search description.
|
|
for ( Process* zombie = process->zombiechild; zombie; zombie = zombie->nextsibling )
|
|
{
|
|
if ( pid != -1 && pid != zombie->pid ) { continue; }
|
|
|
|
pid = zombie->pid;
|
|
// TODO: Validate that status is a valid user-space int!
|
|
if ( status ) { *status = zombie->exitstatus; }
|
|
|
|
if ( zombie == process->zombiechild )
|
|
{
|
|
process->zombiechild = zombie->nextsibling;
|
|
if ( zombie->nextsibling ) { zombie->nextsibling->prevsibling = NULL; }
|
|
}
|
|
else
|
|
{
|
|
zombie->prevsibling->nextsibling = zombie->nextsibling;
|
|
if ( zombie->nextsibling ) { zombie->nextsibling->prevsibling = zombie->prevsibling; }
|
|
}
|
|
|
|
// And so, the process was fully deleted.
|
|
delete zombie;
|
|
|
|
return pid;
|
|
}
|
|
|
|
// The process needs to have children, otherwise we are waiting for
|
|
// nothing to happen.
|
|
if ( !process->firstchild ) { Error::Set(ECHILD); return -1; }
|
|
|
|
// Resumes this system call when the wait condition has been met.
|
|
thread->onchildprocessexit = SysWaitCallback;
|
|
|
|
// Resume the system call with these parameters.
|
|
thread->scfunc = (void*) SysWait;
|
|
SysWait_t* state = (SysWait_t*) thread->scstate;
|
|
state->pid = pid;
|
|
state->status = status;
|
|
state->options = options;
|
|
thread->scsize = sizeof(SysWait_t);
|
|
|
|
// Now go do something else.
|
|
Syscall::Incomplete();
|
|
return 0;
|
|
}
|
|
|
|
int SysRegisterErrno(int* errnop)
|
|
{
|
|
CurrentProcess()->errnop = errnop;
|
|
return 0;
|
|
}
|
|
|
|
void* SysSbrk(intptr_t increment)
|
|
{
|
|
Process* process = CurrentProcess();
|
|
ProcessSegment* dataseg = NULL;
|
|
for ( ProcessSegment* iter = process->segments; iter; iter = iter->next )
|
|
{
|
|
if ( !iter->type == SEG_DATA ) { continue; }
|
|
if ( dataseg && iter->position < dataseg->position ) { continue; }
|
|
dataseg = iter;
|
|
}
|
|
if ( !dataseg ) { Error::Set(ENOMEM); return (void*) -1UL; }
|
|
addr_t currentend = dataseg->position + dataseg->size;
|
|
addr_t newend = currentend + increment;
|
|
if ( newend < dataseg->position ) { Error::Set(EINVAL); return (void*) -1UL; }
|
|
if ( newend < currentend )
|
|
{
|
|
addr_t unmapfrom = Page::AlignUp(newend);
|
|
if ( unmapfrom < currentend )
|
|
{
|
|
size_t unmapbytes = Page::AlignUp(currentend - unmapfrom);
|
|
Memory::UnmapRange(unmapfrom, unmapbytes);
|
|
}
|
|
}
|
|
else if ( currentend < newend )
|
|
{
|
|
// TODO: HACK: Make a safer way of expanding the data segment
|
|
// without segments possibly colliding!
|
|
addr_t mapfrom = Page::AlignUp(currentend);
|
|
if ( mapfrom < newend )
|
|
{
|
|
size_t mapbytes = Page::AlignUp(newend - mapfrom);
|
|
int prot = PROT_FORK | PROT_READ | PROT_WRITE | PROT_KREAD | PROT_KWRITE;
|
|
if ( !Memory::MapRange(mapfrom, mapbytes, prot) )
|
|
{
|
|
return (void*) -1UL;
|
|
}
|
|
}
|
|
}
|
|
dataseg->size += increment;
|
|
return (void*) newend;
|
|
}
|
|
|
|
size_t SysGetPageSize()
|
|
{
|
|
// TODO: Query the virtual memory layer or look up in the process class.
|
|
return 0x1000UL;
|
|
}
|
|
|
|
void Process::Init()
|
|
{
|
|
Syscall::Register(SYSCALL_EXEC, (void*) SysExecVE);
|
|
Syscall::Register(SYSCALL_SFORKR, (void*) SysSForkR);
|
|
Syscall::Register(SYSCALL_GETPID, (void*) SysGetPID);
|
|
Syscall::Register(SYSCALL_GETPPID, (void*) SysGetParentPID);
|
|
Syscall::Register(SYSCALL_EXIT, (void*) SysExit);
|
|
Syscall::Register(SYSCALL_WAIT, (void*) SysWait);
|
|
Syscall::Register(SYSCALL_REGISTER_ERRNO, (void*) SysRegisterErrno);
|
|
Syscall::Register(SYSCALL_SBRK, (void*) SysSbrk);
|
|
Syscall::Register(SYSCALL_GET_PAGE_SIZE, (void*) SysGetPageSize);
|
|
|
|
nextpidtoallocate = 0;
|
|
|
|
pidlist = new SortedList<Process*>(ProcessCompare);
|
|
if ( !pidlist ) { Panic("could not allocate pidlist\n"); }
|
|
}
|
|
|
|
addr_t Process::AllocVirtualAddr(size_t size)
|
|
{
|
|
return (mmapfrom -= size);
|
|
}
|
|
}
|