48#include "debug/Semihosting.hh"
51#include "params/BaseSemihosting.hh"
76 {0x20000,
"semi:ADP_Stopped_BranchThroughZero"},
77 {0x20001,
"semi:ADP_Stopped_UndefinedInstr"},
78 {0x20002,
"semi:ADP_Stopped_SoftwareInterrupt"},
79 {0x20003,
"semi:ADP_Stopped_PrefetchAbort"},
80 {0x20004,
"semi:ADP_Stopped_DataAbort"},
81 {0x20005,
"semi:ADP_Stopped_AddressException"},
82 {0x20006,
"semi:ADP_Stopped_IRQ"},
83 {0x20007,
"semi:ADP_Stopped_FIQ"},
85 {0x20020,
"semi:ADP_Stopped_BreakPoint"},
86 {0x20021,
"semi:ADP_Stopped_WatchPoint"},
87 {0x20022,
"semi:ADP_Stopped_StepComplete"},
88 {0x20023,
"semi:ADP_Stopped_RunTimeErrorUnknown"},
89 {0x20024,
"semi:ADP_Stopped_InternalError"},
90 {0x20025,
"semi:ADP_Stopped_UserInterruption"},
91 {0x20026,
"semi:ADP_Stopped_ApplicationExit"},
92 {0x20027,
"semi:ADP_Stopped_StackOverflow"},
93 {0x20028,
"semi:ADP_Stopped_DivisionByZero"},
94 {0x20029,
"semi:ADP_Stopped_DivisionByZero"},
98 0x53, 0x48, 0x46, 0x42,
106 {
"stdout", ::stdout},
108 {
"stderr", ::stderr},
112 :
SimObject(
p), cmdLine(
p.cmd_line), memReserve(
p.mem_reserve),
113 stackSize(
p.stack_size), timeBase([
p] {
114 struct tm t =
p.time;
117 tickShift(calcTickShift()), semiErrno(0),
118 filesRootDir(!
p.files_root_dir.empty() &&
p.files_root_dir.back() !=
'/' ?
119 p.files_root_dir +
'/' :
121 stdin(getSTDIO(
"stdin",
p.stdin,
"r")),
122 stdout(getSTDIO(
"stdout",
p.stdout,
"w")),
123 stderr(
p.stderr ==
p.stdout ? stdout : getSTDIO(
"stderr",
p.stderr,
"w"))
127 files.push_back(
nullptr);
130 inform(
"Semihosting: Shifting elapsed ticks by %i bits.", tickShift);
139 for (
int i = 0;
i <
files.size();
i++) {
154 paramIn(cp,
"num_files", num_files);
155 files.resize(num_files);
156 for (
int i = 0;
i < num_files;
i++)
160std::optional<std::string>
168 warn(
"BaseSemihosting::readString(): attempting to read too large "
169 "(%d bytes) string from %#x",
len, ptr);
177 return std::string(buf.data());
186 DPRINTF(Semihosting,
"Semihosting SYS_OPEN(0x%x, %i[%s], %i)\n", name_base,
187 fmode,
mode ?
mode :
"-", name_size);
188 if (!
mode || !name_base)
191 std::optional<std::string> fnameOpt =
readString(tc, name_base, name_size);
192 if (!fnameOpt.has_value())
194 std::string fname = *fnameOpt;
195 if (!fname.empty() && fname.front() !=
'/' && fname !=
":tt" &&
196 fname !=
":semihosting-features")
199 std::unique_ptr<BaseSemihosting::FileBase> file =
201 int64_t ret = file->open();
202 DPRINTF(Semihosting,
"Semihosting SYS_OPEN(\"%s\", %i[%s]): %i\n", fname,
207 files.push_back(std::move(file));
215 if (handle >
files.size()) {
216 DPRINTF(Semihosting,
"Semihosting SYS_CLOSE(%i): Illegal file\n");
220 std::unique_ptr<FileBase> &file =
files[handle];
221 int64_t
error = file->close();
222 DPRINTF(Semihosting,
"Semihosting SYS_CLOSE(%i[%s]): %i\n", handle,
223 file->fileName(),
error);
229 files[handle].reset();
239 DPRINTF(Semihosting,
"Semihosting SYS_WRITEC('%c')\n",
c);
249 DPRINTF(Semihosting,
"Semihosting SYS_WRITE0(...)\n");
253 DDUMP(Semihosting, str.data(), str.size());
254 std::cout.write(str.c_str(), str.size());
267 DPRINTF(Semihosting,
"Semihosting SYS_WRITE(%x, %d)\n",
addr, size);
270 DDUMP(Semihosting, buffer.data(), buffer.size());
272 int64_t ret =
files[handle]->write(buffer.data(), buffer.size());
291 int64_t ret =
files[handle]->read(buffer.data(), buffer.size());
295 panic_if(ret > buffer.size(),
"Read longer than buffer size.");
300 return retOK(size - ret);
307 return retOK((
char)std::cin.get());
322 int64_t ret =
files[handle]->isTTY();
326 return retOK(ret ? 1 : 0);
336 int64_t ret =
files[handle]->seek(pos);
350 int64_t ret =
files[handle]->flen();
362 std::string
path =
"";
363 int64_t unlink_call_ret = 0;
368 unlink_call_ret = unlink(
path.c_str());
370 }
while ((unlink_call_ret < 0) && (errno == EBUSY));
372 const size_t path_len =
path.length();
373 if (path_len >= size)
384 std::optional<std::string> fname =
readString(tc, name_base, name_size);
386 if (!fname.has_value()) {
388 }
else if (remove(fname->c_str()) != 0) {
397 size_t from_size,
Addr to_addr,
size_t to_size)
399 std::optional<std::string> from =
readString(tc, from_addr, from_size);
400 std::optional<std::string>
to =
readString(tc, to_addr, to_size);
401 if (!from.has_value() || !
to.has_value()) {
403 }
else if (rename(from->c_str(),
to->c_str()) != 0) {
425 const std::optional<std::string> cmd =
readString(tc, cmd_addr, cmd_size);
426 if (!cmd.has_value())
428 warn(
"Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
446 size_t size = size_arg.
read(tc, proxy, endian);
448 if (
cmdLine.size() + 1 < size) {
463 fatal_if(memories.size() < 1,
"No memories reported from System");
465 "Multiple physical memory ranges available. "
466 "Using first range heap/stack.");
473 const Addr phys_max = (1ULL << 32) - 1;
475 "Physical memory out of range for a 32-bit guest.");
476 if (mem_end > phys_max) {
477 warn(
"Some physical memory out of range for a 32-bit guest.");
483 "Physical memory too small to fit desired stack and a heap.");
485 heap_base = mem_start;
487 stack_base = (mem_end + 1) & ~0x7ULL;
488 stack_limit = heap_limit;
490 inform(
"Reporting heap/stack info to guest:\n"
491 "\tHeap base: 0x%x\n"
492 "\tHeap limit: 0x%x\n"
493 "\tStack base: 0x%x\n"
494 "\tStack limit: 0x%x\n",
495 heap_base, heap_limit, stack_base, stack_limit);
501 uint64_t heap_base, heap_limit, stack_base, stack_limit;
502 gatherHeapInfo(tc,
false, heap_base, heap_limit, stack_base, stack_limit);
504 std::array<uint32_t, 4> block = {
505 {(uint32_t)heap_base, (uint32_t)heap_limit, (uint32_t)stack_base,
506 (uint32_t)stack_limit}};
515 uint64_t heap_base, heap_limit, stack_base, stack_limit;
516 gatherHeapInfo(tc,
true, heap_base, heap_limit, stack_base, stack_limit);
518 std::array<uint64_t, 4> block = {
519 {heap_base, heap_limit, stack_base, stack_limit}};
566 low.
write(tc, proxy, tick, endian);
567 high.write(tc, proxy, tick >> 32, endian);
587 const char *stream_name,
const std::string &
name,
const char *
mode)
593 fatal(
"Failed to open %s (%s): %s\n", stream_name,
name,
602std::unique_ptr<BaseSemihosting::FileBase>
606 std::unique_ptr<FileBase> file;
607 if (fname ==
":semihosting-features") {
616std::unique_ptr<BaseSemihosting::FileBase>
620 std::unique_ptr<FileBase> file;
627 std::string fname,
mode;
630 file = create(parent, fname,
mode.c_str());
632 file->unserialize(cp);
727BaseSemihosting::File::~
727BaseSemihosting::File::~ {
…}
737 panic_if(file,
"Trying to open an already open file.\n");
739 if (
_name ==
":tt") {
740 if (
mode[0] ==
'r') {
742 }
else if (
mode[0] ==
'w') {
743 file = parent.stdout;
744 }
else if (
mode[0] ==
'a') {
745 file = parent.stderr;
747 warn(
"Unknown file mode for the ':tt' special file");
751 std::string real_mode(this->
mode);
754 if (in_cpt && real_mode[0] ==
'w')
757 file = fopen(
_name.c_str(), real_mode.c_str());
760 return file ? 0 : -errno;
766 panic_if(!file,
"Trying to close an already closed file.\n");
779 return file == parent.stdout || file == parent.stderr ||
780 file == parent.stdin;
786 panic_if(!file,
"Trying to read from a closed file");
788 size_t ret = fread(buffer, 1, size, file);
792 return ferror(file) ? -EINVAL : 0;
801 panic_if(!file,
"Trying to write to a closed file");
803 size_t ret = fwrite(buffer, 1, size, file);
816 panic_if(!file,
"Trying to seek in a closed file");
819 if (fseek(file, _pos, SEEK_SET) == 0)
829 long pos = ftell(file);
833 if (fseek(file, 0, SEEK_END) != 0)
836 long len = ftell(file);
840 if (fseek(file, pos, SEEK_SET) != 0)
852 long pos = file ? ftell(file) : 0;
853 panic_if(pos < 0,
"Failed to get file position.");
863 if (openImpl(
true) < 0) {
870 if (fseek(file, pos, SEEK_SET) != 0) {
871 fatal(
"Failed seek to current position (%i) in '%s'", pos,
_name);
#define DDUMP(x, data, count)
DPRINTF is a debugging trace facility that allows one to selectively enable tracing statements.
The AddrRange class encapsulates an address range, and supports a number of tests to check if two ran...
Internal state for open files.
virtual int64_t write(const uint8_t *buffer, uint64_t size)
Write data to file.
void serialize(CheckpointOut &cp) const override
Serialize an object.
virtual int64_t seek(uint64_t pos)
Seek to an absolute position in the file.
virtual int64_t read(uint8_t *buffer, uint64_t size)
Read data from file.
static std::unique_ptr< FileBase > create(BaseSemihosting &parent, const std::string &fname, const char *mode)
virtual int64_t flen()
Get the length of a file in bytes.
void unserialize(CheckpointIn &cp) override
Unserialize an object.
Implementation of the ':semihosting-features' magic file.
void unserialize(CheckpointIn &cp) override
Unserialize an object.
int64_t seek(uint64_t pos) override
Seek to an absolute position in the file.
FileFeatures(BaseSemihosting &_parent, const char *name, const char *mode)
int64_t flen() override
Get the length of a file in bytes.
int64_t read(uint8_t *buffer, uint64_t size) override
Read data from file.
void serialize(CheckpointOut &cp) const override
Serialize an object.
bool isTTY() const override
Check if a file corresponds to a TTY device.
int64_t close() override
Close the file.
void unserialize(CheckpointIn &cp) override
Unserialize an object.
File(BaseSemihosting &_parent, const char *name, const char *mode)
void serialize(CheckpointOut &cp) const override
Serialize an object.
int64_t read(uint8_t *buffer, uint64_t size) override
Read data from file.
int64_t flen() override
Get the length of a file in bytes.
int64_t seek(uint64_t pos) override
Seek to an absolute position in the file.
int64_t openImpl(bool unserialize)
int64_t write(const uint8_t *buffer, uint64_t size) override
Write data to file.
Semihosting for AArch32, AArch64, RISCV-32 and RISCV-64: https://github.com/ARM-software/abi-aa/blob/...
RetErrno callReadC(ThreadContext *tc)
RetErrno callElapsed32(ThreadContext *tc, InPlaceArg low, InPlaceArg high)
RetErrno callExit64(ThreadContext *tc, uint64_t code, uint64_t subcode)
static const std::map< uint64_t, const char * > exitCodes
RetErrno callFLen(ThreadContext *tc, Handle handle)
const std::string cmdLine
static const std::array< uint8_t, 5 > features
static FILE * getSTDIO(const char *stream_name, const std::string &name, const char *mode)
RetErrno callTickFreq(ThreadContext *tc)
std::pair< uint64_t, SemiErrno > RetErrno
virtual ByteOrder byteOrder(ThreadContext *tc) const =0
static const std::map< const std::string, FILE * > stdioMap
RetErrno callRename(ThreadContext *tc, Addr from_addr, size_t from_size, Addr to_addr, size_t to_size)
RetErrno callExitExtended(ThreadContext *tc, uint64_t code, uint64_t subcode)
RetErrno callTmpNam(ThreadContext *tc, Addr buffer, uint64_t id, size_t size)
RetErrno callSeek(ThreadContext *tc, Handle handle, uint64_t pos)
std::vector< std::unique_ptr< FileBase > > files
static const std::vector< const char * > fmodes
RetErrno callSystem(ThreadContext *tc, Addr cmd_addr, size_t cmd_size)
BaseSemihosting(const BaseSemihostingParams &p)
void unserialize(CheckpointIn &cp) override
Unserialize an object.
RetErrno callWrite(ThreadContext *tc, Handle handle, Addr buffer, size_t size)
RetErrno callOpen(ThreadContext *tc, const Addr name_base, int fmode, size_t name_size)
static RetErrno retError(SemiErrno e)
RetErrno callIsError(ThreadContext *tc, int64_t status)
void semiExit(uint64_t code, uint64_t subcode)
RetErrno callElapsed64(ThreadContext *tc, InPlaceArg ticks)
RetErrno callRead(ThreadContext *tc, Handle handle, Addr buffer, size_t size)
const time_t timeBase
Base time when the simulation started.
RetErrno callExit32(ThreadContext *tc, InPlaceArg code)
void serialize(CheckpointOut &cp) const override
Serialize an object.
virtual PortProxy & portProxy(ThreadContext *tc) const =0
static RetErrno retOK(uint64_t r)
void gatherHeapInfo(ThreadContext *tc, bool aarch64, Addr &heap_base, Addr &heap_limit, Addr &stack_base, Addr &stack_limit)
RetErrno callGetCmdLine(ThreadContext *tc, Addr addr, InPlaceArg size_arg)
RetErrno callHeapInfo32(ThreadContext *tc, Addr block_addr)
RetErrno callClock(ThreadContext *tc)
std::optional< std::string > readString(ThreadContext *tc, Addr ptr, size_t len)
RetErrno callRemove(ThreadContext *tc, Addr name_base, size_t name_size)
RetErrno callHeapInfo64(ThreadContext *tc, Addr block_addr)
uint64_t semiTick(Tick tick) const
RetErrno callIsTTY(ThreadContext *tc, Handle handle)
RetErrno callWriteC(ThreadContext *tc, InPlaceArg c)
RetErrno callErrno(ThreadContext *tc)
RetErrno callTime(ThreadContext *tc)
RetErrno callWrite0(ThreadContext *tc, InPlaceArg str)
RetErrno callClose(ThreadContext *tc, Handle handle)
virtual std::string name() const
std::string resolve(const std::string &name) const
Returns relative file names prepended with name of this directory.
This object is a proxy for a port or other object which implements the functional response protocol,...
void readBlob(Addr addr, void *p, uint64_t size) const
Higher level interfaces based on the above.
T read(Addr address) const
Read sizeof(T) bytes from address and return as object T.
void writeBlob(Addr addr, const void *p, uint64_t size) const
Same as tryWriteBlob, but insists on success.
void write(Addr address, const T &data) const
Write object T to address.
void readString(std::string &str, Addr addr) const
Same as tryReadString, but insists on success.
static std::stack< std::string > path
Abstract superclass for simulation objects.
memory::PhysicalMemory & getPhysMem()
Get a pointer to access the physical memory of the system.
ThreadContext is the external interface to all thread state for anything outside of the CPU.
virtual System * getSystemPtr()=0
The physical memory encapsulates all memories in the system and provides basic functionality for acce...
AddrRangeList getConfAddrRanges() const
Get the memory ranges for all memories that are to be reported to the configuration table.
#define fatal_if(cond,...)
Conditional fatal macro that checks the supplied condition and only causes a fatal error if the condi...
#define fatal(...)
This implements a cprintf based fatal() function.
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
static const std::string & currentSection()
Gets the fully-qualified name of the active section.
bool sectionExists(const std::string §ion)
#define warn_if(cond,...)
Conditional warning macro that checks the supplied condition and only prints a warning if the conditi...
double s
These variables equal the number of ticks in the unit of time they're named after in a double.
Tick Frequency
The simulated frequency of curTick(). (In ticks per second)
Copyright (c) 2024 Arm Limited All rights reserved.
Tick curTick()
The universal simulation clock.
std::ostream CheckpointOut
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
void paramOut(CheckpointOut &cp, const std::string &name, ExtMachInst const &machInst)
time_t mkutctime(struct tm *time)
void paramIn(CheckpointIn &cp, const std::string &name, ExtMachInst &machInst)
void exitSimLoop(const std::string &message, int exit_code, Tick when, Tick repeat, bool serialize)
Schedule an event to exit the simulation loop (returning to Python) at the end of the current cycle (...
std::ostream & operator<<(std::ostream &os, const BaseSemihosting::InPlaceArg &ipa)
std::string csprintf(const char *format, const Args &...args)
void ccprintf(cp::Print &print)
#define UNSERIALIZE_SCALAR(scalar)
#define SERIALIZE_SCALAR(scalar)
void write(ThreadContext *tc, PortProxy &proxy, uint64_t val, ByteOrder endian)
uint64_t read(ThreadContext *tc, PortProxy &proxy, ByteOrder endian)
const std::string & name()