diff options
Diffstat (limited to 'src/sim')
-rw-r--r-- | src/sim/Process.py | 2 | ||||
-rw-r--r-- | src/sim/fd_array.cc | 7 | ||||
-rw-r--r-- | src/sim/fd_array.hh | 8 | ||||
-rw-r--r-- | src/sim/process.cc | 201 | ||||
-rw-r--r-- | src/sim/process.hh | 71 | ||||
-rw-r--r-- | src/sim/syscall_desc.hh | 4 | ||||
-rw-r--r-- | src/sim/syscall_emul.cc | 141 | ||||
-rw-r--r-- | src/sim/syscall_emul.hh | 269 |
8 files changed, 538 insertions, 165 deletions
diff --git a/src/sim/Process.py b/src/sim/Process.py index 9c4ee4760..743e5247c 100644 --- a/src/sim/Process.py +++ b/src/sim/Process.py @@ -40,7 +40,7 @@ class Process(SimObject): useArchPT = Param.Bool('false', 'maintain an in-memory version of the page\ table in an architecture-specific format') kvmInSE = Param.Bool('false', 'initialize the process for KvmCPU in SE') - max_stack_size = Param.MemorySize('64MB', 'maximum size of the stack') + maxStackSize = Param.MemorySize('64MB', 'maximum size of the stack') uid = Param.Int(100, 'user id') euid = Param.Int(100, 'effective user id') diff --git a/src/sim/fd_array.cc b/src/sim/fd_array.cc index d73707054..9b6cbb573 100644 --- a/src/sim/fd_array.cc +++ b/src/sim/fd_array.cc @@ -322,6 +322,13 @@ FDArray::getFDEntry(int tgt_fd) return _fdArray[tgt_fd]; } +void +FDArray::setFDEntry(int tgt_fd, std::shared_ptr<FDEntry> fdep) +{ + assert(0 <= tgt_fd && tgt_fd < _fdArray.size()); + _fdArray[tgt_fd] = fdep; +} + int FDArray::closeFDEntry(int tgt_fd) { diff --git a/src/sim/fd_array.hh b/src/sim/fd_array.hh index 1d57c4654..a41e078e7 100644 --- a/src/sim/fd_array.hh +++ b/src/sim/fd_array.hh @@ -104,6 +104,14 @@ class FDArray int getSize() const { return _fdArray.size(); } /** + * Put the pointer specified by fdep into the _fdArray entry indexed + * by tgt_fd. + * @param tgt_fd Use target file descriptors to index the array. + * @param fdep Incoming pointer used to set the entry pointed to by tgt_fd. + */ + void setFDEntry(int tgt_fd, std::shared_ptr<FDEntry> fdep); + + /** * Try to close the host file descriptor. If successful, set the * specified file descriptor entry object pointer to nullptr. * Used to "close" the target file descriptor. diff --git a/src/sim/process.cc b/src/sim/process.cc index caf986123..7cfaf6530 100644 --- a/src/sim/process.cc +++ b/src/sim/process.cc @@ -95,9 +95,6 @@ using namespace TheISA; Process::Process(ProcessParams * params, ObjectFile * obj_file) : SimObject(params), system(params->system), - brk_point(0), stack_base(0), stack_size(0), stack_min(0), - max_stack_size(params->max_stack_size), - next_thread_stack_base(0), useArchPT(params->useArchPT), kvmInSE(params->kvmInSE), pTable(useArchPT ? @@ -113,10 +110,10 @@ Process::Process(ProcessParams * params, ObjectFile * obj_file) _gid(params->gid), _egid(params->egid), _pid(params->pid), _ppid(params->ppid), _pgid(params->pgid), drivers(params->drivers), - fds(make_shared<FDArray>(params->input, params->output, params->errout)) + fds(make_shared<FDArray>(params->input, params->output, params->errout)), + maxStackSize(params->maxStackSize), + childClearTID(0) { - mmap_end = 0; - if (_pid >= System::maxPID) fatal("_pid is too large: %d", _pid); @@ -124,14 +121,29 @@ Process::Process(ProcessParams * params, ObjectFile * obj_file) if (!ret_pair.second) fatal("_pid %d is already used", _pid); - // load up symbols, if any... these may be used for debugging or - // profiling. + /** + * Linux bundles together processes into this concept called a thread + * group. The thread group is responsible for recording which processes + * behave as threads within a process context. The thread group leader + * is the process who's tgid is equal to its pid. Other processes which + * belong to the thread group, but do not lead the thread group, are + * treated as child threads. These threads are created by the clone system + * call with options specified to create threads (differing from the + * options used to implement a fork). By default, set up the tgid/pid + * with a new, equivalent value. If CLONE_THREAD is specified, patch + * the tgid value with the old process' value. + */ + _tgid = params->pid; + + exitGroup = new bool(); + memState = new MemState(); + sigchld = new bool(); + if (!debugSymbolTable) { debugSymbolTable = new SymbolTable(); if (!objFile->loadGlobalSymbols(debugSymbolTable) || !objFile->loadLocalSymbols(debugSymbolTable) || !objFile->loadWeakSymbols(debugSymbolTable)) { - // didn't load any symbols delete debugSymbolTable; debugSymbolTable = NULL; } @@ -139,14 +151,98 @@ Process::Process(ProcessParams * params, ObjectFile * obj_file) } void +Process::clone(ThreadContext *otc, ThreadContext *ntc, + Process *np, TheISA::IntReg flags) +{ + if (CLONE_VM & flags) { + /** + * Share the process memory address space between the new process + * and the old process. Changes in one will be visible in the other + * due to the pointer use. + */ + delete np->pTable; + np->pTable = pTable; + ntc->getMemProxy().setPageTable(np->pTable); + + delete np->memState; + np->memState = memState; + } else { + /** + * Duplicate the process memory address space. The state needs to be + * copied over (rather than using pointers to share everything). + */ + typedef std::vector<pair<Addr,Addr>> MapVec; + MapVec mappings; + pTable->getMappings(&mappings); + + for (auto map : mappings) { + Addr paddr, vaddr = map.first; + bool alloc_page = !(np->pTable->translate(vaddr, paddr)); + np->replicatePage(vaddr, paddr, otc, ntc, alloc_page); + } + + *np->memState = *memState; + } + + if (CLONE_FILES & flags) { + /** + * The parent and child file descriptors are shared because the + * two FDArray pointers are pointing to the same FDArray. Opening + * and closing file descriptors will be visible to both processes. + */ + np->fds = fds; + } else { + /** + * Copy the file descriptors from the old process into the new + * child process. The file descriptors entry can be opened and + * closed independently of the other process being considered. The + * host file descriptors are also dup'd so that the flags for the + * host file descriptor is independent of the other process. + */ + for (int tgt_fd = 0; tgt_fd < fds->getSize(); tgt_fd++) { + std::shared_ptr<FDArray> nfds = np->fds; + std::shared_ptr<FDEntry> this_fde = (*fds)[tgt_fd]; + if (!this_fde) { + nfds->setFDEntry(tgt_fd, nullptr); + continue; + } + nfds->setFDEntry(tgt_fd, this_fde->clone()); + + auto this_hbfd = std::dynamic_pointer_cast<HBFDEntry>(this_fde); + if (!this_hbfd) + continue; + + int this_sim_fd = this_hbfd->getSimFD(); + if (this_sim_fd <= 2) + continue; + + int np_sim_fd = dup(this_sim_fd); + assert(np_sim_fd != -1); + + auto nhbfd = std::dynamic_pointer_cast<HBFDEntry>((*nfds)[tgt_fd]); + nhbfd->setSimFD(np_sim_fd); + } + } + + if (CLONE_THREAD & flags) { + np->_tgid = _tgid; + delete np->exitGroup; + np->exitGroup = exitGroup; + } + + np->argv.insert(np->argv.end(), argv.begin(), argv.end()); + np->envp.insert(np->envp.end(), envp.begin(), envp.end()); +} + +void Process::regStats() { SimObject::regStats(); using namespace Stats; - num_syscalls - .name(name() + ".num_syscalls") + numSyscalls + .name(name() + ".numSyscalls") .desc("Number of system calls") ; } @@ -154,15 +250,27 @@ Process::regStats() ThreadContext * Process::findFreeContext() { - for (int id : contextIds) { - ThreadContext *tc = system->getThreadContext(id); - if (tc->status() == ThreadContext::Halted) - return tc; + for (auto &it : system->threadContexts) { + if (ThreadContext::Halted == it->status()) + return it; } return NULL; } void +Process::revokeThreadContext(int context_id) +{ + std::vector<ContextID>::iterator it; + for (it = contextIds.begin(); it != contextIds.end(); it++) { + if (*it == context_id) { + contextIds.erase(it); + return; + } + } + warn("Unable to find thread context to revoke"); +} + +void Process::initState() { if (contextIds.empty()) @@ -193,24 +301,44 @@ Process::allocateMem(Addr vaddr, int64_t size, bool clobber) clobber ? PageTableBase::Clobber : PageTableBase::Zero); } +void +Process::replicatePage(Addr vaddr, Addr new_paddr, ThreadContext *old_tc, + ThreadContext *new_tc, bool allocate_page) +{ + if (allocate_page) + new_paddr = system->allocPhysPages(1); + + // Read from old physical page. + uint8_t *buf_p = new uint8_t[PageBytes]; + old_tc->getMemProxy().readBlob(vaddr, buf_p, PageBytes); + + // Create new mapping in process address space by clobbering existing + // mapping (if any existed) and then write to the new physical page. + bool clobber = true; + pTable->map(vaddr, new_paddr, PageBytes, clobber); + new_tc->getMemProxy().writeBlob(vaddr, buf_p, PageBytes); + delete[] buf_p; +} + bool Process::fixupStackFault(Addr vaddr) { // Check if this is already on the stack and there's just no page there // yet. - if (vaddr >= stack_min && vaddr < stack_base) { + if (vaddr >= memState->stackMin && vaddr < memState->stackBase) { allocateMem(roundDown(vaddr, PageBytes), PageBytes); return true; } // We've accessed the next page of the stack, so extend it to include // this address. - if (vaddr < stack_min && vaddr >= stack_base - max_stack_size) { - while (vaddr < stack_min) { - stack_min -= TheISA::PageBytes; - if (stack_base - stack_min > max_stack_size) + if (vaddr < memState->stackMin + && vaddr >= memState->stackBase - maxStackSize) { + while (vaddr < memState->stackMin) { + memState->stackMin -= TheISA::PageBytes; + if (memState->stackBase - memState->stackMin > maxStackSize) fatal("Maximum stack size exceeded\n"); - allocateMem(stack_min, TheISA::PageBytes); + allocateMem(memState->stackMin, TheISA::PageBytes); inform("Increasing stack size by one page."); }; return true; @@ -221,12 +349,12 @@ Process::fixupStackFault(Addr vaddr) void Process::serialize(CheckpointOut &cp) const { - SERIALIZE_SCALAR(brk_point); - SERIALIZE_SCALAR(stack_base); - SERIALIZE_SCALAR(stack_size); - SERIALIZE_SCALAR(stack_min); - SERIALIZE_SCALAR(next_thread_stack_base); - SERIALIZE_SCALAR(mmap_end); + SERIALIZE_SCALAR(memState->brkPoint); + SERIALIZE_SCALAR(memState->stackBase); + SERIALIZE_SCALAR(memState->stackSize); + SERIALIZE_SCALAR(memState->stackMin); + SERIALIZE_SCALAR(memState->nextThreadStackBase); + SERIALIZE_SCALAR(memState->mmapEnd); pTable->serialize(cp); /** * Checkpoints for file descriptors currently do not work. Need to @@ -244,12 +372,12 @@ Process::serialize(CheckpointOut &cp) const void Process::unserialize(CheckpointIn &cp) { - UNSERIALIZE_SCALAR(brk_point); - UNSERIALIZE_SCALAR(stack_base); - UNSERIALIZE_SCALAR(stack_size); - UNSERIALIZE_SCALAR(stack_min); - UNSERIALIZE_SCALAR(next_thread_stack_base); - UNSERIALIZE_SCALAR(mmap_end); + UNSERIALIZE_SCALAR(memState->brkPoint); + UNSERIALIZE_SCALAR(memState->stackBase); + UNSERIALIZE_SCALAR(memState->stackSize); + UNSERIALIZE_SCALAR(memState->stackMin); + UNSERIALIZE_SCALAR(memState->nextThreadStackBase); + UNSERIALIZE_SCALAR(memState->mmapEnd); pTable->unserialize(cp); /** * Checkpoints for file descriptors currently do not work. Need to @@ -278,7 +406,7 @@ Process::map(Addr vaddr, Addr paddr, int size, bool cacheable) void Process::syscall(int64_t callnum, ThreadContext *tc, Fault *fault) { - num_syscalls++; + numSyscalls++; SyscallDesc *desc = getDesc(callnum); if (desc == NULL) @@ -318,12 +446,13 @@ Process::updateBias() // We are allocating the memory area; set the bias to the lowest address // in the allocated memory region. - Addr ld_bias = mmapGrowsDown() ? mmap_end - interp_mapsize : mmap_end; + Addr *end = &memState->mmapEnd; + Addr ld_bias = mmapGrowsDown() ? *end - interp_mapsize : *end; // Adjust the process mmap area to give the interpreter room; the real // execve system call would just invoke the kernel's internal mmap // functions to make these adjustments. - mmap_end = mmapGrowsDown() ? ld_bias : mmap_end + interp_mapsize; + *end = mmapGrowsDown() ? ld_bias : *end + interp_mapsize; interp->updateBias(ld_bias); } diff --git a/src/sim/process.hh b/src/sim/process.hh index fa2eff6fa..9dc29dcb0 100644 --- a/src/sim/process.hh +++ b/src/sim/process.hh @@ -72,6 +72,39 @@ class Process : public SimObject { } }; + struct MemState + { + Addr brkPoint; + Addr stackBase; + unsigned stackSize; + Addr stackMin; + Addr nextThreadStackBase; + Addr mmapEnd; + + MemState() + : brkPoint(0), stackBase(0), stackSize(0), stackMin(0), + nextThreadStackBase(0), mmapEnd(0) + { } + + MemState& + operator=(const MemState &in) + { + if (this == &in) + return *this; + + brkPoint = in.brkPoint; + stackBase = in.stackBase; + stackSize = in.stackSize; + stackMin = in.stackMin; + nextThreadStackBase = in.nextThreadStackBase; + mmapEnd = in.mmapEnd; + return *this; + } + + void serialize(CheckpointOut &cp) const; + void unserialize(CheckpointIn &cp); + }; + Process(ProcessParams *params, ObjectFile *obj_file); void serialize(CheckpointOut &cp) const override; @@ -140,7 +173,13 @@ class Process : public SimObject ThreadContext *findFreeContext(); /** - * Does mmap region grow upward or downward from mmap_end? Most + * After delegating a thread context to a child process + * no longer should relate to the ThreadContext + */ + void revokeThreadContext(int context_id); + + /** + * Does mmap region grow upward or downward from mmapEnd? Most * platforms grow downward, but a few (such as Alpha) grow upward * instead, so they can override this method to return false. */ @@ -161,6 +200,12 @@ class Process : public SimObject */ bool map(Addr vaddr, Addr paddr, int size, bool cacheable = true); + void replicatePage(Addr vaddr, Addr new_paddr, ThreadContext *old_tc, + ThreadContext *new_tc, bool alloc_page); + + void clone(ThreadContext *old_tc, ThreadContext *new_tc, Process *new_p, + TheISA::IntReg flags); + // list of all blocked contexts std::list<WaitRec> waitList; @@ -170,15 +215,7 @@ class Process : public SimObject // system object which owns this process System *system; - Addr brk_point; // top of the data segment - Addr stack_base; // stack segment base - unsigned stack_size; // initial stack size - Addr stack_min; // furthest address accessed from stack base - Addr max_stack_size; // the maximum size allowed for the stack - Addr next_thread_stack_base; // addr for next region w/ multithreaded apps - Addr mmap_end; // base of automatic mmap region allocs - - Stats::Scalar num_syscalls; // track how many system calls are executed + Stats::Scalar numSyscalls; // track how many system calls are executed bool useArchPT; // flag for using architecture specific page table bool kvmInSE; // running KVM requires special initialization @@ -209,6 +246,20 @@ class Process : public SimObject std::vector<EmulatedDriver *> drivers; std::shared_ptr<FDArray> fds; + + bool *exitGroup; + + Addr maxStackSize; + MemState *memState; + + /** + * Calls a futex wakeup at the address specified by this pointer when + * this process exits. + */ + uint64_t childClearTID; + + // Process was forked with SIGCHLD set. + bool *sigchld; }; #endif // __PROCESS_HH__ diff --git a/src/sim/syscall_desc.hh b/src/sim/syscall_desc.hh index b4d24e672..d72803e43 100644 --- a/src/sim/syscall_desc.hh +++ b/src/sim/syscall_desc.hh @@ -107,6 +107,10 @@ class SyscallDesc { std::string name() { return _name; } + int getFlags() const { return _flags; } + + void setFlags(int flags) { _flags = flags; } + private: /** System call name (e.g., open, mmap, clone, socket, etc.) */ std::string _name; diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index c6b89b0c7..07899ec9a 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -74,18 +74,41 @@ ignoreFunc(SyscallDesc *desc, int callnum, Process *process, return 0; } +static void +exitFutexWake(ThreadContext *tc, uint64_t uaddr) +{ + std::map<uint64_t, std::list<ThreadContext *> * > + &futex_map = tc->getSystemPtr()->futexMap; + + int wokenUp = 0; + std::list<ThreadContext *> * tcWaitList; + if (futex_map.count(uaddr)) { + tcWaitList = futex_map.find(uaddr)->second; + if (tcWaitList->size() > 0) { + tcWaitList->front()->activate(); + tcWaitList->pop_front(); + wokenUp++; + } + if (tcWaitList->empty()) { + futex_map.erase(uaddr); + delete tcWaitList; + } + } + DPRINTF(SyscallVerbose, "exit: FUTEX_WAKE, activated %d waiting " + "thread contexts\n", wokenUp); +} SyscallReturn -exitFunc(SyscallDesc *desc, int callnum, Process *process, - ThreadContext *tc) +exitFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) { - if (process->system->numRunningContexts() == 1) { - // Last running context... exit simulator + if (p->system->numRunningContexts() == 1 && !p->childClearTID) { + // Last running free-parent context; exit simulator. int index = 0; exitSimLoop("target called exit()", - process->getSyscallArg(tc, index) & 0xff); + p->getSyscallArg(tc, index) & 0xff); } else { - // other running threads... just halt this one + if (p->childClearTID) + exitFutexWake(tc, p->childClearTID); tc->halt(); } @@ -130,11 +153,12 @@ brkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) // in Linux at least, brk(0) returns the current break value // (note that the syscall and the glibc function have different behavior) if (new_brk == 0) - return p->brk_point; + return p->memState->brkPoint; - if (new_brk > p->brk_point) { + if (new_brk > p->memState->brkPoint) { // might need to allocate some new pages - for (ChunkGenerator gen(p->brk_point, new_brk - p->brk_point, + for (ChunkGenerator gen(p->memState->brkPoint, + new_brk - p->memState->brkPoint, PageBytes); !gen.done(); gen.next()) { if (!p->pTable->translate(gen.addr())) p->allocateMem(roundDown(gen.addr(), PageBytes), PageBytes); @@ -159,12 +183,22 @@ brkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) } } - p->brk_point = new_brk; + p->memState->brkPoint = new_brk; DPRINTF_SYSCALL(Verbose, "brk: break point changed to: %#X\n", - p->brk_point); - return p->brk_point; + p->memState->brkPoint); + return p->memState->brkPoint; } +SyscallReturn +setTidAddressFunc(SyscallDesc *desc, int callnum, Process *process, + ThreadContext *tc) +{ + int index = 0; + uint64_t tidPtr = process->getSyscallArg(tc, index); + + process->childClearTID = tidPtr; + return process->pid(); +} SyscallReturn closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) @@ -860,89 +894,6 @@ getegidFunc(SyscallDesc *desc, int callnum, Process *process, return process->egid(); } - -SyscallReturn -cloneFunc(SyscallDesc *desc, int callnum, Process *process, - ThreadContext *tc) -{ - int index = 0; - IntReg flags = process->getSyscallArg(tc, index); - IntReg newStack = process->getSyscallArg(tc, index); - - DPRINTF(SyscallVerbose, "In sys_clone:\n"); - DPRINTF(SyscallVerbose, " Flags=%llx\n", flags); - DPRINTF(SyscallVerbose, " Child stack=%llx\n", newStack); - - - if (flags != 0x10f00) { - warn("This sys_clone implementation assumes flags " - "CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD " - "(0x10f00), and may not work correctly with given flags " - "0x%llx\n", flags); - } - - ThreadContext* ctc; // child thread context - if ((ctc = process->findFreeContext())) { - DPRINTF(SyscallVerbose, " Found unallocated thread context\n"); - - ctc->clearArchRegs(); - - // Arch-specific cloning code - #if THE_ISA == ALPHA_ISA or THE_ISA == X86_ISA - // Cloning the misc. regs for these archs is enough - TheISA::copyMiscRegs(tc, ctc); - #elif THE_ISA == SPARC_ISA - TheISA::copyRegs(tc, ctc); - - // TODO: Explain what this code actually does :-) - ctc->setIntReg(NumIntArchRegs + 6, 0); - ctc->setIntReg(NumIntArchRegs + 4, 0); - ctc->setIntReg(NumIntArchRegs + 3, NWindows - 2); - ctc->setIntReg(NumIntArchRegs + 5, NWindows); - ctc->setMiscReg(MISCREG_CWP, 0); - ctc->setIntReg(NumIntArchRegs + 7, 0); - ctc->setMiscRegNoEffect(MISCREG_TL, 0); - ctc->setMiscReg(MISCREG_ASI, ASI_PRIMARY); - - for (int y = 8; y < 32; y++) - ctc->setIntReg(y, tc->readIntReg(y)); - #elif THE_ISA == ARM_ISA - TheISA::copyRegs(tc, ctc); - #else - fatal("sys_clone is not implemented for this ISA\n"); - #endif - - // Set up stack register - ctc->setIntReg(TheISA::StackPointerReg, newStack); - - // Set up syscall return values in parent and child - ctc->setIntReg(ReturnValueReg, 0); // return value, child - - // Alpha needs SyscallSuccessReg=0 in child - #if THE_ISA == ALPHA_ISA - ctc->setIntReg(TheISA::SyscallSuccessReg, 0); - #endif - - // In SPARC/Linux, clone returns 0 on pseudo-return register if - // parent, non-zero if child - #if THE_ISA == SPARC_ISA - tc->setIntReg(TheISA::SyscallPseudoReturnReg, 0); - ctc->setIntReg(TheISA::SyscallPseudoReturnReg, 1); - #endif - - ctc->pcState(tc->nextInstAddr()); - - ctc->activate(); - - // Should return nonzero child TID in parent's syscall return register, - // but for our pthread library any non-zero value will work - return 1; - } else { - fatal("Called sys_clone, but no unallocated thread contexts found!\n"); - return 0; - } -} - SyscallReturn fallocateFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) { diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index e0cbbe786..8a34c3415 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -81,6 +81,7 @@ #include <memory> #include <string> +#include "arch/utility.hh" #include "base/intmath.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" @@ -90,14 +91,14 @@ #include "cpu/base.hh" #include "cpu/thread_context.hh" #include "mem/page_table.hh" +#include "params/Process.hh" #include "sim/emul_driver.hh" #include "sim/process.hh" #include "sim/syscall_debug_macros.hh" +#include "sim/syscall_desc.hh" #include "sim/syscall_emul_buf.hh" #include "sim/syscall_return.hh" -class SyscallDesc; - ////////////////////////////////////////////////////////////////////// // // The following emulation functions are generic enough that they @@ -130,6 +131,10 @@ SyscallReturn exitFunc(SyscallDesc *desc, int num, SyscallReturn exitGroupFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); +/// Target set_tid_address() handler. +SyscallReturn setTidAddressFunc(SyscallDesc *desc, int num, + Process *p, ThreadContext *tc); + /// Target getpagesize() handler. SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); @@ -142,7 +147,7 @@ SyscallReturn brkFunc(SyscallDesc *desc, int num, SyscallReturn closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); -/// Target read() handler. +// Target read() handler. SyscallReturn readFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); @@ -272,10 +277,6 @@ SyscallReturn geteuidFunc(SyscallDesc *desc, int num, SyscallReturn getegidFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); -/// Target clone() handler. -SyscallReturn cloneFunc(SyscallDesc *desc, int num, - Process *p, ThreadContext *tc); - /// Target access() handler SyscallReturn accessFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); @@ -304,14 +305,14 @@ futexFunc(SyscallDesc *desc, int callnum, Process *process, std::map<uint64_t, std::list<ThreadContext *> * > &futex_map = tc->getSystemPtr()->futexMap; - DPRINTF(SyscallVerbose, "In sys_futex: Address=%llx, op=%d, val=%d\n", + DPRINTF(SyscallVerbose, "futex: Address=%llx, op=%d, val=%d\n", uaddr, op, val); op &= ~OS::TGT_FUTEX_PRIVATE_FLAG; if (op == OS::TGT_FUTEX_WAIT) { if (timeout != 0) { - warn("sys_futex: FUTEX_WAIT with non-null timeout unimplemented;" + warn("futex: FUTEX_WAIT with non-null timeout unimplemented;" "we'll wait indefinitely"); } @@ -321,7 +322,7 @@ futexFunc(SyscallDesc *desc, int callnum, Process *process, delete[] buf; if (val != mem_val) { - DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAKE, read: %d, " + DPRINTF(SyscallVerbose, "futex: FUTEX_WAKE, read: %d, " "expected: %d\n", mem_val, val); return -OS::TGT_EWOULDBLOCK; } @@ -336,8 +337,8 @@ futexFunc(SyscallDesc *desc, int callnum, Process *process, std::list<ThreadContext *> * >(uaddr, tcWaitList)); } tcWaitList->push_back(tc); - DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAIT, suspending calling " - "thread context\n"); + DPRINTF(SyscallVerbose, "futex: FUTEX_WAIT, suspending calling thread " + "context on address 0x%lx\n", uaddr); tc->suspend(); return 0; } else if (op == OS::TGT_FUTEX_WAKE){ @@ -355,11 +356,12 @@ futexFunc(SyscallDesc *desc, int callnum, Process *process, delete tcWaitList; } } - DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAKE, activated %d waiting " - "thread contexts\n", wokenUp); + DPRINTF(SyscallVerbose, "futex: FUTEX_WAKE, activated %d waiting " + "thread context on address 0x%lx\n", + wokenUp, uaddr); return wokenUp; } else { - warn("sys_futex: op %d is not implemented, just returning...", op); + warn("futex: op %d is not implemented, just returning...", op); return 0; } @@ -883,11 +885,11 @@ mremapFunc(SyscallDesc *desc, int callnum, Process *process, ThreadContext *tc) new_length = roundUp(new_length, TheISA::PageBytes); if (new_length > old_length) { - if ((start + old_length) == process->mmap_end && + if ((start + old_length) == process->memState->mmapEnd && (!use_provided_address || provided_address == start)) { uint64_t diff = new_length - old_length; - process->allocateMem(process->mmap_end, diff); - process->mmap_end += diff; + process->allocateMem(process->memState->mmapEnd, diff); + process->memState->mmapEnd += diff; return start; } else { if (!use_provided_address && !(flags & OS::TGT_MREMAP_MAYMOVE)) { @@ -895,7 +897,7 @@ mremapFunc(SyscallDesc *desc, int callnum, Process *process, ThreadContext *tc) return -ENOMEM; } else { uint64_t new_start = use_provided_address ? - provided_address : process->mmap_end; + provided_address : process->memState->mmapEnd; process->pTable->remap(start, old_length, new_start); warn("mremapping to new vaddr %08p-%08p, adding %d\n", new_start, new_start + new_length, @@ -905,9 +907,9 @@ mremapFunc(SyscallDesc *desc, int callnum, Process *process, ThreadContext *tc) new_length - old_length, use_provided_address /* clobber */); if (!use_provided_address) - process->mmap_end += new_length; + process->memState->mmapEnd += new_length; if (use_provided_address && - new_start + new_length > process->mmap_end) { + new_start + new_length > process->memState->mmapEnd) { // something fishy going on here, at least notify the user // @todo: increase mmap_end? warn("mmap region limit exceeded with MREMAP_FIXED\n"); @@ -1179,6 +1181,132 @@ statfsFunc(SyscallDesc *desc, int callnum, Process *process, return 0; } +template <class OS> +SyscallReturn +cloneFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) +{ + int index = 0; + TheISA::IntReg flags = p->getSyscallArg(tc, index); + TheISA::IntReg newStack = p->getSyscallArg(tc, index); + Addr ptidPtr = p->getSyscallArg(tc, index); + Addr ctidPtr = p->getSyscallArg(tc, index); + Addr tlsPtr M5_VAR_USED = p->getSyscallArg(tc, index); + + if (((flags & OS::TGT_CLONE_SIGHAND)&& !(flags & OS::TGT_CLONE_VM)) || + ((flags & OS::TGT_CLONE_THREAD) && !(flags & OS::TGT_CLONE_SIGHAND)) || + ((flags & OS::TGT_CLONE_FS) && (flags & OS::TGT_CLONE_NEWNS)) || + ((flags & OS::TGT_CLONE_NEWIPC) && (flags & OS::TGT_CLONE_SYSVSEM)) || + ((flags & OS::TGT_CLONE_NEWPID) && (flags & OS::TGT_CLONE_THREAD)) || + ((flags & OS::TGT_CLONE_VM) && !(newStack))) + return -EINVAL; + + ThreadContext *ctc; + if (!(ctc = p->findFreeContext())) + fatal("clone: no spare thread context in system"); + + /** + * Note that ProcessParams is generated by swig and there are no other + * examples of how to create anything but this default constructor. The + * fields are manually initialized instead of passing parameters to the + * constructor. + */ + ProcessParams *pp = new ProcessParams(); + pp->executable.assign(*(new std::string(p->progName()))); + pp->cmd.push_back(*(new std::string(p->progName()))); + pp->system = p->system; + pp->maxStackSize = p->maxStackSize; + pp->cwd.assign(p->getcwd()); + pp->input.assign("stdin"); + pp->output.assign("stdout"); + pp->errout.assign("stderr"); + pp->uid = p->uid(); + pp->euid = p->euid(); + pp->gid = p->gid(); + pp->egid = p->egid(); + + /* Find the first free PID that's less than the maximum */ + std::set<int> const& pids = p->system->PIDs; + int temp_pid = *pids.begin(); + do { + temp_pid++; + } while (pids.find(temp_pid) != pids.end()); + if (temp_pid >= System::maxPID) + fatal("temp_pid is too large: %d", temp_pid); + + pp->pid = temp_pid; + pp->ppid = (flags & OS::TGT_CLONE_THREAD) ? p->ppid() : p->pid(); + Process *cp = pp->create(); + delete pp; + + Process *owner = ctc->getProcessPtr(); + ctc->setProcessPtr(cp); + cp->assignThreadContext(ctc->contextId()); + owner->revokeThreadContext(ctc->contextId()); + + if (flags & OS::TGT_CLONE_PARENT_SETTID) { + BufferArg ptidBuf(ptidPtr, sizeof(long)); + long *ptid = (long *)ptidBuf.bufferPtr(); + *ptid = cp->pid(); + ptidBuf.copyOut(tc->getMemProxy()); + } + + cp->initState(); + p->clone(tc, ctc, cp, flags); + + if (flags & OS::TGT_CLONE_CHILD_SETTID) { + BufferArg ctidBuf(ctidPtr, sizeof(long)); + long *ctid = (long *)ctidBuf.bufferPtr(); + *ctid = cp->pid(); + ctidBuf.copyOut(ctc->getMemProxy()); + } + + if (flags & OS::TGT_CLONE_CHILD_CLEARTID) + cp->childClearTID = (uint64_t)ctidPtr; + + ctc->clearArchRegs(); + +#if THE_ISA == ALPHA_ISA + TheISA::copyMiscRegs(tc, ctc); +#elif THE_ISA == SPARC_ISA + TheISA::copyRegs(tc, ctc); + ctc->setIntReg(TheISA::NumIntArchRegs + 6, 0); + ctc->setIntReg(TheISA::NumIntArchRegs + 4, 0); + ctc->setIntReg(TheISA::NumIntArchRegs + 3, TheISA::NWindows - 2); + ctc->setIntReg(TheISA::NumIntArchRegs + 5, TheISA::NWindows); + ctc->setMiscReg(TheISA::MISCREG_CWP, 0); + ctc->setIntReg(TheISA::NumIntArchRegs + 7, 0); + ctc->setMiscRegNoEffect(TheISA::MISCREG_TL, 0); + ctc->setMiscReg(TheISA::MISCREG_ASI, TheISA::ASI_PRIMARY); + for (int y = 8; y < 32; y++) + ctc->setIntReg(y, tc->readIntReg(y)); +#elif THE_ISA == ARM_ISA or THE_ISA == X86_ISA + TheISA::copyRegs(tc, ctc); +#endif + +#if THE_ISA == X86_ISA + if (flags & OS::TGT_CLONE_SETTLS) { + ctc->setMiscRegNoEffect(TheISA::MISCREG_FS_BASE, tlsPtr); + ctc->setMiscRegNoEffect(TheISA::MISCREG_FS_EFF_BASE, tlsPtr); + } +#endif + + if (newStack) + ctc->setIntReg(TheISA::StackPointerReg, newStack); + + cp->setSyscallReturn(ctc, 0); + +#if THE_ISA == ALPHA_ISA + ctc->setIntReg(TheISA::SyscallSuccessReg, 0); +#elif THE_ISA == SPARC_ISA + tc->setIntReg(TheISA::SyscallPseudoReturnReg, 0); + ctc->setIntReg(TheISA::SyscallPseudoReturnReg, 1); +#endif + + ctc->pcState(tc->nextInstAddr()); + ctc->activate(); + + return cp->pid(); +} /// Target fstatfs() handler. template <class OS> @@ -1329,8 +1457,9 @@ mmapImpl(SyscallDesc *desc, int num, Process *p, ThreadContext *tc, // Extend global mmap region if necessary. Note that we ignore the // start address unless MAP_FIXED is specified. if (!(tgt_flags & OS::TGT_MAP_FIXED)) { - start = p->mmapGrowsDown() ? p->mmap_end - length : p->mmap_end; - p->mmap_end = p->mmapGrowsDown() ? start : p->mmap_end + length; + Addr *end = &p->memState->mmapEnd; + start = p->mmapGrowsDown() ? *end - length : *end; + *end = p->mmapGrowsDown() ? start : *end + length; } DPRINTF_SYSCALL(Verbose, " mmap range is 0x%x - 0x%x\n", @@ -1584,6 +1713,100 @@ utimesFunc(SyscallDesc *desc, int callnum, Process *process, return 0; } + +template <class OS> +SyscallReturn +execveFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) +{ + desc->setFlags(0); + + int index = 0; + std::string path; + SETranslatingPortProxy & mem_proxy = tc->getMemProxy(); + if (!mem_proxy.tryReadString(path, p->getSyscallArg(tc, index))) + return -EFAULT; + + if (access(path.c_str(), F_OK) == -1) + return -EACCES; + + auto read_in = [](std::vector<std::string> & vect, + SETranslatingPortProxy & mem_proxy, + Addr mem_loc) + { + for (int inc = 0; ; inc++) { + BufferArg b((mem_loc + sizeof(Addr) * inc), sizeof(Addr)); + b.copyIn(mem_proxy); + + if (!*(Addr*)b.bufferPtr()) + break; + + vect.push_back(std::string()); + mem_proxy.tryReadString(vect[inc], *(Addr*)b.bufferPtr()); + } + }; + + /** + * Note that ProcessParams is generated by swig and there are no other + * examples of how to create anything but this default constructor. The + * fields are manually initialized instead of passing parameters to the + * constructor. + */ + ProcessParams *pp = new ProcessParams(); + pp->executable = path; + Addr argv_mem_loc = p->getSyscallArg(tc, index); + read_in(pp->cmd, mem_proxy, argv_mem_loc); + Addr envp_mem_loc = p->getSyscallArg(tc, index); + read_in(pp->env, mem_proxy, envp_mem_loc); + pp->uid = p->uid(); + pp->egid = p->egid(); + pp->euid = p->euid(); + pp->gid = p->gid(); + pp->ppid = p->ppid(); + pp->pid = p->pid(); + pp->input.assign("cin"); + pp->output.assign("cout"); + pp->errout.assign("cerr"); + pp->cwd.assign(p->getcwd()); + pp->system = p->system; + pp->maxStackSize = p->maxStackSize; + /** + * Prevent process object creation with identical PIDs (which will trip + * a fatal check in Process constructor). The execve call is supposed to + * take over the currently executing process' identity but replace + * whatever it is doing with a new process image. Instead of hijacking + * the process object in the simulator, we create a new process object + * and bind to the previous process' thread below (hijacking the thread). + */ + p->system->PIDs.erase(p->pid()); + Process *new_p = pp->create(); + delete pp; + + /** + * Work through the file descriptor array and close any files marked + * close-on-exec. + */ + new_p->fds = p->fds; + for (int i = 0; i < new_p->fds->getSize(); i++) { + std::shared_ptr<FDEntry> fdep = (*new_p->fds)[i]; + if (fdep && fdep->getCOE()) + new_p->fds->closeFDEntry(i); + } + + *new_p->sigchld = true; + + delete p; + tc->clearArchRegs(); + tc->setProcessPtr(new_p); + new_p->assignThreadContext(tc->contextId()); + new_p->initState(); + tc->activate(); + TheISA::PCState pcState = tc->pcState(); + tc->setNPC(pcState.instAddr()); + + desc->setFlags(SyscallDesc::SuppressReturnValue); + return 0; +} + /// Target getrusage() function. template <class OS> SyscallReturn |