arch,sim: Use the guest ABI mechanism with pseudo instructions.

Right now, there are only two places which call the pseudoInst function
directly, the ARM KVM CPU and the generic mmapped IPR. These two
callers currently use the generic "PseudoInstABI" which is just a
wrapper around the existing getArgument function.

In the future, this getArgument function will be disolved, and the
PseudoInstABI will be defined for each ABI. Since it currently mimics
the Linux ABI since gem5 can only handle one ABI at a time right now,
this implementation will probably be shared by linux system calls,
except that the pseudo inst implementation will eat return values since
those are returned through other means when the pseudo inst is based on
magic address ranges.

Jira Issue: https://gem5.atlassian.net/browse/GEM5-187

Change-Id: Ied97e4a968795158873e492289a1058c8e4e411b
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/23178
Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu>
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
diff --git a/src/arch/arm/kvm/arm_cpu.cc b/src/arch/arm/kvm/arm_cpu.cc
index 4d6b9a1..0a77ee0 100644
--- a/src/arch/arm/kvm/arm_cpu.cc
+++ b/src/arch/arm/kvm/arm_cpu.cc
@@ -320,8 +320,9 @@
     const uint8_t func((reg_ip >> 8) & 0xFF);
     const uint8_t subfunc(reg_ip & 0xFF);
 
-    DPRINTF(Kvm, "KVM Hypercall: 0x%x/0x%x\n", func, subfunc);
-    const uint64_t ret(PseudoInst::pseudoInst(getContext(0), func, subfunc));
+    DPRINTF(Kvm, "KVM Hypercall: %#x/%#x\n", func, subfunc);
+    const uint64_t ret =
+        PseudoInst::pseudoInst<PseudoInstABI>(getContext(0), func, subfunc);
 
     // Just set the return value using the KVM API instead of messing
     // with the context. We could have used the context, but that
diff --git a/src/arch/generic/mmapped_ipr.cc b/src/arch/generic/mmapped_ipr.cc
index bf6bf99..8bdd180 100644
--- a/src/arch/generic/mmapped_ipr.cc
+++ b/src/arch/generic/mmapped_ipr.cc
@@ -47,7 +47,7 @@
     uint64_t ret;
 
     assert((offset >> 16) == 0);
-    ret = PseudoInst::pseudoInst(xc, func, subfunc);
+    ret = PseudoInst::pseudoInst<PseudoInstABI>(xc, func, subfunc);
     if (pkt->isRead())
         pkt->set(ret, TheISA::GuestByteOrder);
 }
diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc
index 53892b5..bd2c64e 100644
--- a/src/sim/pseudo_inst.cc
+++ b/src/sim/pseudo_inst.cc
@@ -51,10 +51,6 @@
 #include <string>
 #include <vector>
 
-#include <gem5/asm/generic/m5ops.h>
-
-#include "arch/pseudo_inst.hh"
-#include "arch/utility.hh"
 #include "arch/vtophys.hh"
 #include "base/debug.hh"
 #include "base/output.hh"
@@ -63,7 +59,6 @@
 #include "cpu/quiesce_event.hh"
 #include "cpu/thread_context.hh"
 #include "debug/Loader.hh"
-#include "debug/PseudoInst.hh"
 #include "debug/Quiesce.hh"
 #include "debug/WorkItems.hh"
 #include "dev/net/dist_iface.hh"
@@ -81,11 +76,10 @@
 #include "sim/vptr.hh"
 
 using namespace std;
-
 using namespace Stats;
-using namespace TheISA;
 
-namespace PseudoInst {
+namespace PseudoInst
+{
 
 static inline void
 panicFsOnlyPseudoInst(const char *name)
@@ -93,140 +87,6 @@
     panic("Pseudo inst \"%s\" is only available in Full System mode.");
 }
 
-uint64_t
-pseudoInst(ThreadContext *tc, uint8_t func, uint8_t subfunc)
-{
-    uint64_t args[4];
-
-    DPRINTF(PseudoInst, "PseudoInst::pseudoInst(%i, %i)\n", func, subfunc);
-
-    // We need to do this in a slightly convoluted way since
-    // getArgument() might have side-effects on arg_num. We could have
-    // used the Argument class, but due to the possible side effects
-    // from getArgument, it'd most likely break.
-    int arg_num(0);
-    for (int i = 0; i < sizeof(args) / sizeof(*args); ++i) {
-        args[arg_num] = getArgument(tc, arg_num, sizeof(uint64_t), false);
-        ++arg_num;
-    }
-
-    switch (func) {
-      case M5OP_ARM:
-        arm(tc);
-        break;
-
-      case M5OP_QUIESCE:
-        quiesce(tc);
-        break;
-
-      case M5OP_QUIESCE_NS:
-        quiesceNs(tc, args[0]);
-        break;
-
-      case M5OP_QUIESCE_CYCLE:
-        quiesceCycles(tc, args[0]);
-        break;
-
-      case M5OP_QUIESCE_TIME:
-        return quiesceTime(tc);
-
-      case M5OP_RPNS:
-        return rpns(tc);
-
-      case M5OP_WAKE_CPU:
-        wakeCPU(tc, args[0]);
-        break;
-
-      case M5OP_EXIT:
-        m5exit(tc, args[0]);
-        break;
-
-      case M5OP_FAIL:
-        m5fail(tc, args[0], args[1]);
-        break;
-
-      case M5OP_INIT_PARAM:
-        return initParam(tc, args[0], args[1]);
-
-      case M5OP_LOAD_SYMBOL:
-        loadsymbol(tc);
-        break;
-
-      case M5OP_RESET_STATS:
-        resetstats(tc, args[0], args[1]);
-        break;
-
-      case M5OP_DUMP_STATS:
-        dumpstats(tc, args[0], args[1]);
-        break;
-
-      case M5OP_DUMP_RESET_STATS:
-        dumpresetstats(tc, args[0], args[1]);
-        break;
-
-      case M5OP_CHECKPOINT:
-        m5checkpoint(tc, args[0], args[1]);
-        break;
-
-      case M5OP_WRITE_FILE:
-        return writefile(tc, args[0], args[1], args[2], args[3]);
-
-      case M5OP_READ_FILE:
-        return readfile(tc, args[0], args[1], args[2]);
-
-      case M5OP_DEBUG_BREAK:
-        debugbreak(tc);
-        break;
-
-      case M5OP_SWITCH_CPU:
-        switchcpu(tc);
-        break;
-
-      case M5OP_ADD_SYMBOL:
-        addsymbol(tc, args[0], args[1]);
-        break;
-
-      case M5OP_PANIC:
-        panic("M5 panic instruction called at %s\n", tc->pcState());
-
-      case M5OP_WORK_BEGIN:
-        workbegin(tc, args[0], args[1]);
-        break;
-
-      case M5OP_WORK_END:
-        workend(tc, args[0], args[1]);
-        break;
-
-      case M5OP_ANNOTATE:
-      case M5OP_RESERVED2:
-      case M5OP_RESERVED3:
-      case M5OP_RESERVED4:
-      case M5OP_RESERVED5:
-        warn("Unimplemented m5 op (0x%x)\n", func);
-        break;
-
-      /* SE mode functions */
-      case M5OP_SE_SYSCALL:
-        m5Syscall(tc);
-        break;
-
-      case M5OP_SE_PAGE_FAULT:
-        m5PageFault(tc);
-        break;
-
-      /* dist-gem5 functions */
-      case M5OP_DIST_TOGGLE_SYNC:
-        togglesync(tc);
-        break;
-
-      default:
-        warn("Unhandled m5 op: 0x%x\n", func);
-        break;
-    }
-
-    return 0;
-}
-
 void
 arm(ThreadContext *tc)
 {
diff --git a/src/sim/pseudo_inst.hh b/src/sim/pseudo_inst.hh
index 977ed1d..8685b82 100644
--- a/src/sim/pseudo_inst.hh
+++ b/src/sim/pseudo_inst.hh
@@ -43,25 +43,48 @@
 #ifndef __SIM_PSEUDO_INST_HH__
 #define __SIM_PSEUDO_INST_HH__
 
+#include <gem5/asm/generic/m5ops.h>
+
 class ThreadContext;
 
-//We need the "Tick" and "Addr" data types from here
-#include "base/types.hh"
+#include "arch/pseudo_inst.hh"
+#include "arch/utility.hh"
+#include "base/types.hh" // For Tick and Addr data types.
+#include "debug/PseudoInst.hh"
+#include "sim/guest_abi.hh"
 
-namespace PseudoInst {
+struct PseudoInstABI
+{
+    using Position = int;
+};
 
-/**
- * Execute a decoded M5 pseudo instruction
- *
- * The ISA-specific code is responsible to decode the pseudo inst
- * function number and subfunction number. After that has been done,
- * the rest of the instruction can be implemented in an ISA-agnostic
- * manner using the ISA-specific getArguments functions.
- *
- * @param func M5 pseudo op major function number (see utility/m5/m5ops.h)
- * @param subfunc M5 minor function number. Mainly used for annotations.
- */
-uint64_t pseudoInst(ThreadContext *tc, uint8_t func, uint8_t subfunc);
+namespace GuestABI
+{
+
+template <typename T>
+struct Result<PseudoInstABI, T>
+{
+    static void
+    store(ThreadContext *tc, const T &ret)
+    {
+        // Don't do anything with the pseudo inst results by default.
+    }
+};
+
+template <>
+struct Argument<PseudoInstABI, uint64_t>
+{
+    static uint64_t
+    get(ThreadContext *tc, PseudoInstABI::Position &position)
+    {
+        return TheISA::getArgument(tc, position, sizeof(uint64_t), false);
+    }
+};
+
+} // namespace GuestABI
+
+namespace PseudoInst
+{
 
 void arm(ThreadContext *tc);
 void quiesce(ThreadContext *tc);
@@ -91,6 +114,141 @@
 void m5Syscall(ThreadContext *tc);
 void togglesync(ThreadContext *tc);
 
+/**
+ * Execute a decoded M5 pseudo instruction
+ *
+ * The ISA-specific code is responsible to decode the pseudo inst
+ * function number and subfunction number. After that has been done,
+ * the rest of the instruction can be implemented in an ISA-agnostic
+ * manner using the ISA-specific getArguments functions.
+ *
+ * @param func M5 pseudo op major function number (see utility/m5/m5ops.h)
+ * @param subfunc M5 minor function number. Mainly used for annotations.
+ */
+
+template <typename ABI>
+uint64_t
+pseudoInst(ThreadContext *tc, uint8_t func, uint8_t subfunc)
+{
+    DPRINTF(PseudoInst, "PseudoInst::pseudoInst(%i, %i)\n", func, subfunc);
+
+    switch (func) {
+      case M5OP_ARM:
+        invokeSimcall<ABI>(tc, arm);
+        break;
+
+      case M5OP_QUIESCE:
+        invokeSimcall<ABI>(tc, quiesce);
+        break;
+
+      case M5OP_QUIESCE_NS:
+        invokeSimcall<ABI>(tc, quiesceNs);
+        break;
+
+      case M5OP_QUIESCE_CYCLE:
+        invokeSimcall<ABI>(tc, quiesceCycles);
+        break;
+
+      case M5OP_QUIESCE_TIME:
+        return invokeSimcall<ABI>(tc, quiesceTime);
+
+      case M5OP_RPNS:
+        return invokeSimcall<ABI>(tc, rpns);
+
+      case M5OP_WAKE_CPU:
+        invokeSimcall<ABI>(tc, wakeCPU);
+        break;
+
+      case M5OP_EXIT:
+        invokeSimcall<ABI>(tc, m5exit);
+        break;
+
+      case M5OP_FAIL:
+        invokeSimcall<ABI>(tc, m5fail);
+        break;
+
+      case M5OP_INIT_PARAM:
+        return invokeSimcall<ABI>(tc, initParam);
+
+      case M5OP_LOAD_SYMBOL:
+        invokeSimcall<ABI>(tc, loadsymbol);
+        break;
+
+      case M5OP_RESET_STATS:
+        invokeSimcall<ABI>(tc, resetstats);
+        break;
+
+      case M5OP_DUMP_STATS:
+        invokeSimcall<ABI>(tc, dumpstats);
+        break;
+
+      case M5OP_DUMP_RESET_STATS:
+        invokeSimcall<ABI>(tc, dumpresetstats);
+        break;
+
+      case M5OP_CHECKPOINT:
+        invokeSimcall<ABI>(tc, m5checkpoint);
+        break;
+
+      case M5OP_WRITE_FILE:
+        return invokeSimcall<ABI>(tc, writefile);
+
+      case M5OP_READ_FILE:
+        return invokeSimcall<ABI>(tc, readfile);
+
+      case M5OP_DEBUG_BREAK:
+        invokeSimcall<ABI>(tc, debugbreak);
+        break;
+
+      case M5OP_SWITCH_CPU:
+        invokeSimcall<ABI>(tc, switchcpu);
+        break;
+
+      case M5OP_ADD_SYMBOL:
+        invokeSimcall<ABI>(tc, addsymbol);
+        break;
+
+      case M5OP_PANIC:
+        panic("M5 panic instruction called at %s\n", tc->pcState());
+
+      case M5OP_WORK_BEGIN:
+        invokeSimcall<ABI>(tc, workbegin);
+        break;
+
+      case M5OP_WORK_END:
+        invokeSimcall<ABI>(tc, workend);
+        break;
+
+      case M5OP_ANNOTATE:
+      case M5OP_RESERVED2:
+      case M5OP_RESERVED3:
+      case M5OP_RESERVED4:
+      case M5OP_RESERVED5:
+        warn("Unimplemented m5 op (%#x)\n", func);
+        break;
+
+      /* SE mode functions */
+      case M5OP_SE_SYSCALL:
+        invokeSimcall<ABI>(tc, m5Syscall);
+        break;
+
+      case M5OP_SE_PAGE_FAULT:
+        invokeSimcall<ABI>(tc, TheISA::m5PageFault);
+        break;
+
+      /* dist-gem5 functions */
+      case M5OP_DIST_TOGGLE_SYNC:
+        invokeSimcall<ABI>(tc, togglesync);
+        break;
+
+      default:
+        warn("Unhandled m5 op: %#x\n", func);
+        break;
+    }
+
+    return 0;
+}
+
 } // namespace PseudoInst
 
 #endif // __SIM_PSEUDO_INST_HH__