exitSimLoop("halt instruction encountered");
}}, IsNonSpeculative);
0x83: callsys({{
- xc->syscall(R0);
+ xc->syscall(R0, &fault);
}}, IsSerializeAfter, IsNonSpeculative, IsSyscall);
// Read uniq reg into ABI return value register (r0)
0x9e: rduniq({{ R0 = Runiq; }}, IsIprAccess);
callNum = tc->readIntReg(INTREG_X8);
else
callNum = tc->readIntReg(INTREG_R7);
- tc->syscall(callNum);
+ Fault fault;
+ tc->syscall(callNum, &fault);
// Advance the PC since that won't happen automatically.
PCState pc = tc->pcState();
0x2: movz({{ Rd = (Rt == 0) ? Rs : Rd; }});
0x3: movn({{ Rd = (Rt != 0) ? Rs : Rd; }});
0x4: decode FullSystemInt {
- 0: syscall_se({{ xc->syscall(R2); }},
+ 0: syscall_se({{ xc->syscall(R2, &fault); }},
IsSerializeAfter, IsNonSpeculative);
default: syscall({{ fault = std::make_shared<SystemCallFault>(); }});
}
55: stfdu({{ Mem_df = Fs; }});
}
- 17: IntOp::sc({{ xc->syscall(R0); }},
+ 17: IntOp::sc({{ xc->syscall(R0, &fault); }},
[ IsSyscall, IsNonSpeculative, IsSerializeAfter ]);
format FloatArithOp {
void
SyscallFault::invoke_se(ThreadContext *tc, const StaticInstPtr &inst)
{
- tc->syscall(tc->readIntReg(SyscallNumReg));
+ Fault *fault = NoFault;
+ tc->syscall(tc->readIntReg(SyscallNumReg), fault);
}
SparcProcess *sp = dynamic_cast<SparcProcess *>(p);
assert(sp);
- sp->handleTrap(_n, tc);
+ Fault fault;
+ sp->handleTrap(_n, tc, &fault);
// We need to explicitly advance the pc, since that's not done for us
// on a faulting instruction
: Sparc32Process(params, objFile)
{}
-void Sparc32LinuxProcess::handleTrap(int trapNum, ThreadContext *tc)
+void Sparc32LinuxProcess::handleTrap(int trapNum, ThreadContext *tc,
+ Fault *fault)
{
switch (trapNum) {
case 0x10: //Linux 32 bit syscall trap
- tc->syscall(tc->readIntReg(1));
+ tc->syscall(tc->readIntReg(1), fault);
break;
default:
- SparcProcess::handleTrap(trapNum, tc);
+ SparcProcess::handleTrap(trapNum, tc, fault);
}
}
: Sparc64Process(params, objFile)
{}
-void Sparc64LinuxProcess::handleTrap(int trapNum, ThreadContext *tc)
+void Sparc64LinuxProcess::handleTrap(int trapNum, ThreadContext *tc,
+ Fault *fault)
{
switch (trapNum) {
// case 0x10: // Linux 32 bit syscall trap
case 0x6d: // Linux 64 bit syscall trap
- tc->syscall(tc->readIntReg(1));
+ tc->syscall(tc->readIntReg(1), fault);
break;
default:
- SparcProcess::handleTrap(trapNum, tc);
+ SparcProcess::handleTrap(trapNum, tc, fault);
}
}
return SparcLinuxProcess::getDesc32(callnum);
}
- void handleTrap(int trapNum, ThreadContext *tc);
+ void handleTrap(int trapNum, ThreadContext *tc, Fault *fault);
};
/// A process with emulated 32 bit SPARC/Linux syscalls.
return SparcLinuxProcess::getDesc(callnum);
}
- void handleTrap(int trapNum, ThreadContext *tc);
+ void handleTrap(int trapNum, ThreadContext *tc, Fault *fault);
};
SyscallReturn getresuidFunc(SyscallDesc *desc, int num,
}
void
-SparcProcess::handleTrap(int trapNum, ThreadContext *tc)
+SparcProcess::handleTrap(int trapNum, ThreadContext *tc, Fault *fault)
{
PCState pc = tc->pcState();
switch (trapNum) {
public:
// Handles traps which request services from the operating system
- virtual void handleTrap(int trapNum, ThreadContext *tc);
+ virtual void handleTrap(int trapNum, ThreadContext *tc, Fault *fault);
Addr readFillStart() { return fillStart; }
Addr readSpillStart() { return spillStart; }
// will sign extend it, and there's no easy way to
// specify only checking the first byte.
0xffffffffffffff80:
- SyscallInst::int80('xc->syscall(Rax)',
- IsSyscall, IsNonSpeculative, IsSerializeAfter);
+ SyscallInst::int80('xc->syscall(Rax, &fault)',
+ IsSyscall, IsNonSpeculative,
+ IsSerializeAfter);
}
default: Inst::INT(Ib);
}
}
0x05: decode FullSystemInt {
- 0: SyscallInst::syscall('xc->syscall(Rax)',
- IsSyscall, IsNonSpeculative, IsSerializeAfter);
+ 0: SyscallInst::syscall('xc->syscall(Rax, &fault)',
+ IsSyscall, IsNonSpeculative,
+ IsSerializeAfter);
default: decode MODE_MODE {
0x0: decode MODE_SUBMODE {
0x0: Inst::SYSCALL_64();
0x2: Inst::RDMSR();
0x3: rdpmc();
0x4: decode FullSystemInt {
- 0: SyscallInst::sysenter('xc->syscall(Rax)',
- IsSyscall, IsNonSpeculative, IsSerializeAfter);
+ 0: SyscallInst::sysenter('xc->syscall(Rax, &fault)',
+ IsSyscall, IsNonSpeculative,
+ IsSerializeAfter);
default: sysenter();
}
0x5: sysexit();
}
void
-I386Process::syscall(int64_t callnum, ThreadContext *tc)
+I386Process::syscall(int64_t callnum, ThreadContext *tc, Fault *fault)
{
TheISA::PCState pc = tc->pcState();
Addr eip = pc.pc();
pc.npc(vsyscallPage.base + vsyscallPage.vsysexitOffset);
tc->pcState(pc);
}
- X86Process::syscall(callnum, tc);
+ X86Process::syscall(callnum, tc, fault);
}
void argsInit(int intSize, int pageSize);
void initState();
- void syscall(int64_t callnum, ThreadContext *tc);
+ void syscall(int64_t callnum, ThreadContext *tc, Fault *fault);
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);
{
DPRINTF(PseudoInst, "PseudoInst::m5Syscall()\n");
- tc->syscall(tc->readIntReg(INTREG_RAX));
+ Fault fault;
+ tc->syscall(tc->readIntReg(INTREG_RAX), &fault);
+
MiscReg rflags = tc->readMiscReg(MISCREG_RFLAGS);
rflags &= ~(1 << 16);
tc->setMiscReg(MISCREG_RFLAGS, rflags);
checker = Param.BaseCPU(NULL, "checker CPU")
+ syscallRetryLatency = Param.Cycles(10000, "Cycles to wait until retry")
+
do_checkpoint_insts = Param.Bool(True,
"enable checkpoint pseudo instructions")
do_statistics_insts = Param.Bool(True,
numThreads(p->numThreads), system(p->system),
functionTraceStream(nullptr), currentFunctionStart(0),
currentFunctionEnd(0), functionEntryTick(0),
- addressMonitor(p->numThreads)
+ addressMonitor(p->numThreads),
+ syscallRetryLatency(p->syscallRetryLatency)
{
// if Python did not provide a valid ID, do it here
if (_cpuId == -1 ) {
assert(tid < numThreads);
return &addressMonitor[tid];
}
+
+ Cycles syscallRetryLatency;
};
#endif // THE_ISA == NULL_ISA
void wakeup(ThreadID tid) override { }
// Assume that the normal CPU's call to syscall was successful.
// The checker's state would have already been updated by the syscall.
- void syscall(int64_t callnum) override { }
+ void syscall(int64_t callnum, Fault *fault) override { }
void handleError()
{
SETranslatingPortProxy &getMemProxy() { return actualTC->getMemProxy(); }
/** Executes a syscall in SE mode. */
- void syscall(int64_t callnum)
- { return actualTC->syscall(callnum); }
+ void syscall(int64_t callnum, Fault *fault)
+ { return actualTC->syscall(callnum, fault); }
Status status() const { return actualTC->status(); }
/**
* Executes a syscall specified by the callnum.
*/
- virtual void syscall(int64_t callnum) = 0;
+ virtual void syscall(int64_t callnum, Fault *fault) = 0;
/** @} */
}
void
- syscall(int64_t callnum) override
+ syscall(int64_t callnum, Fault *fault) override
{
if (FullSystem)
panic("Syscall emulation isn't available in FS mode.\n");
- thread.syscall(callnum);
+ thread.syscall(callnum, fault);
}
ThreadContext *tcBase() override { return thread.getTC(); }
size_t numROBFreeEntries(ThreadID tid);
/** Generates an event to schedule a squash due to a trap. */
- void generateTrapEvent(ThreadID tid);
+ void generateTrapEvent(ThreadID tid, Fault inst_fault);
/** Records that commit needs to initiate a squash due to an
* external state update through the TC.
template <class Impl>
void
-DefaultCommit<Impl>::generateTrapEvent(ThreadID tid)
+DefaultCommit<Impl>::generateTrapEvent(ThreadID tid, Fault inst_fault)
{
DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid);
TrapEvent *trap = new TrapEvent(this, tid);
- cpu->schedule(trap, cpu->clockEdge(trapLatency));
+ Cycles latency = dynamic_pointer_cast<SyscallRetryFault>(inst_fault) ?
+ cpu->syscallRetryLatency : trapLatency;
+
+ cpu->schedule(trap, cpu->clockEdge(latency));
trapInFlight[tid] = true;
thread[tid]->trapPending = true;
}
commitStatus[0] = TrapPending;
+ interrupt = NoFault;
+
// Generate trap squash event.
- generateTrapEvent(0);
+ generateTrapEvent(0, interrupt);
- interrupt = NoFault;
avoidQuiesceLiveLock = false;
} else {
DPRINTF(Commit, "Interrupt pending: instruction is %sin "
}
// Generate trap squash event.
- generateTrapEvent(tid);
+ generateTrapEvent(tid, inst_fault);
return false;
}
template <class Impl>
void
-FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid)
+FullO3CPU<Impl>::syscall(int64_t callnum, ThreadID tid, Fault *fault)
{
DPRINTF(O3CPU, "[tid:%i] Executing syscall().\n\n", tid);
++(this->thread[tid]->funcExeInst);
// Execute the actual syscall.
- this->thread[tid]->syscall(callnum);
+ this->thread[tid]->syscall(callnum, fault);
// Decrease funcExeInst by one as the normal commit will handle
// incrementing it.
/** Executes a syscall.
* @todo: Determine if this needs to be virtual.
*/
- void syscall(int64_t callnum, ThreadID tid);
+ void syscall(int64_t callnum, ThreadID tid, Fault *fault);
/** Starts draining the CPU's pipeline of all instructions in
* order to stop all memory accesses. */
bool simPalCheck(int palFunc);
/** Emulates a syscall. */
- void syscall(int64_t callnum);
+ void syscall(int64_t callnum, Fault *fault);
public:
template <class Impl>
void
-BaseO3DynInst<Impl>::syscall(int64_t callnum)
+BaseO3DynInst<Impl>::syscall(int64_t callnum, Fault *fault)
{
if (FullSystem)
panic("Syscall emulation isn't available in FS mode.\n");
// changes, update this instruction's nextPC because the syscall
// must have changed the nextPC.
TheISA::PCState curPC = this->cpu->pcState(this->threadNumber);
- this->cpu->syscall(callnum, this->threadNumber);
+ this->cpu->syscall(callnum, this->threadNumber, fault);
TheISA::PCState newPC = this->cpu->pcState(this->threadNumber);
if (!(curPC == newPC)) {
this->pcState(newPC);
{ thread->storeCondFailures = sc_failures; }
/** Executes a syscall in SE mode. */
- virtual void syscall(int64_t callnum)
- { return cpu->syscall(callnum, thread->threadId()); }
+ virtual void syscall(int64_t callnum, Fault *fault)
+ { return cpu->syscall(callnum, thread->threadId(), fault); }
/** Reads the funcExeInst counter. */
virtual Counter readFuncExeInst() { return thread->funcExeInst; }
ThreadContext *getTC() { return tc; }
/** Handles the syscall. */
- void syscall(int64_t callnum) { process->syscall(callnum, tc); }
+ void syscall(int64_t callnum, Fault *fault)
+ {
+ process->syscall(callnum, tc, fault);
+ }
void dumpFuncProfile()
{
preExecute();
+ Tick stall_ticks = 0;
if (curStaticInst) {
fault = curStaticInst->execute(&t_info, traceData);
traceData = NULL;
}
+ if (dynamic_pointer_cast<SyscallRetryFault>(fault)) {
+ // Retry execution of system calls after a delay.
+ // Prevents immediate re-execution since conditions which
+ // caused the retry are unlikely to change every tick.
+ stall_ticks += clockEdge(syscallRetryLatency) - curTick();
+ }
+
postExecute();
}
curStaticInst->isFirstMicroop()))
instCnt++;
- Tick stall_ticks = 0;
if (simulate_inst_stalls && icache_access)
stall_ticks += icache_latency;
/**
* Executes a syscall specified by the callnum.
*/
- void syscall(int64_t callnum) override
+ void syscall(int64_t callnum, Fault *fault) override
{
if (FullSystem)
panic("Syscall emulation isn't available in FS mode.");
- thread->syscall(callnum);
+ thread->syscall(callnum, fault);
}
/** Returns a pointer to the ThreadContext. */
return;
if (fault != NoFault) {
- advancePC(fault);
DPRINTF(SimpleCPU, "Fault occured, scheduling fetch event\n");
- reschedule(fetchEvent, clockEdge(), true);
+
+ advancePC(fault);
+
+ Tick stall = dynamic_pointer_cast<SyscallRetryFault>(fault) ?
+ clockEdge(syscallRetryLatency) : clockEdge();
+
+ reschedule(fetchEvent, stall, true);
+
_status = Faulting;
return;
}
void setStCondFailures(unsigned sc_failures)
{ storeCondFailures = sc_failures; }
- void syscall(int64_t callnum)
+ void syscall(int64_t callnum, Fault *fault)
{
- process->syscall(callnum, tc);
+ process->syscall(callnum, tc, fault);
}
uint64_t readIntRegFlat(int idx) { return intRegs[idx]; }
// Same with st cond failures.
virtual Counter readFuncExeInst() = 0;
- virtual void syscall(int64_t callnum) = 0;
+ virtual void syscall(int64_t callnum, Fault *fault) = 0;
// This function exits the thread context in the CPU and returns
// 1 if the CPU has no more active threads (meaning it's OK to exit);
void setStCondFailures(unsigned sc_failures)
{ actualTC->setStCondFailures(sc_failures); }
- void syscall(int64_t callnum)
- { actualTC->syscall(callnum); }
+ void syscall(int64_t callnum, Fault *fault)
+ { actualTC->syscall(callnum, fault); }
Counter readFuncExeInst() { return actualTC->readFuncExeInst(); }
tc->pcState(tc->pcState());
}
+void SyscallRetryFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
+{
+ tc->pcState(tc->pcState());
+}
+
void GenericPageTableFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
bool handled = false;
StaticInst::nullStaticInstPtr);
};
+/*
+ * This class is needed to allow system call retries to occur for blocking
+ * system calls in SE mode. A retry fault will be generated by the system call
+ * emulation code if blocking conditions arise; the fault is passed up the
+ * function call chain into the CPU model where it is handled by retrying the
+ * syscall instruction on a later tick.
+ */
+class SyscallRetryFault : public FaultBase
+{
+ public:
+ virtual FaultName name() const { return "System call retry fault"; }
+ SyscallRetryFault() {}
+ void invoke(ThreadContext *tc, const StaticInstPtr &inst =
+ StaticInst::nullStaticInstPtr);
+};
+
class GenericPageTableFault : public FaultBase
{
private:
}
void
-Process::syscall(int64_t callnum, ThreadContext *tc)
+Process::syscall(int64_t callnum, ThreadContext *tc, Fault *fault)
{
num_syscalls++;
if (desc == NULL)
fatal("Syscall %d out of range", callnum);
- desc->doSyscall(callnum, this, tc);
+ desc->doSyscall(callnum, this, tc, fault);
}
IntReg
void initState() override;
DrainState drain() override;
- void syscall(int64_t callnum, ThreadContext *tc);
+ void syscall(int64_t callnum, ThreadContext *tc, Fault *fault);
virtual TheISA::IntReg getSyscallArg(ThreadContext *tc, int &i) = 0;
virtual TheISA::IntReg getSyscallArg(ThreadContext *tc, int &i, int width);
virtual void setSyscallArg(ThreadContext *tc, int i,
#include "sim/syscall_desc.hh"
+#include <memory>
+
#include "base/trace.hh"
+#include "base/types.hh"
#include "config/the_isa.hh"
#include "cpu/base.hh"
#include "cpu/thread_context.hh"
+#include "sim/faults.hh"
#include "sim/process.hh"
#include "sim/syscall_debug_macros.hh"
#include "sim/syscall_return.hh"
void
-SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc)
+SyscallDesc::doSyscall(int callnum, Process *process, ThreadContext *tc,
+ Fault *fault)
{
TheISA::IntReg arg[6] M5_VAR_USED;
* blocking behavior, warn that the system call will retry;
* alternatively, print the return value.
*/
- if (retval.needsRetry())
+ if (retval.needsRetry()) {
+ *fault = std::make_shared<SyscallRetryFault>();
DPRINTF_SYSCALL(Base, "%s needs retry\n", _name);
- else
+ } else
DPRINTF_SYSCALL(Base, "%s returns %d\n", _name, retval.encodedValue());
if (!(_flags & SyscallDesc::SuppressReturnValue) && !retval.needsRetry())
#include <string>
+#include "base/types.hh"
+
class Process;
class SyscallReturn;
class ThreadContext;
* @param proc Handle for the owning Process to pass information
* @param tc Handle for owning ThreadContext to pass information
*/
- void doSyscall(int callnum, Process *proc, ThreadContext *tc);
+ void doSyscall(int callnum, Process *proc, ThreadContext *tc,
+ Fault *fault);
/**
* Return false if WarnOnce is set and a warning has already been issued.