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>
diff --git a/src/arch/mips/isa/decoder.isa b/src/arch/mips/isa/decoder.isa
index 6e19f85..d35fc18 100644
--- a/src/arch/mips/isa/decoder.isa
+++ b/src/arch/mips/isa/decoder.isa
@@ -159,9 +159,12 @@
                     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>();}});
diff --git a/src/arch/power/isa/decoder.isa b/src/arch/power/isa/decoder.isa
index 2e88aea..b7b9aff 100644
--- a/src/arch/power/isa/decoder.isa
+++ b/src/arch/power/isa/decoder.isa
@@ -515,8 +515,7 @@
         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 {
diff --git a/src/arch/x86/isa/decoder/one_byte_opcodes.isa b/src/arch/x86/isa/decoder/one_byte_opcodes.isa
index b1b6218..b5f77cd 100644
--- a/src/arch/x86/isa/decoder/one_byte_opcodes.isa
+++ b/src/arch/x86/isa/decoder/one_byte_opcodes.isa
@@ -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);
diff --git a/src/arch/x86/isa/decoder/two_byte_opcodes.isa b/src/arch/x86/isa/decoder/two_byte_opcodes.isa
index 0dec25b..5d45144 100644
--- a/src/arch/x86/isa/decoder/two_byte_opcodes.isa
+++ b/src/arch/x86/isa/decoder/two_byte_opcodes.isa
@@ -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();
diff --git a/src/sim/faults.cc b/src/sim/faults.cc
index 9bbc119..b6468ea 100644
--- a/src/sim/faults.cc
+++ b/src/sim/faults.cc
@@ -51,6 +51,16 @@
 }
 
 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)
 {
     tc->pcState(tc->pcState());
diff --git a/src/sim/faults.hh b/src/sim/faults.hh
index 4b5c1a5..62817f0 100644
--- a/src/sim/faults.hh
+++ b/src/sim/faults.hh
@@ -64,6 +64,15 @@
                 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: