blob: 39c3bc0ed48ec1de9e503ff49e82d7604d59240d [file]
// 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.
#ifndef MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_
#define MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_
#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"
// This file defines a class that is used to implement ARM style RiscV
// semihosting as described at https://github.com/riscv/riscv-semihosting-spec.
// This class can be used for both 32 and 64 bit versions of the RiscV
// architecture.
namespace mpact {
namespace sim {
namespace riscv {
using ::mpact::sim::generic::Instruction;
class RiscVArmSemihost {
public:
// Enum for specifying bit width of the architecture.
enum class BitWidth {
kWord32,
kWord64,
};
// These opcodes occur before and after an ebreak instruction to indicate
// that it is a semihosting call.
// The sequence is:
// n - 4: Slli x0, x0, 0x1f
// n : Ebreak
// n + 4: Srai x0, x0, 0x7
static constexpr uint32_t kSlliNop1f = 0x01f01013;
static constexpr uint32_t kEBreak = 0x00100073;
static constexpr uint32_t kSraiNop7 = 0x40705013;
// Register names for the argument registers.
static constexpr char kA0Name[] = "x10"; // Also known as "a0".
static constexpr char kA1Name[] = "x11"; // Also known as "a1".
// Constructor/destructor.
RiscVArmSemihost(BitWidth bit_width, util::MemoryInterface *i_memory_if,
util::MemoryInterface *d_memory_if);
RiscVArmSemihost() = delete;
RiscVArmSemihost(const RiscVArmSemihost &) = delete;
~RiscVArmSemihost();
// If the instruction is a semihosting call, execute the requested function.
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;
}
private:
using SemihostOperation = std::function<absl::Status(uint64_t, uint64_t *)>;
std::function<void()> exit_callback_;
std::function<void(uint64_t)> exception_callback_;
// Exception code for application exit.
static constexpr int kAdpStoppedApplicationExit = 0x20026;
// The following op codes are taken from ARM semihosting documentation.
static constexpr int kSysClose = 0x02;
static constexpr int kSysClock = 0x10;
static constexpr int kSysElapsed = 0x30;
static constexpr int kSysErrno = 0x13;
static constexpr int kSysException = 0x18;
static constexpr int kSysFlen = 0x0c;
static constexpr int kSysGetCmdline = 0x15;
static constexpr int kSysHeapInfo = 0x16;
static constexpr int kSysIsError = 0x08;
static constexpr int kSysIsTty = 0x09;
static constexpr int kSysOpen = 0x01;
static constexpr int kSysRead = 0x06;
static constexpr int kSysReadc = 0x07;
static constexpr int kSysRemove = 0x0e;
static constexpr int kSysRename = 0x0f;
static constexpr int kSysSeek = 0x0a;
static constexpr int kSysSystem = 0x12;
static constexpr int kSysTickFreq = 0x31;
static constexpr int kSysTime = 0x11;
static constexpr int kSysTmpnam = 0x0d;
static constexpr int kSysWrite = 0x05;
static constexpr int kSysWritec = 0x03;
static constexpr int kSysWrite0 = 0x04;
// Functions that implement the semihosting operations.
absl::Status SysClose(uint64_t parameter, uint64_t *ret_val);
absl::Status SysClock(uint64_t parameter, uint64_t *ret_val);
absl::Status SysElapsed(uint64_t parameter, uint64_t *ret_val);
absl::Status SysErrno(uint64_t parameter, uint64_t *ret_val);
absl::Status SysException(uint64_t parameter, uint64_t *ret_val);
absl::Status SysFlen(uint64_t parameter, uint64_t *ret_val);
absl::Status SysGetCmdline(uint64_t parameter, uint64_t *ret_val);
absl::Status SysHeapInfo(uint64_t parameter, uint64_t *ret_val);
absl::Status SysIsError(uint64_t parameter, uint64_t *ret_val);
absl::Status SysIsTty(uint64_t parameter, uint64_t *ret_val);
absl::Status SysOpen(uint64_t parameter, uint64_t *ret_val);
absl::Status SysRead(uint64_t parameter, uint64_t *ret_val);
absl::Status SysReadc(uint64_t parameter, uint64_t *ret_val);
absl::Status SysRemove(uint64_t parameter, uint64_t *ret_val);
absl::Status SysRename(uint64_t parameter, uint64_t *ret_val);
absl::Status SysSeek(uint64_t parameter, uint64_t *ret_val);
absl::Status SysSystem(uint64_t parameter, uint64_t *ret_val);
absl::Status SysTickFreq(uint64_t parameter, uint64_t *ret_val);
absl::Status SysTime(uint64_t parameter, uint64_t *ret_val);
absl::Status SysTmpnam(uint64_t parameter, uint64_t *ret_val);
absl::Status SysWrite(uint64_t parameter, uint64_t *ret_val);
absl::Status SysWritec(uint64_t parameter, uint64_t *ret_val);
absl::Status SysWrite0(uint64_t parameter, uint64_t *ret_val);
bool is_32_bit_;
int sys_errno_ = 0;
generic::DataBufferFactory db_factory_;
// Data buffers used by some of the operations. Giving them lifetime of the
// semihosting instance saves some on allocating and freeing them for each
// operation.
generic::DataBuffer *db_inst_; // Instruction word(s) data buffer.
generic::DataBuffer *db1_; // 1 word data buffer.
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 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
} // namespace sim
} // namespace mpact
#endif // MPACT_RISCV_RISCV_RISCV_ARM_SEMIHOST_H_