summaryrefslogtreecommitdiff
path: root/src/sim
diff options
context:
space:
mode:
Diffstat (limited to 'src/sim')
-rw-r--r--src/sim/Process.py2
-rw-r--r--src/sim/fd_array.cc7
-rw-r--r--src/sim/fd_array.hh8
-rw-r--r--src/sim/process.cc201
-rw-r--r--src/sim/process.hh71
-rw-r--r--src/sim/syscall_desc.hh4
-rw-r--r--src/sim/syscall_emul.cc141
-rw-r--r--src/sim/syscall_emul.hh269
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