Added functionality for the "get command line" semihosting command.

PiperOrigin-RevId: 720273398
Change-Id: Id5c6d2876c1088bdd28a5d74042d9d328aafc721
diff --git a/riscv/riscv_arm_semihost.cc b/riscv/riscv_arm_semihost.cc
index b3ba038..1d1523e 100644
--- a/riscv/riscv_arm_semihost.cc
+++ b/riscv/riscv_arm_semihost.cc
@@ -21,6 +21,7 @@
 #include <cerrno>
 #include <cstdint>
 #include <cstdio>
+#include <cstring>
 #include <limits>
 #include <string>
 #include <utility>
@@ -247,7 +248,7 @@
 
 // Return the length of a file given by the file descriptor.
 absl::Status RiscVArmSemihost::SysFlen(uint64_t parameter, uint64_t *ret_val) {
-  // Load the targete file descriptor.
+  // Load the targeted file descriptor.
   d_memory_if_->Load(parameter, db1_, nullptr, nullptr);
   int target_fd = is_32_bit_ ? static_cast<int>(db1_->Get<uint32_t>(0))
                              : static_cast<int>(db1_->Get<uint64_t>(0));
@@ -273,8 +274,29 @@
 // Currently unimplemented. Will implement if there is a demand.
 absl::Status RiscVArmSemihost::SysGetCmdline(uint64_t parameter,
                                              uint64_t *ret_val) {
-  return absl::UnimplementedError("SysGetCmdline not implemented");
-  // TODO: Complete implementation.
+  d_memory_if_->Load(parameter, db2_, nullptr, nullptr);
+  uint64_t buffer_address = is_32_bit_
+                                ? static_cast<uint64_t>(db2_->Get<uint32_t>(0))
+                                : static_cast<uint64_t>(db2_->Get<uint64_t>(0));
+  size_t buffer_len = is_32_bit_ ? static_cast<size_t>(db2_->Get<uint32_t>(1))
+                                 : static_cast<size_t>(db2_->Get<uint64_t>(1));
+  if (buffer_len <= cmd_line_.size()) {
+    *ret_val = -1ULL;
+    return absl::OkStatus();
+  }
+  auto *db = db_factory_.Allocate<uint8_t>(cmd_line_.size() + 1);
+  std::memcpy(db->raw_ptr(), cmd_line_.c_str(), cmd_line_.size());
+  db->Set<uint8_t>(cmd_line_.size(), 0);
+  d_memory_if_->Store(buffer_address, db);
+  db->DecRef();
+  if (is_32_bit_) {
+    db2_->Set<uint32_t>(1, static_cast<uint32_t>(cmd_line_.size()));
+  } else {
+    db2_->Set<uint64_t>(1, cmd_line_.size());
+  }
+  d_memory_if_->Store(parameter, db2_);
+  *ret_val = cmd_line_.size();
+  return absl::OkStatus();
 }
 
 // Returns 0 information indicating that the call doesn't provide this info.
diff --git a/riscv/riscv_arm_semihost.h b/riscv/riscv_arm_semihost.h
index 19d89bd..39c3bc0 100644
--- a/riscv/riscv_arm_semihost.h
+++ b/riscv/riscv_arm_semihost.h
@@ -17,10 +17,13 @@
 
 #include <cstdint>
 #include <functional>
+#include <string>
+#include <vector>
 
 #include "absl/container/flat_hash_map.h"
 #include "absl/log/log.h"
 #include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
 #include "mpact/sim/generic/data_buffer.h"
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/util/memory/memory_interface.h"
@@ -68,7 +71,17 @@
   void OnEBreak(const Instruction *inst);
   // Return true if the instruction is a semihosting call.
   bool IsSemihostingCall(const Instruction *inst);
+  // Set the command line string.
+  void SetCmdLine(const std::vector<char *> &argv) {
+    cmd_line_.clear();
+    std::string sep;
+    for (const auto &arg : argv) {
+      absl::StrAppend(&cmd_line_, sep, arg);
+      sep = " ";
+    }
+  }
 
+  // Setters.
   void set_exit_callback(std::function<void()> cb) { exit_callback_ = cb; }
   void set_exception_callback(std::function<void(uint64_t)> cb) {
     exception_callback_ = cb;
@@ -144,13 +157,14 @@
   generic::DataBuffer *db2_;      // 2 word data buffer.
   generic::DataBuffer *db3_;      // 3 word data buffer.
   generic::DataBuffer *db4_;      // 4 word data buffer.
-  // Memory interfaces to use to access intstruction and data memory.
+  // Memory interfaces to use to access instruction and data memory.
   util::MemoryInterface *i_memory_if_;
   util::MemoryInterface *d_memory_if_;
   // Map from opcode to semihosting function.
   absl::flat_hash_map<uint64_t, SemihostOperation> semihost_operations_;
   // Map of target file descriptors to host file descriptors.
   absl::flat_hash_map<int, int> fd_map_;
+  std::string cmd_line_;
 };
 
 }  // namespace riscv
diff --git a/riscv/rv32g_sim.cc b/riscv/rv32g_sim.cc
index c46e944..36e945e 100644
--- a/riscv/rv32g_sim.cc
+++ b/riscv/rv32g_sim.cc
@@ -231,17 +231,15 @@
     std::cerr << "Only one semihosting mechanism can be specified" << std::endl;
   }
 
-  if (arg_vec.size() > 2) {
-    std::cerr << "Only a single input file allowed" << std::endl;
-    return -1;
-  }
+  // Erase the simulator executable from arg_vec.
+  arg_vec.erase(arg_vec.begin());
 
   bool quiet = absl::GetFlag(FLAGS_quiet);
   if (quiet) {
     absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError);
   }
 
-  std::string full_file_name = arg_vec[1];
+  std::string full_file_name = arg_vec[0];
   std::string file_name =
       full_file_name.substr(full_file_name.find_last_of('/') + 1);
   std::string file_basename = file_name.substr(0, file_name.find_first_of('.'));
@@ -442,6 +440,7 @@
     // Add ARM semihosting.
     arm_semihost = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32,
                                         memory, memory);
+    arm_semihost->SetCmdLine(arg_vec);
     riscv_top.state()->AddEbreakHandler(
         [arm_semihost](const Instruction *inst) -> bool {
           if (arm_semihost->IsSemihostingCall(inst)) {
@@ -517,7 +516,7 @@
   std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out);
   std::string serialized;
   if (!proto_file.good() || !google::protobuf::TextFormat::PrintToString(
-                                *component_proto.get(), &serialized)) {
+                                *component_proto, &serialized)) {
     LOG(ERROR) << "Failed to write proto to file";
   } else {
     proto_file << serialized;
diff --git a/riscv/rv32gv_sim.cc b/riscv/rv32gv_sim.cc
index 94d1e65..c5b1cd3 100644
--- a/riscv/rv32gv_sim.cc
+++ b/riscv/rv32gv_sim.cc
@@ -232,17 +232,15 @@
     std::cerr << "Only one semihosting mechanism can be specified" << std::endl;
   }
 
-  if (arg_vec.size() > 2) {
-    std::cerr << "Only a single input file allowed" << std::endl;
-    return -1;
-  }
+  // Erase the simulator executable from arg_vec.
+  arg_vec.erase(arg_vec.begin());
 
   bool quiet = absl::GetFlag(FLAGS_quiet);
   if (quiet) {
     absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError);
   }
 
-  std::string full_file_name = arg_vec[1];
+  std::string full_file_name = arg_vec[0];
   std::string file_name =
       full_file_name.substr(full_file_name.find_last_of('/') + 1);
   std::string file_basename = file_name.substr(0, file_name.find_first_of('.'));
@@ -423,6 +421,7 @@
     // Add ARM semihosting.
     arm_semihost = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32,
                                         memory, memory);
+    arm_semihost->SetCmdLine(arg_vec);
     riscv_top.state()->AddEbreakHandler(
         [arm_semihost](const Instruction *inst) -> bool {
           if (arm_semihost->IsSemihostingCall(inst)) {
@@ -498,7 +497,7 @@
   std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out);
   std::string serialized;
   if (!proto_file.good() || !google::protobuf::TextFormat::PrintToString(
-                                *component_proto.get(), &serialized)) {
+                                *component_proto, &serialized)) {
     LOG(ERROR) << "Failed to write proto to file";
   } else {
     proto_file << serialized;
diff --git a/riscv/rv64g_sim.cc b/riscv/rv64g_sim.cc
index 71181b8..5f56635 100644
--- a/riscv/rv64g_sim.cc
+++ b/riscv/rv64g_sim.cc
@@ -199,17 +199,14 @@
     std::cerr << "Only one semihosting mechanism can be specified" << std::endl;
   }
 
-  if (arg_vec.size() > 2) {
-    std::cerr << "Only a single input file allowed" << std::endl;
-    return -1;
-  }
+  arg_vec.erase(arg_vec.begin());
 
   bool quiet = absl::GetFlag(FLAGS_quiet);
   if (quiet) {
     absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError);
   }
 
-  std::string full_file_name = arg_vec[1];
+  std::string full_file_name = arg_vec[0];
   std::string file_name =
       full_file_name.substr(full_file_name.find_last_of('/') + 1);
   std::string file_basename = file_name.substr(0, file_name.find_first_of('.'));
@@ -366,6 +363,7 @@
     // Add ARM semihosting.
     arm_semihost = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord64,
                                         memory, memory);
+    arm_semihost->SetCmdLine(arg_vec);
     riscv_top.state()->AddEbreakHandler(
         [arm_semihost](const Instruction *inst) {
           if (arm_semihost->IsSemihostingCall(inst)) {
diff --git a/riscv/rv64gv_sim.cc b/riscv/rv64gv_sim.cc
index 113fa6f..4029017 100644
--- a/riscv/rv64gv_sim.cc
+++ b/riscv/rv64gv_sim.cc
@@ -204,17 +204,14 @@
     std::cerr << "Only one semihosting mechanism can be specified" << std::endl;
   }
 
-  if (arg_vec.size() > 2) {
-    std::cerr << "Only a single input file allowed" << std::endl;
-    return -1;
-  }
+  arg_vec.erase(arg_vec.begin());
 
   bool quiet = absl::GetFlag(FLAGS_quiet);
   if (quiet) {
     absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError);
   }
 
-  std::string full_file_name = arg_vec[1];
+  std::string full_file_name = arg_vec[0];
   std::string file_name =
       full_file_name.substr(full_file_name.find_last_of('/') + 1);
   std::string file_basename = file_name.substr(0, file_name.find_first_of('.'));
@@ -378,6 +375,7 @@
     // Add ARM semihosting.
     arm_semihost = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord64,
                                         memory, memory);
+    arm_semihost->SetCmdLine(arg_vec);
     riscv_top.state()->AddEbreakHandler(
         [arm_semihost](const Instruction *inst) {
           if (arm_semihost->IsSemihostingCall(inst)) {
@@ -453,7 +451,7 @@
   std::fstream proto_file(proto_file_name.c_str(), std::ios_base::out);
   std::string serialized;
   if (!proto_file.good() || !google::protobuf::TextFormat::PrintToString(
-                                *component_proto.get(), &serialized)) {
+                                *component_proto, &serialized)) {
     LOG(ERROR) << "Failed to write proto to file";
   } else {
     proto_file << serialized;