| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "riscv/riscv_arm_semihost.h" |
| |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <time.h> |
| |
| #include <cerrno> |
| #include <cstdint> |
| #include <cstdio> |
| #include <cstring> |
| #include <limits> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/functional/bind_front.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "mpact/sim/generic/register.h" |
| #include "mpact/sim/util/memory/memory_interface.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace riscv { |
| |
| RiscVArmSemihost::RiscVArmSemihost(BitWidth bit_width, |
| util::MemoryInterface* i_memory_if, |
| util::MemoryInterface* d_memory_if) |
| : i_memory_if_(i_memory_if), |
| d_memory_if_(d_memory_if), |
| // Put the functions that implement the different semihosting operations |
| // into a map according to their function code. |
| semihost_operations_( |
| {{kSysClose, absl::bind_front(&RiscVArmSemihost::SysClose, this)}, |
| {kSysClock, absl::bind_front(&RiscVArmSemihost::SysClock, this)}, |
| {kSysElapsed, absl::bind_front(&RiscVArmSemihost::SysElapsed, this)}, |
| {kSysErrno, absl::bind_front(&RiscVArmSemihost::SysErrno, this)}, |
| {kSysException, |
| absl::bind_front(&RiscVArmSemihost::SysException, this)}, |
| {kSysFlen, absl::bind_front(&RiscVArmSemihost::SysFlen, this)}, |
| {kSysGetCmdline, |
| absl::bind_front(&RiscVArmSemihost::SysGetCmdline, this)}, |
| {kSysHeapInfo, |
| absl::bind_front(&RiscVArmSemihost::SysHeapInfo, this)}, |
| {kSysIsError, absl::bind_front(&RiscVArmSemihost::SysIsError, this)}, |
| {kSysIsTty, absl::bind_front(&RiscVArmSemihost::SysIsTty, this)}, |
| {kSysOpen, absl::bind_front(&RiscVArmSemihost::SysOpen, this)}, |
| {kSysRead, absl::bind_front(&RiscVArmSemihost::SysRead, this)}, |
| {kSysReadc, absl::bind_front(&RiscVArmSemihost::SysReadc, this)}, |
| {kSysRemove, absl::bind_front(&RiscVArmSemihost::SysRemove, this)}, |
| {kSysRename, absl::bind_front(&RiscVArmSemihost::SysRename, this)}, |
| {kSysSeek, absl::bind_front(&RiscVArmSemihost::SysSeek, this)}, |
| {kSysSystem, absl::bind_front(&RiscVArmSemihost::SysSystem, this)}, |
| {kSysTickFreq, |
| absl::bind_front(&RiscVArmSemihost::SysTickFreq, this)}, |
| {kSysTime, absl::bind_front(&RiscVArmSemihost::SysTime, this)}, |
| {kSysTmpnam, absl::bind_front(&RiscVArmSemihost::SysTmpnam, this)}, |
| {kSysWrite, absl::bind_front(&RiscVArmSemihost::SysWrite, this)}, |
| {kSysWritec, absl::bind_front(&RiscVArmSemihost::SysWritec, this)}, |
| {kSysWrite0, |
| absl::bind_front(&RiscVArmSemihost::SysWrite0, this)}}) { |
| is_32_bit_ = bit_width == BitWidth::kWord32; |
| // Pre-allocate some fixed length data buffers that are used across semihost |
| // calls. Set the length according to the register width for those used to |
| // fetch parameters from data memory. |
| db_inst_ = db_factory_.Allocate<uint32_t>(3); |
| db1_ = is_32_bit_ ? db_factory_.Allocate<uint32_t>(1) |
| : db_factory_.Allocate<uint64_t>(1); |
| db2_ = is_32_bit_ ? db_factory_.Allocate<uint32_t>(2) |
| : db_factory_.Allocate<uint64_t>(2); |
| db3_ = is_32_bit_ ? db_factory_.Allocate<uint32_t>(3) |
| : db_factory_.Allocate<uint64_t>(3); |
| db4_ = is_32_bit_ ? db_factory_.Allocate<uint32_t>(4) |
| : db_factory_.Allocate<uint64_t>(4); |
| // stdin |
| fd_map_.insert(std::make_pair(0, 0)); |
| // stdout |
| fd_map_.insert(std::make_pair(1, 1)); |
| // stderr |
| fd_map_.insert(std::make_pair(2, 2)); |
| } |
| |
| RiscVArmSemihost::~RiscVArmSemihost() { |
| db_inst_->DecRef(); |
| db1_->DecRef(); |
| db2_->DecRef(); |
| db3_->DecRef(); |
| db4_->DecRef(); |
| } |
| |
| bool RiscVArmSemihost::IsSemihostingCall(const Instruction* inst) { |
| if (inst == nullptr) return false; |
| // Load the instruction words on either side of the ebreak instruction. |
| uint64_t address = inst->address() - 4; |
| i_memory_if_->Load(address, db_inst_, nullptr, nullptr); |
| // Check to see if this is a semihosting call, if not, return. |
| if ((db_inst_->Get<uint32_t>(0) != kSlliNop1f) || |
| (db_inst_->Get<uint32_t>(1) != kEBreak) || |
| (db_inst_->Get<uint32_t>(2) != kSraiNop7)) { |
| return false; |
| } |
| return true; |
| } |
| |
| void RiscVArmSemihost::OnEBreak(const Instruction* inst) { |
| if (!IsSemihostingCall(inst)) return; |
| |
| // Handle the semihosting call. |
| auto registers = inst->state()->registers(); |
| auto iter = registers->find(kA0Name); |
| auto* a0 = iter == registers->end() ? nullptr : iter->second; |
| iter = registers->find(kA1Name); |
| auto* a1 = iter == registers->end() ? nullptr : iter->second; |
| if ((a0 == nullptr) || (a1 == nullptr)) { |
| LOG(ERROR) << "Failed to fetch semihost argument registers"; |
| } |
| |
| // Read the op number and the address of the parameter block from the |
| // registers. |
| uint64_t op_num = 0; |
| uint64_t parameter = 0; |
| if (is_32_bit_) { |
| op_num = a0->data_buffer()->Get<uint32_t>(0); |
| parameter = a1->data_buffer()->Get<uint32_t>(0); |
| } else { |
| op_num = a0->data_buffer()->Get<uint64_t>(0); |
| parameter = a1->data_buffer()->Get<uint64_t>(0); |
| } |
| |
| // Make sure the op number is valid. |
| auto op = semihost_operations_.find(op_num); |
| if (op == semihost_operations_.end()) { |
| LOG(ERROR) << absl::StrCat("Illegal semihost operation (", |
| absl::Hex(op_num), ")"); |
| return; |
| } |
| |
| // Call the semihosting op. |
| uint64_t ret_val = 0; |
| auto status = op->second(parameter, &ret_val); |
| |
| // In case of error, set an error code and log the error message. |
| if (!status.ok()) { |
| LOG(ERROR) << absl::StrCat("Semihost error: ", status.message()); |
| if (is_32_bit_) { |
| a0->data_buffer()->Set<uint32_t>(0, std::numeric_limits<uint32_t>::max()); |
| } else { |
| a0->data_buffer()->Set<uint64_t>(0, std::numeric_limits<uint64_t>::max()); |
| } |
| } else { |
| if (is_32_bit_) { |
| a0->data_buffer()->Set<uint32_t>(0, static_cast<uint32_t>(ret_val)); |
| } else { |
| a0->data_buffer()->Set<uint64_t>(0, ret_val); |
| } |
| } |
| } |
| |
| absl::Status RiscVArmSemihost::SysClose(uint64_t parameter, uint64_t* ret_val) { |
| // Load the file descriptor from the parameter block. |
| 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)); |
| // Check to see that the target file descriptor is in the map, if not, |
| // the fd is not for an opened file. |
| auto iter = fd_map_.find(target_fd); |
| if (iter == fd_map_.end()) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| // Get the host fd and close the file. |
| int host_fd = iter->second; |
| // If it's stdin, stdout, or stderr, ignore, just return ok. |
| if (host_fd <= 2) { |
| *ret_val = 0; |
| return absl::OkStatus(); |
| } |
| int ret = close(host_fd); |
| if (!ret) { |
| sys_errno_ = errno; |
| } else { |
| // Remove the file descriptor from the map. |
| fd_map_.erase(iter); |
| } |
| *ret_val = static_cast<uint64_t>(ret); |
| return absl::OkStatus(); |
| } |
| |
| // Currently not implemented, will implement once there is need for it. |
| absl::Status RiscVArmSemihost::SysClock(uint64_t parameter, uint64_t* ret_val) { |
| return absl::UnimplementedError("SysClock not implemented"); |
| // TODO: Complete implementation. |
| } |
| |
| // Currently not implemented, will implement once there is need for it. |
| absl::Status RiscVArmSemihost::SysElapsed(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysElapsed not implemented"); |
| // TODO: Complete implementation. |
| } |
| |
| // Return the value of the simulated errno. |
| absl::Status RiscVArmSemihost::SysErrno(uint64_t parameter, uint64_t* ret_val) { |
| *ret_val = sys_errno_; |
| return absl::OkStatus(); |
| } |
| |
| // Exception notification. The program should be terminated. |
| absl::Status RiscVArmSemihost::SysException(uint64_t parameter, uint64_t*) { |
| // In gcc it seems like the parameter value is passed in the parameter |
| // register for RV32, but stored in memory, and then a pointer passed in the |
| // parameter register for RV64. A bit odd... |
| uint32_t param_value; |
| if (is_32_bit_) { |
| param_value = static_cast<uint32_t>(parameter); |
| } else { |
| d_memory_if_->Load(parameter, db1_, nullptr, nullptr); |
| param_value = static_cast<uint32_t>(db1_->Get<uint64_t>(0)); |
| } |
| if (param_value == kAdpStoppedApplicationExit) { |
| if (exit_callback_ != nullptr) { |
| exit_callback_(); |
| return absl::OkStatus(); |
| } |
| LOG(ERROR) << "Program exit not caught in ARM semihosting - no callback"; |
| return absl::NotFoundError("Exit callback not valid in ARM semihosting"); |
| } |
| LOG(ERROR) << absl::StrCat( |
| "Exception ", absl::Hex(param_value), " other than ApplicationExit (", |
| absl::Hex(kAdpStoppedApplicationExit), ") are not implemented"); |
| return absl::UnimplementedError( |
| "Exceptions other than ApplicationExit are not implemented"); |
| } |
| |
| // Return the length of a file given by the file descriptor. |
| absl::Status RiscVArmSemihost::SysFlen(uint64_t parameter, uint64_t* ret_val) { |
| // 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)); |
| // Verify that the file descriptor is valid. |
| auto iter = fd_map_.find(target_fd); |
| if (iter == fd_map_.end()) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| auto host_fd = iter->second; |
| // Get the file info, and get the file length. |
| struct stat statbuf; |
| int res = fstat(host_fd, &statbuf); |
| if (res < 0) { |
| sys_errno_ = errno; |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| *ret_val = static_cast<uint64_t>(statbuf.st_size); |
| return absl::OkStatus(); |
| } |
| |
| // Currently unimplemented. Will implement if there is a demand. |
| absl::Status RiscVArmSemihost::SysGetCmdline(uint64_t parameter, |
| uint64_t* ret_val) { |
| 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. |
| absl::Status RiscVArmSemihost::SysHeapInfo(uint64_t parameter, |
| uint64_t* ret_val) { |
| d_memory_if_->Load(parameter, db1_, nullptr, nullptr); |
| uint64_t block_address = is_32_bit_ |
| ? static_cast<int>(db1_->Get<uint32_t>(0)) |
| : static_cast<int>(db1_->Get<uint64_t>(0)); |
| // Return all zeros. |
| auto* db = db_factory_.Allocate<uint32_t>(4); |
| d_memory_if_->Load(block_address, db, nullptr, nullptr); |
| db->Set<uint32_t>(0, 0); |
| db->Set<uint32_t>(1, 0); |
| db->Set<uint32_t>(2, 0); |
| db->Set<uint32_t>(3, 0); |
| d_memory_if_->Store(block_address, db); |
| db->DecRef(); |
| return absl::OkStatus(); |
| } |
| |
| // This function is not implemented for now. Will look into it if there is |
| // demand. |
| absl::Status RiscVArmSemihost::SysIsError(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysGetCmdline not implemented"); |
| // TODO: Complete implementation. |
| } |
| |
| // Check if the fd is a tty. |
| absl::Status RiscVArmSemihost::SysIsTty(uint64_t parameter, uint64_t* ret_val) { |
| // Load the target 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)); |
| // Check if the fd is valid. |
| auto iter = fd_map_.find(target_fd); |
| if (iter == fd_map_.end()) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| auto host_fd = iter->second; |
| // Return the value of the isatty call. |
| int ret = isatty(host_fd); |
| if (!ret) { |
| sys_errno_ = errno; |
| } |
| *ret_val = static_cast<uint64_t>(ret); |
| return absl::OkStatus(); |
| } |
| |
| // Open a file, return the file descriptor if successful. |
| absl::Status RiscVArmSemihost::SysOpen(uint64_t parameter, uint64_t* ret_val) { |
| // Load the parameter block consisiting of pointer to a string, the file open |
| // mode, and the length of the string. |
| d_memory_if_->Load(parameter, db3_, nullptr, nullptr); |
| uint64_t string_address = is_32_bit_ |
| ? static_cast<uint64_t>(db3_->Get<uint32_t>(0)) |
| : static_cast<uint64_t>(db3_->Get<uint64_t>(0)); |
| int mode = is_32_bit_ ? static_cast<int>(db3_->Get<int32_t>(1)) |
| : static_cast<int>(db3_->Get<int64_t>(1)); |
| uint64_t file_name_len = is_32_bit_ |
| ? static_cast<int>(db3_->Get<uint32_t>(2)) |
| : static_cast<int>(db3_->Get<uint64_t>(2)); |
| // Allocate a data buffer for the file name string, load it, and initialize |
| // a string variable with it. |
| std::string file_name; |
| if (file_name_len > 0) { |
| auto* db_c = db_factory_.Allocate<uint8_t>(file_name_len); |
| d_memory_if_->Load(string_address, db_c, nullptr, nullptr); |
| file_name = std::string(static_cast<char*>(db_c->raw_ptr()), file_name_len); |
| db_c->DecRef(); |
| } |
| // If the name is ":tt" then it's either cin or cout depending on the mode. |
| // In this case just dup the corresponding host fd's. |
| int host_fd; |
| int target_fd; |
| if (file_name == ":tt") { |
| if (mode == O_RDONLY) { |
| host_fd = 0; |
| target_fd = dup(0); |
| } else { |
| host_fd = 1; |
| target_fd = dup(1); |
| } |
| } else { |
| // Open the file, create if needed for write/update modes. |
| if (mode == O_RDONLY) { |
| host_fd = open(file_name.c_str(), 0, mode); |
| } else { |
| host_fd = open(file_name.c_str(), O_CREAT, mode); |
| } |
| target_fd = dup(host_fd); |
| } |
| *ret_val = static_cast<uint64_t>(target_fd); |
| if (target_fd < 0) { |
| sys_errno_ = errno; |
| return absl::OkStatus(); |
| } |
| |
| fd_map_.insert(std::make_pair(target_fd, host_fd)); |
| return absl::OkStatus(); |
| } |
| |
| absl::Status RiscVArmSemihost::SysRead(uint64_t parameter, uint64_t* ret_val) { |
| // Load the parameter block, consisting of the target file descriptor, the |
| // target buffer address, and buffer length. |
| d_memory_if_->Load(parameter, db3_, nullptr, nullptr); |
| int target_fd = is_32_bit_ ? static_cast<int>(db3_->Get<uint32_t>(0)) |
| : static_cast<int>(db3_->Get<uint64_t>(0)); |
| uint64_t buffer_address = is_32_bit_ |
| ? static_cast<uint64_t>(db3_->Get<uint32_t>(1)) |
| : static_cast<uint64_t>(db3_->Get<uint64_t>(1)); |
| int length = is_32_bit_ ? static_cast<int>(db3_->Get<int32_t>(2)) |
| : static_cast<int>(db3_->Get<int64_t>(2)); |
| // Check that the file descriptor is valid. |
| auto iter = fd_map_.find(target_fd); |
| if (iter == fd_map_.end()) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| auto host_fd = iter->second; |
| // Allocate a data buffer sufficient for the target buffer length. |
| auto* db = db_factory_.Allocate<uint8_t>(length); |
| // Read from the file/ |
| int res = read(host_fd, db->raw_ptr(), length); |
| *ret_val = static_cast<uint64_t>(res); |
| if (res < 0) { |
| sys_errno_ = errno; |
| return absl::OkStatus(); |
| } |
| // Write to the buffer. |
| d_memory_if_->Store(buffer_address, db); |
| db->DecRef(); |
| return absl::OkStatus(); |
| } |
| |
| // Read a byte from the debug console. This is not implemented for now. |
| absl::Status RiscVArmSemihost::SysReadc(uint64_t parameter, uint64_t* ret_val) { |
| return absl::UnimplementedError("SysReadc not implemented"); |
| // TODO: Complete implementation. |
| } |
| |
| // Remove a file from the host file system. This will not be implemented. |
| absl::Status RiscVArmSemihost::SysRemove(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysRemove not implemented"); |
| } |
| |
| // Rename a file in the host file system. This will not be implemented. |
| absl::Status RiscVArmSemihost::SysRename(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysRename not implemented"); |
| } |
| |
| // Seek in the file specified by the target file descriptor. |
| absl::Status RiscVArmSemihost::SysSeek(uint64_t parameter, uint64_t* ret_val) { |
| // Load the parameters consisting of the target fd and the desired seek |
| // offset. |
| d_memory_if_->Load(parameter, db2_, nullptr, nullptr); |
| int target_fd = is_32_bit_ ? static_cast<int>(db2_->Get<uint32_t>(0)) |
| : static_cast<int>(db2_->Get<uint64_t>(0)); |
| uint64_t offset = is_32_bit_ ? static_cast<int>(db2_->Get<uint32_t>(1)) |
| : static_cast<int>(db2_->Get<uint64_t>(1)); |
| // Verify that the target fd is valid. |
| auto iter = fd_map_.find(target_fd); |
| if (iter == fd_map_.end()) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| auto host_fd = iter->second; |
| // Perform the seek relative to the beginning of the file. |
| auto res = lseek(host_fd, offset, SEEK_SET); |
| if (res < 0) { |
| sys_errno_ = errno; |
| res = -1; |
| } else { |
| res = 0; |
| } |
| *ret_val = static_cast<uint64_t>(res); |
| return absl::OkStatus(); |
| } |
| |
| // Execute a command in the shell of the host. This will not be implemented. |
| absl::Status RiscVArmSemihost::SysSystem(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysSystem not implemented"); |
| } |
| |
| // Return the system tick frequency. For now just return -1 to indicate that |
| // this call is not fully supported. |
| absl::Status RiscVArmSemihost::SysTickFreq(uint64_t parameter, |
| uint64_t* ret_val) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| |
| // Return unix time in seconds. |
| absl::Status RiscVArmSemihost::SysTime(uint64_t parameter, uint64_t* ret_val) { |
| *ret_val = time(nullptr); |
| return absl::OkStatus(); |
| } |
| |
| // Return a temporary file name. |
| absl::Status RiscVArmSemihost::SysTmpnam(uint64_t parameter, |
| uint64_t* ret_val) { |
| // Load parameters consisting of a pointer to a buffer, an int (0-255) that |
| // is a target identifier for this filename, and the length of the buffer. |
| d_memory_if_->Load(parameter, db3_, nullptr, nullptr); |
| uint64_t buffer_address = is_32_bit_ |
| ? static_cast<int>(db3_->Get<uint32_t>(0)) |
| : static_cast<int>(db3_->Get<uint64_t>(0)); |
| int target_id = is_32_bit_ ? static_cast<uint64_t>(db3_->Get<uint32_t>(1)) |
| : static_cast<uint64_t>(db3_->Get<uint64_t>(1)); |
| int length = is_32_bit_ ? static_cast<int>(db3_->Get<int32_t>(2)) |
| : static_cast<int>(db3_->Get<int64_t>(2)); |
| // Validate the target_id. |
| if (target_id < 0 || target_id > 255) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| // The length of the buffer has to be at least L_tmpnam |
| if (length < L_tmpnam) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| // Allocate a data buffer and call tmpnam, then write the name to the buffer. |
| auto tmpnam_db = db_factory_.Allocate<uint8_t>(length); |
| tmpnam(static_cast<char*>(tmpnam_db->raw_ptr())); |
| d_memory_if_->Store(buffer_address, tmpnam_db); |
| tmpnam_db->DecRef(); |
| return absl::OkStatus(); |
| } |
| |
| // Write data to a file. |
| absl::Status RiscVArmSemihost::SysWrite(uint64_t parameter, uint64_t* ret_val) { |
| // Load parameters consisting of target fd, target buffer address, and |
| // length. |
| d_memory_if_->Load(parameter, db3_, nullptr, nullptr); |
| int target_fd = is_32_bit_ ? static_cast<int>(db3_->Get<uint32_t>(0)) |
| : static_cast<int>(db3_->Get<uint64_t>(0)); |
| uint64_t buffer_address = is_32_bit_ |
| ? static_cast<uint64_t>(db3_->Get<uint32_t>(1)) |
| : static_cast<uint64_t>(db3_->Get<uint64_t>(1)); |
| int length = is_32_bit_ ? static_cast<int>(db3_->Get<int32_t>(2)) |
| : static_cast<int>(db3_->Get<int64_t>(2)); |
| // Verify that the target fd is valid. |
| auto iter = fd_map_.find(target_fd); |
| if (iter == fd_map_.end()) { |
| *ret_val = -1ULL; |
| return absl::OkStatus(); |
| } |
| auto host_fd = iter->second; |
| // Allocate the data buffer necessary to read the data to be written to the |
| // file. |
| auto* db = db_factory_.Allocate<uint8_t>(length); |
| d_memory_if_->Load(buffer_address, db, nullptr, nullptr); |
| // Write the data to the file. |
| int res = write(host_fd, db->raw_ptr(), length); |
| *ret_val = static_cast<uint64_t>(res); |
| if (res < 0) { |
| sys_errno_ = errno; |
| } |
| *ret_val = static_cast<uint64_t>(res); |
| db->DecRef(); |
| return absl::OkStatus(); |
| } |
| |
| // Write a byte to the degbug console. This is not implemented for now. |
| absl::Status RiscVArmSemihost::SysWritec(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysWritec not implemented"); |
| } |
| |
| // Write a null terminated string to the debug console. This is not implemented |
| // for now. |
| absl::Status RiscVArmSemihost::SysWrite0(uint64_t parameter, |
| uint64_t* ret_val) { |
| return absl::UnimplementedError("SysWrite0 not implemented"); |
| } |
| |
| } // namespace riscv |
| } // namespace sim |
| } // namespace mpact |