diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -74,18 +74,41 @@ return 0; } +static void +exitFutexWake(ThreadContext *tc, uint64_t uaddr) +{ + std::map * > + &futex_map = tc->getSystemPtr()->futexMap; + + int wokenUp = 0; + std::list * 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 @@ // 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->brk_point; - if (new_brk > p->brk_point) { + if (new_brk > p->memState->brk_point) { // might need to allocate some new pages - for (ChunkGenerator gen(p->brk_point, new_brk - p->brk_point, + for (ChunkGenerator gen(p->memState->brk_point, + new_brk - p->memState->brk_point, PageBytes); !gen.done(); gen.next()) { if (!p->pTable->translate(gen.addr())) p->allocateMem(roundDown(gen.addr(), PageBytes), PageBytes); @@ -159,12 +183,22 @@ } } - p->brk_point = new_brk; + p->memState->brk_point = new_brk; DPRINTF_SYSCALL(Verbose, "brk: break point changed to: %#X\n", - p->brk_point); - return p->brk_point; + p->memState->brk_point); + return p->memState->brk_point; } +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) @@ -850,88 +884,6 @@ 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() ) != NULL ) { - 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 accessFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, int index) { diff --git a/src/sim/syscall_desc.hh b/src/sim/syscall_desc.hh --- a/src/sim/syscall_desc.hh +++ b/src/sim/syscall_desc.hh @@ -107,6 +107,10 @@ std::string name() { return _name; } + int getFlags() { 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.hh b/src/sim/syscall_emul.hh --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -70,6 +70,7 @@ #include #include +#include "arch/utility.hh" #include "base/intmath.hh" #include "base/loader/object_file.hh" #include "base/misc.hh" @@ -79,15 +80,15 @@ #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/fd_entry.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 @@ -116,6 +117,10 @@ 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); @@ -128,7 +133,7 @@ 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); @@ -254,10 +259,6 @@ 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); @@ -285,14 +286,14 @@ std::map * > &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"); } @@ -302,7 +303,7 @@ 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; } @@ -317,8 +318,8 @@ std::list * >(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){ @@ -336,11 +337,12 @@ 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; } @@ -836,11 +838,11 @@ 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->mmap_end && (!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->mmap_end, diff); + process->memState->mmap_end += diff; return start; } else { if (!use_provided_address && !(flags & OS::TGT_MREMAP_MAYMOVE)) { @@ -848,7 +850,7 @@ return -ENOMEM; } else { uint64_t new_start = use_provided_address ? - provided_address : process->mmap_end; + provided_address : process->memState->mmap_end; process->pTable->remap(start, old_length, new_start); warn("mremapping to new vaddr %08p-%08p, adding %d\n", new_start, new_start + new_length, @@ -858,9 +860,9 @@ new_length - old_length, use_provided_address /* clobber */); if (!use_provided_address) - process->mmap_end += new_length; + process->memState->mmap_end += new_length; if (use_provided_address && - new_start + new_length > process->mmap_end) { + new_start + new_length > process->memState->mmap_end) { // something fishy going on here, at least notify the user // @todo: increase mmap_end? warn("mmap region limit exceeded with MREMAP_FIXED\n"); @@ -1131,6 +1133,125 @@ return 0; } +template +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 = 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->stealFreeContext()) == NULL) + fatal("clone: no spare thread context in system"); + + 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->max_stack_size = 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 */ + auto it_begin = p->system->PIDs.begin(); + uint64_t temp_pid = *it_begin + 1; + while (p->system->PIDs.find(temp_pid) != p->system->PIDs.end()) + temp_pid++; + 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(); + + ctc->setProcessPtr(cp); + cp->assignThreadContext(ctc->contextId()); + p->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 +#if 0 + TheISA::copyRegs(tc, ctc); + 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)); +#endif +#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 @@ -1304,8 +1425,9 @@ // 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->mmap_end; + start = p->mmapGrowsDown() ? *end - length : *end; + *end = p->mmapGrowsDown() ? start : *end + length; } DPRINTF_SYSCALL(Verbose, " mmap range is 0x%x - 0x%x\n", @@ -1561,6 +1683,104 @@ return 0; } + +template +SyscallReturn +execveFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) +{ + desc->setFlags(0); + + int index = 0; + std::string path; + std::vector argv; + std::vector envp; + + if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) + return (TheISA::IntReg)-EFAULT; + + if (access(path.c_str(), F_OK) == -1) + return -EACCES; + + Addr argv_p = p->getSyscallArg(tc, index); + Addr envp_p = p->getSyscallArg(tc, index); + + int numArgv = 0; + while (true) { + BufferArg argvBuf((argv_p + sizeof(Addr) * numArgv), sizeof(Addr)); + argvBuf.copyIn(tc->getMemProxy()); + + if (!*(Addr*)argvBuf.bufferPtr()) + break; + + argv.push_back(std::string()); + tc->getMemProxy().tryReadString(argv[numArgv++], + *(Addr*)argvBuf.bufferPtr()); + } + + int numEnvp = 0; + while (true) { + BufferArg envpBuf((envp_p + sizeof(Addr) * numEnvp), sizeof(Addr)); + envpBuf.copyIn(tc->getMemProxy()); + + if (!*(Addr*)envpBuf.bufferPtr()) + break; + + envp.push_back(std::string()); + tc->getMemProxy().tryReadString(envp[numEnvp++], + *(Addr*)envpBuf.bufferPtr()); + } + + ProcessParams *pp = new ProcessParams(); + + pp->executable = path; + + for (int i = 0; i < numArgv; i++) + pp->cmd.push_back(std::string(argv[i])); + + for (int i = 0; i < numEnvp; i++) + pp->env.push_back(std::string(envp[i])); + + pp->uid = p->uid(); + pp->pid = p->pid(); + pp->egid = p->egid(); + pp->euid = p->euid(); + pp->gid = p->gid(); + pp->ppid = p->ppid(); + + pp->input.assign("cin"); + pp->output.assign("cout"); + pp->errout.assign("cerr"); + + pp->cwd.assign(p->getcwd()); + pp->system = p->system; + pp->max_stack_size = p->maxStackSize; + + Process *new_p = pp->create(); + + new_p->fds = p->fds; + + for (int i = 0; i < new_p->fds.getSize(); i++) { + if (new_p->fds[i] != nullptr && new_p->fds[i]->getCOE()) { + if (new_p->fds[i]->getSimFD() > 2) + close(new_p->fds[i]->getSimFD()); + new_p->fds[i] = nullptr; + } + } + + *new_p->sigchld = true; + + 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 SyscallReturn diff --git a/src/sim/process.hh b/src/sim/process.hh --- a/src/sim/process.hh +++ b/src/sim/process.hh @@ -72,6 +72,39 @@ { } }; + struct MemState + { + Addr brk_point; + Addr stack_base; + unsigned stack_size; + Addr stack_min; + Addr next_thread_stack_base; + Addr mmap_end; + + MemState() + : brk_point(0), stack_base(0), stack_size(0), stack_min(0), + next_thread_stack_base(0), mmap_end(0) + { } + + MemState& + operator=(const MemState &in) + { + if (this == &in) + return *this; + + brk_point = in.brk_point; + stack_base = in.stack_base; + stack_size = in.stack_size; + stack_min = in.stack_min; + next_thread_stack_base = in.next_thread_stack_base; + mmap_end = in.mmap_end; + return *this; + } + + void serialize(CheckpointOut &cp) const; + void unserialize(CheckpointIn &cp); + }; + Process(ProcessParams *params, ObjectFile *obj_file); void serialize(CheckpointOut &cp) const override; @@ -140,6 +173,26 @@ ThreadContext *findFreeContext(); /** + * Steal an available (Halted) context by searching through all halted + * thread contexts in the system. We will rob the process which owns + * the context and place it in our own context vector. + */ + ThreadContext *stealFreeContext(); + + /** + * After delegating a thread context to a child process + * no longer should relate to the ThreadContext + */ + void revokeThreadContext(int context_id) + { + int size = contextIds.size(); + // sanity check + if (size < (context_id + 1)) + return; + contextIds.erase(contextIds.begin() + context_id); + } + + /** * Does mmap region grow upward or downward from mmap_end? Most * platforms grow downward, but a few (such as Alpha) grow upward * instead, so they can override this method to return false. @@ -161,6 +214,12 @@ */ 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 waitList; @@ -170,15 +229,7 @@ // 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 +260,20 @@ std::vector drivers; 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/process.cc b/src/sim/process.cc --- a/src/sim/process.cc +++ b/src/sim/process.cc @@ -100,9 +100,6 @@ 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 ? @@ -117,10 +114,10 @@ _gid(params->gid), _egid(params->egid), _pid(params->pid), _ppid(params->ppid), _pgid(params->pgid), drivers(params->drivers), - fds(params->input, params->output, params->errout) + fds(params->input, params->output, params->errout), + maxStackSize(params->max_stack_size), + childClearTID(0) { - mmap_end = 0; - if (_pid >= System::maxPID) fatal("_pid is too large: %d", _pid); @@ -128,14 +125,29 @@ 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; } @@ -143,14 +155,77 @@ } void +Process::clone(ThreadContext *otc, ThreadContext *ntc, + Process *new_process, TheISA::IntReg flags) +{ + Process *np = dynamic_cast(new_process); + + typedef std::vector> MapVec; + + MapVec mappings; + pTable->getMappings(&mappings); + + if (CLONE_VM & flags) { + delete np->pTable; + np->pTable = pTable; + ntc->getMemProxy().setPageTable(np->pTable); + + delete np->memState; + } else 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) { + np->fds = fds; + } else for (int tgt_fd = 0; tgt_fd < fds.getSize(); tgt_fd++) { + /** + * Copy the file descriptors from one process to the other. If the + * file descriptor entry is empty, make the copy empty as well + */ + if (fds[tgt_fd] == nullptr) { + np->fds[tgt_fd] = nullptr; + continue; + } + + /** + * Create a cloned copy of the file descriptor and assign a new host + * file descriptor by executing a dup on the current host file + * descriptor. + */ + np->fds[tgt_fd] = fds[tgt_fd]->clone(); + if (fds[tgt_fd]->getSimFD() > 2) { + int np_sim_fd = dup(fds[tgt_fd]->getSimFD()); + assert(np_sim_fd != -1); + np->fds[tgt_fd]->setSimFD(np_sim_fd); + } + } + + if (CLONE_THREAD & flags) { + np->_tgid = _tgid; + delete np->exitGroup; + np->exitGroup = exitGroup; + } + + std::vector::iterator it; + for (it = argv.begin(); it != argv.end(); it++) + np->argv.push_back(*(new std::string(*it))); + + for (it = envp.begin(); it != envp.end(); it++) + np->envp.push_back(*(new std::string(*it))); +} + +void Process::regStats() { SimObject::regStats(); using namespace Stats; - num_syscalls - .name(name() + ".num_syscalls") + numSyscalls + .name(name() + ".numSyscalls") .desc("Number of system calls") ; } @@ -166,6 +241,24 @@ return NULL; } +ThreadContext * +Process::stealFreeContext() +{ + vector::iterator begin = system->threadContexts.begin(); + vector::iterator end = system->threadContexts.end(); + for (vector::iterator it = begin; it < end; it++) { + if (ThreadContext::Halted != (*it)->status()) + continue; + + int contextId = (*it)->contextId(); + (*it)->getProcessPtr()->revokeThreadContext(contextId); + assignThreadContext(contextId); + return *it; + } + + return NULL; +} + void Process::initState() { @@ -197,24 +290,43 @@ 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); +} + 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->stack_min && vaddr < memState->stack_base) { 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->stack_min + && vaddr >= memState->stack_base - maxStackSize) { + while (vaddr < memState->stack_min) { + memState->stack_min -= TheISA::PageBytes; + if (memState->stack_base - memState->stack_min > maxStackSize) fatal("Maximum stack size exceeded\n"); - allocateMem(stack_min, TheISA::PageBytes); + allocateMem(memState->stack_min, TheISA::PageBytes); inform("Increasing stack size by one page."); }; return true; @@ -225,12 +337,12 @@ 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->brk_point); + SERIALIZE_SCALAR(memState->stack_base); + SERIALIZE_SCALAR(memState->stack_size); + SERIALIZE_SCALAR(memState->stack_min); + SERIALIZE_SCALAR(memState->next_thread_stack_base); + SERIALIZE_SCALAR(memState->mmap_end); pTable->serialize(cp); for (int x = 0; x < fds.getSize(); x++) { ; //fds[x].serializeSection(cp, csprintf("FDEntry%d", x)); @@ -241,12 +353,12 @@ 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->brk_point); + UNSERIALIZE_SCALAR(memState->stack_base); + UNSERIALIZE_SCALAR(memState->stack_size); + UNSERIALIZE_SCALAR(memState->stack_min); + UNSERIALIZE_SCALAR(memState->next_thread_stack_base); + UNSERIALIZE_SCALAR(memState->mmap_end); pTable->unserialize(cp); for (int x = 0; x < fds.getSize(); x++) { ; //fds[x]->unserializeSection(cp, csprintf("FDEntry%d", x)); @@ -269,7 +381,7 @@ void Process::syscall(int64_t callnum, ThreadContext *tc, Fault *fault) { - num_syscalls++; + numSyscalls++; SyscallDesc *desc = getDesc(callnum); if (desc == NULL) @@ -309,12 +421,13 @@ // 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->mmap_end; + 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/mem/page_table.cc b/src/mem/page_table.cc --- a/src/mem/page_table.cc +++ b/src/mem/page_table.cc @@ -104,6 +104,14 @@ } void +FuncPageTable::getMappings(std::vector> *addr_maps) +{ + PTableItr iter, end; + for (iter = pTable.begin(), end = pTable.end(); iter != end; iter++) + addr_maps->push_back(make_pair(iter->first, iter->second.pageStart())); +} + +void FuncPageTable::unmap(Addr vaddr, int64_t size) { assert(pageOffset(vaddr) == 0); diff --git a/src/mem/se_translating_port_proxy.hh b/src/mem/se_translating_port_proxy.hh --- a/src/mem/se_translating_port_proxy.hh +++ b/src/mem/se_translating_port_proxy.hh @@ -83,6 +83,8 @@ SETranslatingPortProxy(MasterPort& port, Process* p, AllocType alloc); virtual ~SETranslatingPortProxy(); + void setPageTable(PageTableBase *p) { pTable = p; } + void setProcess(Process *p) { process = p; } bool tryReadBlob(Addr addr, uint8_t *p, int size) const; bool tryWriteBlob(Addr addr, const uint8_t *p, int size) const; bool tryMemsetBlob(Addr addr, uint8_t val, int size) const; diff --git a/src/sim/Process.py b/src/sim/Process.py --- a/src/sim/Process.py +++ b/src/sim/Process.py @@ -42,10 +42,10 @@ kvmInSE = Param.Bool('false', 'initialize the process for KvmCPU in SE') max_stack_size = Param.MemorySize('64MB', 'maximum size of the stack') - uid = Param.Int(100, 'user id') - euid = Param.Int(100, 'effective user id') - gid = Param.Int(100, 'group id') - egid = Param.Int(100, 'effective group id') + uid = Param.Int(1000, 'user id') + euid = Param.Int(1000, 'effective user id') + gid = Param.Int(1000, 'group id') + egid = Param.Int(1000, 'effective group id') pid = Param.Int(100, 'process id') ppid = Param.Int(0, 'parent process id') pgid = Param.Int(100, 'process group id') diff --git a/src/cpu/thread_state.hh b/src/cpu/thread_state.hh --- a/src/cpu/thread_state.hh +++ b/src/cpu/thread_state.hh @@ -107,6 +107,16 @@ Process *getProcessPtr() { return process; } + void setProcessPtr(Process *p) + { + process = p; + if (proxy) { + delete proxy; + proxy = NULL; + initMemProxies(NULL); + } + } + SETranslatingPortProxy &getMemProxy(); /** Reads the number of instructions functionally executed and diff --git a/src/mem/page_table.hh b/src/mem/page_table.hh --- a/src/mem/page_table.hh +++ b/src/mem/page_table.hh @@ -194,6 +194,9 @@ pTableCache[2].valid = false; } } + + virtual void getMappings(std::vector> + *addr_mappings) {}; }; /** @@ -239,6 +242,8 @@ void serialize(CheckpointOut &cp) const override; void unserialize(CheckpointIn &cp) override; + + void getMappings(std::vector> *addr_mappings); }; /** diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh --- a/src/cpu/thread_context.hh +++ b/src/cpu/thread_context.hh @@ -161,6 +161,8 @@ virtual Process *getProcessPtr() = 0; + virtual void setProcessPtr(Process *p) = 0; + virtual Status status() const = 0; virtual void setStatus(Status new_status) = 0; @@ -223,6 +225,8 @@ virtual void pcState(const TheISA::PCState &val) = 0; + virtual void setNPC(Addr val) = 0; + virtual void pcStateNoRecord(const TheISA::PCState &val) = 0; virtual Addr instAddr() = 0; @@ -360,6 +364,8 @@ Process *getProcessPtr() { return actualTC->getProcessPtr(); } + void setProcessPtr(Process *p) { actualTC->setProcessPtr(p); } + Status status() const { return actualTC->status(); } void setStatus(Status new_status) { actualTC->setStatus(new_status); } @@ -430,6 +436,8 @@ void pcState(const TheISA::PCState &val) { actualTC->pcState(val); } + void setNPC(Addr val) { actualTC->setNPC(val); } + void pcStateNoRecord(const TheISA::PCState &val) { actualTC->pcState(val); } Addr instAddr() { return actualTC->instAddr(); } diff --git a/src/cpu/o3/thread_context.hh b/src/cpu/o3/thread_context.hh --- a/src/cpu/o3/thread_context.hh +++ b/src/cpu/o3/thread_context.hh @@ -119,6 +119,8 @@ /** Returns a pointer to this thread's process. */ virtual Process *getProcessPtr() { return thread->getProcessPtr(); } + virtual void setProcessPtr(Process *p) { thread->setProcessPtr(p); } + virtual PortProxy &getPhysProxy() { return thread->getPhysProxy(); } virtual FSTranslatingPortProxy &getVirtProxy(); @@ -213,6 +215,10 @@ /** Sets this thread's PC state. */ virtual void pcState(const TheISA::PCState &val); + /** TODO: currently cpu does not have an implementation of setNPC. */ + virtual void setNPC(Addr val) + { /*cpu->setNPC(val);*/ } + virtual void pcStateNoRecord(const TheISA::PCState &val); /** Reads this thread's PC. */ diff --git a/src/cpu/simple_thread.hh b/src/cpu/simple_thread.hh --- a/src/cpu/simple_thread.hh +++ b/src/cpu/simple_thread.hh @@ -355,6 +355,12 @@ return _pcState.nextInstAddr(); } + void + setNPC(Addr val) + { + _pcState.setNPC(val); + } + MicroPC microPC() { diff --git a/src/arch/x86/types.hh b/src/arch/x86/types.hh --- a/src/arch/x86/types.hh +++ b/src/arch/x86/types.hh @@ -305,6 +305,13 @@ PCState() {} PCState(Addr val) { set(val); } + void + setNPC(Addr val) + { + Base::setNPC(val); + _size = 0; + } + uint8_t size() const { return _size; } void size(uint8_t newSize) { _size = newSize; } diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh --- a/src/cpu/checker/thread_context.hh +++ b/src/cpu/checker/thread_context.hh @@ -130,6 +130,8 @@ Process *getProcessPtr() { return actualTC->getProcessPtr(); } + void setProcessPtr(Process *p) { actualTC->setProcessPtr(p); } + PortProxy &getPhysProxy() { return actualTC->getPhysProxy(); } FSTranslatingPortProxy &getVirtProxy() @@ -254,6 +256,12 @@ return actualTC->pcState(val); } + void setNPC(Addr val) + { + checkerTC->setNPC(val); + actualTC->setNPC(val); + } + void pcStateNoRecord(const TheISA::PCState &val) { return actualTC->pcState(val); diff --git a/src/arch/x86/process.cc b/src/arch/x86/process.cc --- a/src/arch/x86/process.cc +++ b/src/arch/x86/process.cc @@ -100,8 +100,20 @@ : Process(params, objFile), syscallDescs(_syscallDescs), numSyscallDescs(_numSyscallDescs) { - brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); - brk_point = roundUp(brk_point, PageBytes); + memState->brk_point = objFile->dataBase() + objFile->dataSize() + + objFile->bssSize(); + memState->brk_point = roundUp(memState->brk_point, PageBytes); +} + +void X86Process::clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *p, TheISA::IntReg flags) +{ + Process::clone(old_tc, new_tc, p, flags); + X86Process *process = dynamic_cast(p); + process->memState->brk_point = memState->brk_point; + process->_gdtStart = _gdtStart; + process->_gdtSize = _gdtSize; + process->syscallDescs = syscallDescs; } X86_64Process::X86_64Process(ProcessParams *params, ObjectFile *objFile, @@ -117,10 +129,11 @@ // Set up stack. On X86_64 Linux, stack goes from the top of memory // downward, less the hole for the kernel address space plus one page // for undertermined purposes. - stack_base = (Addr)0x7FFFFFFFF000ULL; + memState->stack_base = (Addr)0x7FFFFFFFF000ULL; // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // "mmap_base" is a function which defines where mmap region starts in // the process address space. @@ -130,7 +143,7 @@ // We do not use any address space layout randomization in gem5 // therefore the random fields become zero; the smallest gap space was // chosen but gap could potentially be much larger. - mmap_end = (Addr)0x7FFFF7FFF000ULL; + memState->mmap_end = (Addr)0x7FFFF7FFF000ULL; } void @@ -159,10 +172,11 @@ vsyscallPage.vsyscallOffset = 0x400; vsyscallPage.vsysexitOffset = 0x410; - stack_base = _gdtStart; + memState->stack_base = _gdtStart; // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // "mmap_base" is a function which defines where mmap region starts in // the process address space. @@ -172,7 +186,7 @@ // We do not use any address space layout randomization in gem5 // therefore the random fields become zero; the smallest gap space was // chosen but gap could potentially be much larger. - mmap_end = (Addr)0xB7FFF000ULL; + memState->mmap_end = (Addr)0xB7FFF000ULL; } SyscallDesc* @@ -946,18 +960,21 @@ aux_padding + frame_size; - stack_min = stack_base - space_needed; - stack_min = roundDown(stack_min, align); - stack_size = roundUp(stack_base - stack_min, pageSize); + memState->stack_min = memState->stack_base - space_needed; + memState->stack_min = roundDown(memState->stack_min, align); + memState->stack_size = roundUp(memState->stack_base - memState->stack_min, + pageSize); // map memory - Addr stack_end = roundDown(stack_base - stack_size, pageSize); + Addr stack_end = roundDown(memState->stack_base - memState->stack_size, + pageSize); - DPRINTF(Stack, "Mapping the stack: 0x%x %dB\n", stack_end, stack_size); - allocateMem(stack_end, stack_size); + DPRINTF(Stack, "Mapping the stack: 0x%x %dB\n", + stack_end, memState->stack_size); + allocateMem(stack_end, memState->stack_size); // map out initial stack contents - IntType sentry_base = stack_base - sentry_size; + IntType sentry_base = memState->stack_base - sentry_size; IntType file_name_base = sentry_base - file_name_size; IntType env_data_base = file_name_base - env_data_size; IntType arg_data_base = env_data_base - arg_data_size; @@ -976,7 +993,7 @@ DPRINTF(Stack, "0x%x - envp array\n", envp_array_base); DPRINTF(Stack, "0x%x - argv array\n", argv_array_base); DPRINTF(Stack, "0x%x - argc \n", argc_base); - DPRINTF(Stack, "0x%x - stack min\n", stack_min); + DPRINTF(Stack, "0x%x - stack min\n", memState->stack_min); // write contents to stack @@ -1023,14 +1040,14 @@ ThreadContext *tc = system->getThreadContext(contextIds[0]); //Set the stack pointer register - tc->setIntReg(StackPointerReg, stack_min); + tc->setIntReg(StackPointerReg, memState->stack_min); // There doesn't need to be any segment base added in since we're dealing // with the flat segmentation model. tc->pcState(getStartPC()); //Align the "stack_min" to a page boundary. - stack_min = roundDown(stack_min, pageSize); + memState->stack_min = roundDown(memState->stack_min, pageSize); } void @@ -1074,6 +1091,19 @@ return tc->setIntReg(ArgumentReg[i], val); } +void +X86_64Process::clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *p, TheISA::IntReg flags) +{ + X86Process::clone(old_tc, new_tc, p, flags); + X86_64Process *process = dynamic_cast(p); + VSyscallPage *page = &process->vsyscallPage; + page->base = vsyscallPage.base; + page->size = vsyscallPage.size; + page->vtimeOffset = vsyscallPage.vtimeOffset; + page->vgettimeofdayOffset = vsyscallPage.vgettimeofdayOffset; +} + X86ISA::IntReg I386Process::getSyscallArg(ThreadContext *tc, int &i) { @@ -1098,3 +1128,16 @@ assert(i < NumArgumentRegs); return tc->setIntReg(ArgumentReg[i], val); } + +void +I386Process::clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *p, TheISA::IntReg flags) +{ + X86Process::clone(old_tc, new_tc, p, flags); + I386Process *process = dynamic_cast(p); + VSyscallPage *page = &process->vsyscallPage; + page->base = vsyscallPage.base; + page->size = vsyscallPage.size; + page->vsyscallOffset = vsyscallPage.vsyscallOffset; + page->vsysexitOffset = vsyscallPage.vsysexitOffset; +} diff --git a/src/arch/x86/linux/process.cc b/src/arch/x86/linux/process.cc --- a/src/arch/x86/linux/process.cc +++ b/src/arch/x86/linux/process.cc @@ -276,10 +276,10 @@ /* 53 */ SyscallDesc("socketpair", unimplementedFunc), /* 54 */ SyscallDesc("setsockopt", unimplementedFunc), /* 55 */ SyscallDesc("getsockopt", unimplementedFunc), - /* 56 */ SyscallDesc("clone", cloneFunc), + /* 56 */ SyscallDesc("clone", cloneFunc), /* 57 */ SyscallDesc("fork", unimplementedFunc), /* 58 */ SyscallDesc("vfork", unimplementedFunc), - /* 59 */ SyscallDesc("execve", unimplementedFunc), + /* 59 */ SyscallDesc("execve", execveFunc), /* 60 */ SyscallDesc("exit", exitFunc), /* 61 */ SyscallDesc("wait4", unimplementedFunc), /* 62 */ SyscallDesc("kill", unimplementedFunc), @@ -438,7 +438,7 @@ /* 215 */ SyscallDesc("epoll_wait_old", unimplementedFunc), /* 216 */ SyscallDesc("remap_file_pages", unimplementedFunc), /* 217 */ SyscallDesc("getdents64", unimplementedFunc), - /* 218 */ SyscallDesc("set_tid_address", unimplementedFunc), + /* 218 */ SyscallDesc("set_tid_address", setTidAddressFunc), /* 219 */ SyscallDesc("restart_syscall", unimplementedFunc), /* 220 */ SyscallDesc("semtimedop", unimplementedFunc), /* 221 */ SyscallDesc("fadvise64", unimplementedFunc), @@ -542,6 +542,12 @@ sizeof(syscallDescs64) / sizeof(SyscallDesc)) {} +void X86_64LinuxProcess::clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *process, TheISA::IntReg flags) +{ + X86_64Process::clone(old_tc, new_tc, (X86_64Process*)process, flags); +} + static SyscallDesc syscallDescs32[] = { /* 0 */ SyscallDesc("restart_syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), @@ -554,7 +560,7 @@ /* 8 */ SyscallDesc("creat", unimplementedFunc), /* 9 */ SyscallDesc("link", unimplementedFunc), /* 10 */ SyscallDesc("unlink", unimplementedFunc), - /* 11 */ SyscallDesc("execve", unimplementedFunc), + /* 11 */ SyscallDesc("execve", execveFunc), /* 12 */ SyscallDesc("chdir", unimplementedFunc), /* 13 */ SyscallDesc("time", timeFunc), /* 14 */ SyscallDesc("mknod", unimplementedFunc), @@ -663,7 +669,7 @@ /* 117 */ SyscallDesc("ipc", unimplementedFunc), /* 118 */ SyscallDesc("fsync", unimplementedFunc), /* 119 */ SyscallDesc("sigreturn", unimplementedFunc), - /* 120 */ SyscallDesc("clone", unimplementedFunc), + /* 120 */ SyscallDesc("clone", cloneFunc), /* 121 */ SyscallDesc("setdomainname", unimplementedFunc), /* 122 */ SyscallDesc("uname", unameFunc), /* 123 */ SyscallDesc("modify_ldt", unimplementedFunc), @@ -801,7 +807,7 @@ /* 255 */ SyscallDesc("epoll_ctl", unimplementedFunc), /* 256 */ SyscallDesc("epoll_wait", unimplementedFunc), /* 257 */ SyscallDesc("remap_file_pages", unimplementedFunc), - /* 258 */ SyscallDesc("set_tid_address", unimplementedFunc), + /* 258 */ SyscallDesc("set_tid_address", setTidAddressFunc), /* 259 */ SyscallDesc("timer_create", unimplementedFunc), /* 260 */ SyscallDesc("timer_settime", unimplementedFunc), /* 261 */ SyscallDesc("timer_gettime", unimplementedFunc), @@ -873,3 +879,9 @@ : I386Process(params, objFile, syscallDescs32, sizeof(syscallDescs32) / sizeof(SyscallDesc)) {} + +void I386LinuxProcess::clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *process, TheISA::IntReg flags) +{ + I386Process::clone(old_tc, new_tc, (I386Process*)process, flags); +} diff --git a/src/arch/x86/process.hh b/src/arch/x86/process.hh --- a/src/arch/x86/process.hh +++ b/src/arch/x86/process.hh @@ -82,6 +82,8 @@ SyscallDesc* getDesc(int callnum); void setSyscallReturn(ThreadContext *tc, SyscallReturn return_value); + void clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *process, TheISA::IntReg flags); }; class X86_64Process : public X86Process @@ -108,6 +110,8 @@ /// Explicitly import the otherwise hidden getSyscallArg using Process::getSyscallArg; void setSyscallArg(ThreadContext *tc, int i, X86ISA::IntReg val); + void clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *process, TheISA::IntReg flags); }; class I386Process : public X86Process @@ -134,6 +138,8 @@ X86ISA::IntReg getSyscallArg(ThreadContext *tc, int &i); X86ISA::IntReg getSyscallArg(ThreadContext *tc, int &i, int width); void setSyscallArg(ThreadContext *tc, int i, X86ISA::IntReg val); + void clone(ThreadContext *old_tc, ThreadContext *new_tc, + Process *process, TheISA::IntReg flags); }; /** diff --git a/src/arch/x86/linux/process.hh b/src/arch/x86/linux/process.hh --- a/src/arch/x86/linux/process.hh +++ b/src/arch/x86/linux/process.hh @@ -42,8 +42,11 @@ #include "arch/x86/linux/linux.hh" #include "arch/x86/process.hh" +#include "cpu/thread_context.hh" #include "sim/process.hh" +struct ProcessParams; + namespace X86ISA { class X86_64LinuxProcess : public X86_64Process @@ -51,6 +54,8 @@ public: /// Constructor. X86_64LinuxProcess(ProcessParams * params, ObjectFile *objFile); + void clone(ThreadContext *old_tc, ThreadContext *new_tc, Process *process, + TheISA::IntReg flags); }; class I386LinuxProcess : public I386Process @@ -58,6 +63,8 @@ public: /// Constructor. I386LinuxProcess(ProcessParams * params, ObjectFile *objFile); + void clone(ThreadContext *old_tc, ThreadContext *new_tc, Process *process, + TheISA::IntReg flags); }; } // namespace X86ISA diff --git a/src/arch/sparc/process.hh b/src/arch/sparc/process.hh --- a/src/arch/sparc/process.hh +++ b/src/arch/sparc/process.hh @@ -79,10 +79,10 @@ { // Set up stack. On SPARC Linux, stack goes from the top of memory // downward, less the hole for the kernel address space. - stack_base = (Addr)0xf0000000ULL; + memState->stack_base = (Addr)0xf0000000ULL; // Set up region for mmaps. - mmap_end = 0x70000000; + memState->mmap_end = 0x70000000; } void initState(); @@ -109,10 +109,10 @@ { // Set up stack. On SPARC Linux, stack goes from the top of memory // downward, less the hole for the kernel address space. - stack_base = (Addr)0x80000000000ULL; + memState->stack_base = (Addr)0x80000000000ULL; // Set up region for mmaps. - mmap_end = 0xfffff80000000000ULL; + memState->mmap_end = 0xfffff80000000000ULL; } void initState(); diff --git a/src/arch/sparc/process.cc b/src/arch/sparc/process.cc --- a/src/arch/sparc/process.cc +++ b/src/arch/sparc/process.cc @@ -59,11 +59,13 @@ { // XXX all the below need to be updated for SPARC - Ali - brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); - brk_point = roundUp(brk_point, PageBytes); + memState->brk_point = objFile->dataBase() + objFile->dataSize() + + objFile->bssSize(); + memState->brk_point = roundUp(memState->brk_point, PageBytes); // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // Initialize these to 0s fillStart = 0; @@ -321,15 +323,16 @@ aux_padding + frame_size; - stack_min = stack_base - space_needed; - stack_min = roundDown(stack_min, align); - stack_size = stack_base - stack_min; + memState->stack_min = memState->stack_base - space_needed; + memState->stack_min = roundDown(memState->stack_min, align); + memState->stack_size = memState->stack_base - memState->stack_min; // Allocate space for the stack - allocateMem(roundDown(stack_min, pageSize), roundUp(stack_size, pageSize)); + allocateMem(roundDown(memState->stack_min, pageSize), + roundUp(memState->stack_size, pageSize)); // map out initial stack contents - IntType sentry_base = stack_base - sentry_size; + IntType sentry_base = memState->stack_base - sentry_size; IntType file_name_base = sentry_base - file_name_size; IntType env_data_base = file_name_base - env_data_size; IntType arg_data_base = env_data_base - arg_data_size; @@ -353,9 +356,9 @@ DPRINTF(Stack, "%#x - argv array\n", argv_array_base); DPRINTF(Stack, "%#x - argc \n", argc_base); DPRINTF(Stack, "%#x - window save\n", window_save_base); - DPRINTF(Stack, "%#x - stack min\n", stack_min); + DPRINTF(Stack, "%#x - stack min\n", memState->stack_min); - assert(window_save_base == stack_min); + assert(window_save_base == memState->stack_min); // write contents to stack @@ -394,7 +397,7 @@ // Set up space for the trap handlers into the processes address space. // Since the stack grows down and there is reserved address space abov // it, we can put stuff above it and stay out of the way. - fillStart = stack_base; + fillStart = memState->stack_base; spillStart = fillStart + sizeof(MachInst) * numFillInsts; ThreadContext *tc = system->getThreadContext(contextIds[0]); @@ -402,7 +405,7 @@ // assert(NumArgumentRegs >= 2); // tc->setIntReg(ArgumentReg[0], argc); // tc->setIntReg(ArgumentReg[1], argv_array_base); - tc->setIntReg(StackPointerReg, stack_min - StackBias); + tc->setIntReg(StackPointerReg, memState->stack_min - StackBias); // %g1 is a pointer to a function that should be run at exit. Since we // don't have anything like that, it should be set to 0. @@ -411,7 +414,7 @@ tc->pcState(getStartPC()); // Align the "stack_min" to a page boundary. - stack_min = roundDown(stack_min, pageSize); + memState->stack_min = roundDown(memState->stack_min, pageSize); } void diff --git a/src/arch/sparc/linux/syscalls.cc b/src/arch/sparc/linux/syscalls.cc --- a/src/arch/sparc/linux/syscalls.cc +++ b/src/arch/sparc/linux/syscalls.cc @@ -305,7 +305,7 @@ /* 214 */ SyscallDesc("sysinfo", sysinfoFunc), // 32 bit /* 215 */ SyscallDesc("ipc", unimplementedFunc), // 32 bit /* 216 */ SyscallDesc("sigreturn", unimplementedFunc), // 32 bit - /* 217 */ SyscallDesc("clone", cloneFunc), + /* 217 */ SyscallDesc("clone", cloneFunc), /* 218 */ SyscallDesc("ioprio_get", unimplementedFunc), // 32 bit /* 219 */ SyscallDesc("adjtimex", unimplementedFunc), // 32 bit /* 220 */ SyscallDesc("sigprocmask", unimplementedFunc), // 32 bit @@ -611,7 +611,7 @@ /* 214 */ SyscallDesc("sysinfo", sysinfoFunc), /* 215 */ SyscallDesc("ipc", unimplementedFunc), /* 216 */ SyscallDesc("sigreturn", unimplementedFunc), - /* 217 */ SyscallDesc("clone", cloneFunc), + /* 217 */ SyscallDesc("clone", cloneFunc), /* 218 */ SyscallDesc("ioprio_get", unimplementedFunc), /* 219 */ SyscallDesc("adjtimex", unimplementedFunc), /* 220 */ SyscallDesc("sigprocmask", unimplementedFunc), diff --git a/src/arch/mips/process.cc b/src/arch/mips/process.cc --- a/src/arch/mips/process.cc +++ b/src/arch/mips/process.cc @@ -53,17 +53,19 @@ { // Set up stack. On MIPS, stack starts at the top of kuseg // user address space. MIPS stack grows down from here - stack_base = 0x7FFFFFFF; + memState->stack_base = 0x7FFFFFFF; // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // Set up break point (Top of Heap) - brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); - brk_point = roundUp(brk_point, PageBytes); + memState->brk_point = objFile->dataBase() + objFile->dataSize() + + objFile->bssSize(); + memState->brk_point = roundUp(memState->brk_point, PageBytes); // Set up region for mmaps. Start it 1GB above the top of the heap. - mmap_end = brk_point + 0x40000000L; + memState->mmap_end = memState->brk_point + 0x40000000L; } void @@ -140,15 +142,15 @@ env_data_size; // set bottom of stack - stack_min = stack_base - space_needed; + memState->stack_min = memState->stack_base - space_needed; // align it - stack_min = roundDown(stack_min, pageSize); - stack_size = stack_base - stack_min; + memState->stack_min = roundDown(memState->stack_min, pageSize); + memState->stack_size = memState->stack_base - memState->stack_min; // map memory - allocateMem(stack_min, roundUp(stack_size, pageSize)); + allocateMem(memState->stack_min, roundUp(memState->stack_size, pageSize)); // map out initial stack contents - IntType argv_array_base = stack_min + intSize; // room for argc + IntType argv_array_base = memState->stack_min + intSize; // room for argc IntType envp_array_base = argv_array_base + argv_array_size; IntType auxv_array_base = envp_array_base + envp_array_size; IntType arg_data_base = auxv_array_base + auxv_array_size; @@ -159,7 +161,7 @@ argc = htog((IntType)argc); - initVirtMem.writeBlob(stack_min, (uint8_t*)&argc, intSize); + initVirtMem.writeBlob(memState->stack_min, (uint8_t*)&argc, intSize); copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem); @@ -184,7 +186,7 @@ setSyscallArg(tc, 0, argc); setSyscallArg(tc, 1, argv_array_base); - tc->setIntReg(StackPointerReg, stack_min); + tc->setIntReg(StackPointerReg, memState->stack_min); tc->pcState(getStartPC()); } diff --git a/src/arch/power/process.cc b/src/arch/power/process.cc --- a/src/arch/power/process.cc +++ b/src/arch/power/process.cc @@ -51,17 +51,19 @@ PowerProcess::PowerProcess(ProcessParams *params, ObjectFile *objFile) : Process(params, objFile) { - stack_base = 0xbf000000L; + memState->stack_base = 0xbf000000L; // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // Set up break point (Top of Heap) - brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); - brk_point = roundUp(brk_point, PageBytes); + memState->brk_point = objFile->dataBase() + objFile->dataSize() + + objFile->bssSize(); + memState->brk_point = roundUp(memState->brk_point, PageBytes); // Set up region for mmaps. For now, start at bottom of kuseg space. - mmap_end = 0x70000000L; + memState->mmap_end = 0x70000000L; } void @@ -185,15 +187,16 @@ int space_needed = frame_size + aux_padding; - stack_min = stack_base - space_needed; - stack_min = roundDown(stack_min, align); - stack_size = stack_base - stack_min; + memState->stack_min = memState->stack_base - space_needed; + memState->stack_min = roundDown(memState->stack_min, align); + memState->stack_size = memState->stack_base - memState->stack_min; // map memory - allocateMem(roundDown(stack_min, pageSize), roundUp(stack_size, pageSize)); + allocateMem(roundDown(memState->stack_min, pageSize), + roundUp(memState->stack_size, pageSize)); // map out initial stack contents - uint32_t sentry_base = stack_base - sentry_size; + uint32_t sentry_base = memState->stack_base - sentry_size; uint32_t aux_data_base = sentry_base - aux_data_size; uint32_t env_data_base = aux_data_base - env_data_size; uint32_t arg_data_base = env_data_base - arg_data_size; @@ -212,7 +215,7 @@ DPRINTF(Stack, "0x%x - envp array\n", envp_array_base); DPRINTF(Stack, "0x%x - argv array\n", argv_array_base); DPRINTF(Stack, "0x%x - argc \n", argc_base); - DPRINTF(Stack, "0x%x - stack min\n", stack_min); + DPRINTF(Stack, "0x%x - stack min\n", memState->stack_min); // write contents to stack @@ -257,12 +260,12 @@ ThreadContext *tc = system->getThreadContext(contextIds[0]); //Set the stack pointer register - tc->setIntReg(StackPointerReg, stack_min); + tc->setIntReg(StackPointerReg, memState->stack_min); tc->pcState(getStartPC()); //Align the "stack_min" to a page boundary. - stack_min = roundDown(stack_min, pageSize); + memState->stack_min = roundDown(memState->stack_min, pageSize); } PowerISA::IntReg diff --git a/src/arch/arm/process.cc b/src/arch/arm/process.cc --- a/src/arch/arm/process.cc +++ b/src/arch/arm/process.cc @@ -70,34 +70,38 @@ ObjectFile::Arch _arch) : ArmProcess(params, objFile, _arch) { - stack_base = 0xbf000000L; + memState->stack_base = 0xbf000000L; // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // Set up break point (Top of Heap) - brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); - brk_point = roundUp(brk_point, PageBytes); + memState->brk_point = objFile->dataBase() + objFile->dataSize() + + objFile->bssSize(); + memState->brk_point = roundUp(memState->brk_point, PageBytes); // Set up region for mmaps. For now, start at bottom of kuseg space. - mmap_end = 0x40000000L; + memState->mmap_end = 0x40000000L; } ArmProcess64::ArmProcess64(ProcessParams *params, ObjectFile *objFile, ObjectFile::Arch _arch) : ArmProcess(params, objFile, _arch) { - stack_base = 0x7fffff0000L; + memState->stack_base = 0x7fffff0000L; // Set pointer for next thread stack. Reserve 8M for main stack. - next_thread_stack_base = stack_base - (8 * 1024 * 1024); + memState->next_thread_stack_base = memState->stack_base - + (8 * 1024 * 1024); // Set up break point (Top of Heap) - brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); - brk_point = roundUp(brk_point, PageBytes); + memState->brk_point = objFile->dataBase() + objFile->dataSize() + + objFile->bssSize(); + memState->brk_point = roundUp(memState->brk_point, PageBytes); // Set up region for mmaps. For now, start at bottom of kuseg space. - mmap_end = 0x4000000000L; + memState->mmap_end = 0x4000000000L; } void @@ -300,15 +304,16 @@ int space_needed = frame_size + aux_padding; - stack_min = stack_base - space_needed; - stack_min = roundDown(stack_min, align); - stack_size = stack_base - stack_min; + memState->stack_min = memState->stack_base - space_needed; + memState->stack_min = roundDown(memState->stack_min, align); + memState->stack_size = memState->stack_base - memState->stack_min; // map memory - allocateMem(roundDown(stack_min, pageSize), roundUp(stack_size, pageSize)); + allocateMem(roundDown(memState->stack_min, pageSize), + roundUp(memState->stack_size, pageSize)); // map out initial stack contents - IntType sentry_base = stack_base - sentry_size; + IntType sentry_base = memState->stack_base - sentry_size; IntType aux_data_base = sentry_base - aux_data_size; IntType env_data_base = aux_data_base - env_data_size; IntType arg_data_base = env_data_base - arg_data_size; @@ -329,7 +334,7 @@ DPRINTF(Stack, "0x%x - envp array\n", envp_array_base); DPRINTF(Stack, "0x%x - argv array\n", argv_array_base); DPRINTF(Stack, "0x%x - argc \n", argc_base); - DPRINTF(Stack, "0x%x - stack min\n", stack_min); + DPRINTF(Stack, "0x%x - stack min\n", memState->stack_min); // write contents to stack @@ -375,7 +380,7 @@ ThreadContext *tc = system->getThreadContext(contextIds[0]); //Set the stack pointer register - tc->setIntReg(spIndex, stack_min); + tc->setIntReg(spIndex, memState->stack_min); //A pointer to a function to run when the program exits. We'll set this //to zero explicitly to make sure this isn't used. tc->setIntReg(ArgumentReg0, 0); @@ -402,7 +407,7 @@ tc->pcState(pc); //Align the "stack_min" to a page boundary. - stack_min = roundDown(stack_min, pageSize); + memState->stack_min = roundDown(memState->stack_min, pageSize); } ArmISA::IntReg diff --git a/src/arch/generic/types.hh b/src/arch/generic/types.hh --- a/src/arch/generic/types.hh +++ b/src/arch/generic/types.hh @@ -148,6 +148,12 @@ npc(val + sizeof(MachInst)); }; + void + setNPC(Addr val) + { + npc(val); + } + SimplePCState() {} SimplePCState(Addr val) { set(val); } # Node ID 6fea3eedd8914aff42a8606bc077af98ea103823 # Parent 351ac0e2863fff827e2943b42dff72aecf28560b diff --git a/src/arch/arm/linux/process.cc b/src/arch/arm/linux/process.cc --- a/src/arch/arm/linux/process.cc +++ b/src/arch/arm/linux/process.cc @@ -241,7 +241,7 @@ /* 117 */ SyscallDesc("ipc", unimplementedFunc), /* 118 */ SyscallDesc("fsync", unimplementedFunc), /* 119 */ SyscallDesc("sigreturn", unimplementedFunc), - /* 120 */ SyscallDesc("clone", cloneFunc), + /* 120 */ SyscallDesc("clone", cloneFunc), /* 121 */ SyscallDesc("setdomainname", unimplementedFunc), /* 122 */ SyscallDesc("uname", unameFunc32), /* 123 */ SyscallDesc("unused#123", unimplementedFunc),