arch: Use a fault to trigger system calls in SE mode.
When the system call happens during the execution of the system call instruction, it can be ambiguous what state takes precedence, the state update from the instruction or the system call. These may be tracked differently and found in an unpredictable order in, for example, the O3 CPU. An instruction can avoid updating any state explicitly, but implicitly updated state (specifically the PC) will always update, whether the instruction wants it to or not. If the system call can be deferred by using a Fault object, then it's no longer ambiguous. The PC update will be discarded, and the system call can set the PC however it likes. Because there is no implicit PC update, the PC needs to be walked forward, either to what it would have been anyway, or to what the system call set in NPC. In addition, because of the existing semantics around handling Faults, the instruction no longer needs to be marked as serializing, non-speculative, etc. The "normal", aka architectural, aka FS version of the system call instructions don't return a Fault artificially. Change-Id: I72011a16a89332b1dcfb01c79f2f0d75c55ab773 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/33281 Maintainer: Gabe Black <gabeblack@google.com> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
This commit is contained in:
@@ -159,9 +159,12 @@ decode OPCODE_HI default Unknown::unknown() {
|
||||
0x2: movz({{ Rd = (Rt == 0) ? Rs : Rd; }});
|
||||
0x3: movn({{ Rd = (Rt != 0) ? Rs : Rd; }});
|
||||
0x4: decode FullSystemInt {
|
||||
0: syscall_se({{ xc->syscall(); }},
|
||||
IsSerializeAfter, IsNonSpeculative);
|
||||
default: syscall({{ fault = std::make_shared<SystemCallFault>(); }});
|
||||
0: syscall_se({{
|
||||
fault = std::make_shared<SESyscallFault>();
|
||||
}});
|
||||
default: syscall({{
|
||||
fault = std::make_shared<SystemCallFault>();
|
||||
}});
|
||||
}
|
||||
0x7: sync({{ ; }}, IsMemBarrier);
|
||||
0x5: break({{fault = std::make_shared<BreakpointFault>();}});
|
||||
|
||||
@@ -515,8 +515,7 @@ decode OPCODE default Unknown::unknown() {
|
||||
55: stfdu({{ Mem_df = Fs; }});
|
||||
}
|
||||
|
||||
17: IntOp::sc({{ xc->syscall(); }},
|
||||
[ IsSyscall, IsNonSpeculative, IsSerializeAfter ]);
|
||||
17: IntOp::sc({{ return std::make_shared<SESyscallFault>(); }});
|
||||
|
||||
format FloatArithOp {
|
||||
59: decode A_XO {
|
||||
|
||||
@@ -398,9 +398,9 @@
|
||||
// will sign extend it, and there's no easy way to
|
||||
// specify only checking the first byte.
|
||||
0xffffffffffffff80:
|
||||
SyscallInst::int80('xc->syscall()',
|
||||
IsSyscall, IsNonSpeculative,
|
||||
IsSerializeAfter);
|
||||
SyscallInst::int80({{
|
||||
return std::make_shared<SESyscallFault>();
|
||||
}});
|
||||
}
|
||||
|
||||
default: Inst::INT(Ib);
|
||||
|
||||
@@ -237,9 +237,9 @@
|
||||
}
|
||||
}
|
||||
0x05: decode FullSystemInt {
|
||||
0: SyscallInst::syscall('xc->syscall()',
|
||||
IsSyscall, IsNonSpeculative,
|
||||
IsSerializeAfter);
|
||||
0: SyscallInst::syscall({{
|
||||
return std::make_shared<SESyscallFault>();
|
||||
}});
|
||||
default: decode MODE_MODE {
|
||||
0x0: decode MODE_SUBMODE {
|
||||
0x0: Inst::SYSCALL_64();
|
||||
@@ -431,9 +431,9 @@
|
||||
0x2: Inst::RDMSR();
|
||||
0x3: rdpmc();
|
||||
0x4: decode FullSystemInt {
|
||||
0: SyscallInst::sysenter('xc->syscall()',
|
||||
IsSyscall, IsNonSpeculative,
|
||||
IsSerializeAfter);
|
||||
0: SyscallInst::sysenter({{
|
||||
return std::make_shared<SESyscallFault>();
|
||||
}});
|
||||
default: sysenter();
|
||||
}
|
||||
0x5: sysexit();
|
||||
|
||||
@@ -50,6 +50,16 @@ UnimpFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
panic("Unimpfault: %s", panicStr.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SESyscallFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
{
|
||||
tc->syscall();
|
||||
// Move the PC forward since that doesn't happen automatically.
|
||||
TheISA::PCState pc = tc->pcState();
|
||||
inst->advancePC(pc);
|
||||
tc->pcState(pc);
|
||||
}
|
||||
|
||||
void
|
||||
ReExec::invoke(ThreadContext *tc, const StaticInstPtr &inst)
|
||||
{
|
||||
|
||||
@@ -64,6 +64,15 @@ class UnimpFault : public FaultBase
|
||||
StaticInst::nullStaticInstPtr) override;
|
||||
};
|
||||
|
||||
// A fault to trigger a system call in SE mode.
|
||||
class SESyscallFault : public FaultBase
|
||||
{
|
||||
const char *name() const override { return "syscall_fault"; }
|
||||
|
||||
void invoke(ThreadContext *tc, const StaticInstPtr &inst=
|
||||
StaticInst::nullStaticInstPtr) override;
|
||||
};
|
||||
|
||||
class ReExec : public FaultBase
|
||||
{
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user