arm: Add a gem5 specific pseudo op semihosting call.
authorGabe Black <gabeblack@google.com>
Wed, 26 Feb 2020 23:49:39 +0000 (15:49 -0800)
committerGabe Black <gabeblack@google.com>
Fri, 27 Mar 2020 21:33:03 +0000 (21:33 +0000)
This is in the range of call numbers set aside for extensions. When
called, it will extract the function to use from the first argument
slot. Then it calls the pseudoInst dispatching function using an ABI
which drops the return value (which is handled by semihosting itself)
and which extracts arguments from the remaining slots in the param
structure.

This makes gem5 pseudo ops available on CPU models which support
semihosting but not instruction based or address based "magic"
operations, aka hypercalls. This includes the fast model CPUs.

Change-Id: Ic4817f2b1e6aad7784af77a1a494cf614d4d4c6c
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/25950
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/arch/arm/semihosting.cc
src/arch/arm/semihosting.hh

index 230ded1b2240ab60d524421fed485aac31346182..7b7592f921c9a37ed730ae8c00e3b5dc47557411 100644 (file)
@@ -48,6 +48,7 @@
 #include "mem/secure_port_proxy.hh"
 #include "params/ArmSemihosting.hh"
 #include "sim/byteswap.hh"
+#include "sim/pseudo_inst.hh"
 #include "sim/sim_exit.hh"
 #include "sim/system.hh"
 
@@ -83,6 +84,9 @@ const std::map<uint32_t, ArmSemihosting::SemiCall> ArmSemihosting::calls{
     { SYS_ELAPSED,  { "SYS_ELAPSED", &ArmSemihosting::callElapsed32,
                                      &ArmSemihosting::callElapsed64 } },
     { SYS_TICKFREQ, { "SYS_TICKFREQ", &ArmSemihosting::callTickFreq } },
+    { SYS_GEM5_PSEUDO_OP,
+        { "SYS_GEM5_PSEUDO_OP", &ArmSemihosting::callGem5PseudoOp32,
+                                &ArmSemihosting::callGem5PseudoOp64 } },
 };
 
 const std::vector<const char *> ArmSemihosting::fmodes{
@@ -653,6 +657,87 @@ ArmSemihosting::callTickFreq(ThreadContext *tc)
     return retOK(semiTick(SimClock::Frequency));
 }
 
+
+struct SemiPseudoAbi32 : public ArmSemihosting::Abi32
+{
+    class State : public ArmSemihosting::Abi32::State
+    {
+      public:
+        State(const ThreadContext *tc) : ArmSemihosting::Abi32::State(tc)
+        {
+            // Use getAddr() to skip the func number in the first slot.
+            getAddr();
+        }
+    };
+};
+
+struct SemiPseudoAbi64 : public ArmSemihosting::Abi64
+{
+    class State : public ArmSemihosting::Abi64::State
+    {
+      public:
+        State(const ThreadContext *tc) : ArmSemihosting::Abi64::State(tc)
+        {
+            // Use getAddr() to skip the func number in the first slot.
+            getAddr();
+        }
+    };
+};
+
+namespace GuestABI
+{
+
+// Ignore return values since those will be handled by semihosting.
+template <typename T>
+struct Result<SemiPseudoAbi32, T>
+{
+    static void store(ThreadContext *tc, const T &ret) {}
+};
+template <typename T>
+struct Result<SemiPseudoAbi64, T>
+{
+    static void store(ThreadContext *tc, const T &ret) {}
+};
+
+// Handle arguments the same as for semihosting operations. Skipping the first
+// slot is handled internally by the State type.
+template <typename T>
+struct Argument<SemiPseudoAbi32, T> :
+    public Argument<ArmSemihosting::Abi32, T>
+{};
+template <typename T>
+struct Argument<SemiPseudoAbi64, T> :
+    public Argument<ArmSemihosting::Abi64, T>
+{};
+
+} // namespace GuestABI
+
+ArmSemihosting::RetErrno
+ArmSemihosting::callGem5PseudoOp32(ThreadContext *tc, uint32_t encoded_func)
+{
+    uint8_t func;
+    PseudoInst::decodeAddrOffset(encoded_func, func);
+
+    uint64_t ret;
+    if (PseudoInst::pseudoInst<SemiPseudoAbi32>(tc, func, ret))
+        return retOK(ret);
+    else
+        return retError(EINVAL);
+}
+
+ArmSemihosting::RetErrno
+ArmSemihosting::callGem5PseudoOp64(ThreadContext *tc, uint64_t encoded_func)
+{
+    uint8_t func;
+    PseudoInst::decodeAddrOffset(encoded_func, func);
+
+    uint64_t ret;
+    if (PseudoInst::pseudoInst<SemiPseudoAbi64>(tc, func, ret))
+        return retOK(ret);
+    else
+        return retError(EINVAL);
+}
+
 FILE *
 ArmSemihosting::getSTDIO(const char *stream_name,
                          const std::string &name, const char *mode)
index 58843496c0fa976d334c2e2ee2da974cf5e2eec5..e255d201b058338d9bf02155525e7c3b1f85e7a8 100644 (file)
@@ -208,7 +208,9 @@ class ArmSemihosting : public SimObject
         SYS_ELAPSED = 0x30,
         SYS_TICKFREQ = 0x31,
 
-        MaxStandardOp = 0xFF
+        MaxStandardOp = 0xFF,
+
+        SYS_GEM5_PSEUDO_OP = 0x100
     };
 
     ArmSemihosting(const ArmSemihostingParams *p);
@@ -547,6 +549,9 @@ class ArmSemihosting : public SimObject
     RetErrno callElapsed64(ThreadContext *tc, InPlaceArg ticks);
     RetErrno callTickFreq(ThreadContext *tc);
 
+    RetErrno callGem5PseudoOp32(ThreadContext *tc, uint32_t encoded_func);
+    RetErrno callGem5PseudoOp64(ThreadContext *tc, uint64_t encoded_func);
+
     template <typename Abi>
     void
     unrecognizedCall(ThreadContext *tc, const char *format, uint64_t op)