arch-arm: Add support for semihosting STDIO redirection
authorAndreas Sandberg <andreas.sandberg@arm.com>
Wed, 21 Feb 2018 12:07:01 +0000 (12:07 +0000)
committerAndreas Sandberg <andreas.sandberg@arm.com>
Wed, 16 May 2018 17:20:27 +0000 (17:20 +0000)
The Arm Semihosting layer currently assumes that the guest application
shares STDIO with gem5. This makes it hard to distinguish application
output from gem5's output and makes it impossible to redirect STDIN
when running in interactive mode. Add support for custom STDIO
redirection when instantiating the Semihosting model.

Change-Id: I3411a6b9bfb008ffc3087d8837f59be72bd1e8ae
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Anouk Van Laer <anouk.vanlaer@arm.com>
Reviewed-by: Jack Travaglini <giacomo.travaglini@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/10021
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>

src/arch/arm/ArmSemihosting.py
src/arch/arm/semihosting.cc
src/arch/arm/semihosting.hh

index 1da4c4988efb4fd45e8f94ff9156a1b403af4f6d..7846499140e68f2ccd81d37e245eea8c7a9eef0d 100644 (file)
@@ -46,6 +46,12 @@ class ArmSemihosting(SimObject):
     cxx_header = "arch/arm/semihosting.hh"
 
     cmd_line = Param.String("", "Command line to report to guest");
+    stdin = Param.String("stdin",
+                         "Standard input (stdin for gem5's terminal)")
+    stdout = Param.String("stdout",
+                          "Standard output (stdout for gem5's terminal)")
+    stderr = Param.String("stderr",
+                          "Standard error (stderr for gem5's terminal)")
 
     mem_reserve = Param.MemorySize("32MB",
         "Amount of memory to reserve at the start of the address map. This "
index 89e1b2e7d4c7acb349ab9e3fb3a7162660401de4..46d964d63c32b18a4fe488afe99059505432fe44 100644 (file)
@@ -121,6 +121,15 @@ const std::vector<uint8_t> ArmSemihosting::features{
     0x3,                    // EXT_EXIT_EXTENDED, EXT_STDOUT_STDERR
 };
 
+const std::map<const std::string, FILE *> ArmSemihosting::stdioMap{
+    {"cin",    ::stdin},
+    {"stdin",  ::stdin},
+    {"cout",   ::stdout},
+    {"stdout", ::stdout},
+    {"cerr",   ::stderr},
+    {"stderr", ::stderr},
+};
+
 ArmSemihosting::ArmSemihosting(const ArmSemihostingParams *p)
     : SimObject(p),
       cmdLine(p->cmd_line),
@@ -128,7 +137,11 @@ ArmSemihosting::ArmSemihosting(const ArmSemihostingParams *p)
       stackSize(p->stack_size),
       timeBase([p]{ struct tm t = p->time; return mkutctime(&t); }()),
       tickShift(calcTickShift()),
-      semiErrno(0)
+      semiErrno(0),
+      stdin(getSTDIO("stdin", p->stdin, "r")),
+      stdout(getSTDIO("stdout", p->stdout, "w")),
+      stderr(p->stderr == p->stdout ?
+             stdout : getSTDIO("stderr", p->stderr, "w"))
 {
     // Create an empty place-holder file for position 0 as semi-hosting
     // calls typically expect non-zero file handles.
@@ -681,6 +694,23 @@ ArmSemihosting::getCall(uint32_t op, bool aarch64)
     }
 }
 
+FILE *
+ArmSemihosting::getSTDIO(const char *stream_name,
+                         const std::string &name, const char *mode)
+{
+    auto it = stdioMap.find(name);
+    if (it == stdioMap.end()) {
+        FILE *f = fopen(name.c_str(), mode);
+        if (!f) {
+            fatal("Failed to open %s (%s): %s\n",
+                  stream_name, name, strerror(errno));
+        }
+        return f;
+    } else {
+        return it->second;
+    }
+}
+
 std::unique_ptr<ArmSemihosting::FileBase>
 ArmSemihosting::FileBase::create(
     ArmSemihosting &parent, const std::string &fname, const char *mode)
@@ -819,11 +849,11 @@ ArmSemihosting::File::openImpl(bool in_cpt)
 
     if (_name == ":tt") {
         if (mode[0] == 'r') {
-            file = stdin;
+            file = parent.stdin;
         } else if (mode[0] == 'w') {
-            file = stdout;
+            file = parent.stdout;
         } else if (mode[0] == 'a') {
-            file = stderr;
+            file = parent.stderr;
         } else {
             warn("Unknown file mode for the ':tt' special file");
             return -EINVAL;
@@ -857,7 +887,9 @@ ArmSemihosting::File::close()
 bool
 ArmSemihosting::File::isTTY() const
 {
-    return file == stdout || file == stderr || file == stdin;
+    return file == parent.stdout ||
+        file == parent.stderr ||
+        file == parent.stdin;
 }
 
 int64_t
index 14c5f9d2d47311eb3da32ecccb068a00bba1c0ce..5816460211b642a428b776af6be950e081007822 100644 (file)
@@ -244,6 +244,9 @@ class ArmSemihosting : public SimObject
     };
 
     std::vector<std::unique_ptr<FileBase>> files;
+    FILE *stdin;
+    FILE *stdout;
+    FILE *stderr;
 
   protected: // Helper functions
     unsigned calcTickShift() const {
@@ -342,11 +345,14 @@ class ArmSemihosting : public SimObject
 #undef SEMI_CALL
 
     static const SemiCall *getCall(uint32_t op, bool aarch64);
+    static FILE *getSTDIO(const char *stream_name,
+                          const std::string &name, const char *mode);
 
     static const std::map<uint32_t, SemiCall> calls;
     static const std::vector<const char *> fmodes;
     static const std::map<uint64_t, const char *> exitCodes;
     static const std::vector<uint8_t> features;
+    static const std::map<const std::string, FILE *> stdioMap;
 };
 
 #endif // __ARCH_ARM_SEMIHOSTING_HH__