Change-Id: I352e12d4742f0771859bdbf9634ac87e2c153427 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/49764 Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com> Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
667 lines
22 KiB
C++
667 lines
22 KiB
C++
/*
|
|
* Copyright (c) 2018, 2019 ARM Limited
|
|
* All rights reserved
|
|
*
|
|
* The license below extends only to copyright in the software and shall
|
|
* not be construed as granting a license to any other intellectual
|
|
* property including but not limited to intellectual property relating
|
|
* to a hardware implementation of the functionality of the software
|
|
* licensed hereunder. You may use the software subject to the license
|
|
* terms below provided that you ensure that this notice is replicated
|
|
* unmodified and in its entirety in all distributions of the software,
|
|
* modified or unmodified, in source code or in binary form.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef __ARCH_ARM_SEMIHOSTING_HH__
|
|
#define __ARCH_ARM_SEMIHOSTING_HH__
|
|
|
|
#include <cstdio>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "arch/arm/regs/int.hh"
|
|
#include "arch/arm/utility.hh"
|
|
#include "cpu/thread_context.hh"
|
|
#include "mem/port_proxy.hh"
|
|
#include "sim/core.hh"
|
|
#include "sim/guest_abi.hh"
|
|
#include "sim/sim_object.hh"
|
|
|
|
namespace gem5
|
|
{
|
|
|
|
struct ArmSemihostingParams;
|
|
class SerialDevice;
|
|
|
|
/**
|
|
* Semihosting for AArch32 and AArch64
|
|
*
|
|
* This class implements the Arm semihosting interface. This interface
|
|
* allows baremetal code access service, such as IO, from the
|
|
* simulator. It is conceptually a simplified version of gem5's more
|
|
* general syscall emulation mode.
|
|
*
|
|
* Exits calls (SYS_EXIT, SYS_EXIT_EXTENDED) from the guest get
|
|
* translated into simualtion exits. Well-known exit codes are
|
|
* translated to messages on the form 'semi:ADP_.*' while unknown
|
|
* codes are returned in hex ('semi:0x..'). The subcode is reported in
|
|
* the gem5 exit event.
|
|
*/
|
|
class ArmSemihosting : public SimObject
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
// Standard ARM immediate values which trigger semihosting.
|
|
T32Imm = 0xAB,
|
|
A32Imm = 0x123456,
|
|
A64Imm = 0xF000,
|
|
|
|
// The immediate value which enables gem5 semihosting calls. Use the
|
|
// standard value for thumb.
|
|
Gem5Imm = 0x5D57
|
|
};
|
|
|
|
static PortProxy &portProxy(ThreadContext *tc);
|
|
|
|
struct AbiBase
|
|
{
|
|
template <typename Arg>
|
|
class StateBase
|
|
{
|
|
private:
|
|
Addr argPointer;
|
|
ByteOrder endian;
|
|
|
|
public:
|
|
StateBase(const ThreadContext *tc, Addr arg_pointer) :
|
|
argPointer(arg_pointer), endian(ArmISA::byteOrder(tc))
|
|
{}
|
|
|
|
/*
|
|
* These two methods are used to both read an argument or its
|
|
* address, and to move position on to the next location. Normally
|
|
* State would be more passive, but since it behaves almost the
|
|
* same no matter what the argument type is we can simplify and
|
|
* consolidate a little bit by centralizing these methods.
|
|
*/
|
|
|
|
// Return the address of an argument slot and move past it.
|
|
Addr
|
|
getAddr()
|
|
{
|
|
Addr addr = argPointer;
|
|
argPointer += sizeof(Arg);
|
|
return addr;
|
|
}
|
|
|
|
// Read the value in an argument slot and move past it.
|
|
Arg
|
|
get(ThreadContext *tc)
|
|
{
|
|
Arg arg = ArmSemihosting::portProxy(tc).read<Arg>(
|
|
argPointer, endian);
|
|
argPointer += sizeof(Arg);
|
|
return arg;
|
|
}
|
|
|
|
using ArgType = Arg;
|
|
};
|
|
};
|
|
|
|
struct Abi64 : public AbiBase
|
|
{
|
|
using UintPtr = uint64_t;
|
|
|
|
class State : public StateBase<uint64_t>
|
|
{
|
|
public:
|
|
// For 64 bit semihosting, the params are pointer to by X1.
|
|
explicit State(const ThreadContext *tc) :
|
|
StateBase<uint64_t>(tc, tc->getReg(ArmISA::int_reg::X1))
|
|
{}
|
|
};
|
|
};
|
|
|
|
struct Abi32 : public AbiBase
|
|
{
|
|
using UintPtr = uint32_t;
|
|
|
|
class State : public StateBase<uint64_t>
|
|
{
|
|
public:
|
|
// For 32 bit semihosting, the params are pointer to by R1.
|
|
explicit State(const ThreadContext *tc) :
|
|
StateBase<uint64_t>(tc, tc->getReg(ArmISA::int_reg::R1))
|
|
{}
|
|
};
|
|
};
|
|
|
|
// Use this argument type when you need to modify an argument in place.
|
|
// This will give you the address of the argument itself and the size of
|
|
// each argument slot, rather than the actual value of the argument.
|
|
struct InPlaceArg
|
|
{
|
|
Addr addr;
|
|
size_t size;
|
|
|
|
InPlaceArg(Addr _addr, size_t _size) : addr(_addr), size(_size) {}
|
|
|
|
// A helper function to read the argument since the guest ABI mechanism
|
|
// didn't do that for us.
|
|
uint64_t
|
|
read(ThreadContext *tc, ByteOrder endian)
|
|
{
|
|
auto &proxy = ArmSemihosting::portProxy(tc);
|
|
if (size == 8)
|
|
return proxy.read<uint64_t>(addr, endian);
|
|
else if (size == 4)
|
|
return proxy.read<uint32_t>(addr, endian);
|
|
else
|
|
panic("Unexpected semihosting argument size %d.", size);
|
|
}
|
|
|
|
// A helper function to write to the argument's slot in the params.
|
|
void
|
|
write(ThreadContext *tc, uint64_t val, ByteOrder endian)
|
|
{
|
|
auto &proxy = ArmSemihosting::portProxy(tc);
|
|
if (size == 8)
|
|
proxy.write<uint64_t>(addr, val, endian);
|
|
else if (size == 4)
|
|
proxy.write<uint32_t>(addr, val, endian);
|
|
else
|
|
panic("Unexpected semihosting argument size %d.", size);
|
|
}
|
|
};
|
|
|
|
enum Operation
|
|
{
|
|
SYS_OPEN = 0x01,
|
|
SYS_CLOSE = 0x02,
|
|
SYS_WRITEC = 0x03,
|
|
SYS_WRITE0 = 0x04,
|
|
SYS_WRITE = 0x05,
|
|
SYS_READ = 0x06,
|
|
SYS_READC = 0x07,
|
|
SYS_ISERROR = 0x08,
|
|
SYS_ISTTY = 0x09,
|
|
SYS_SEEK = 0x0A,
|
|
SYS_FLEN = 0x0C,
|
|
SYS_TMPNAM = 0x0D,
|
|
SYS_REMOVE = 0x0E,
|
|
SYS_RENAME = 0x0F,
|
|
SYS_CLOCK = 0x10,
|
|
SYS_TIME = 0x11,
|
|
SYS_SYSTEM = 0x12,
|
|
SYS_ERRNO = 0x13,
|
|
SYS_GET_CMDLINE = 0x15,
|
|
SYS_HEAPINFO = 0x16,
|
|
SYS_EXIT = 0x18,
|
|
SYS_EXIT_EXTENDED = 0x20,
|
|
SYS_ELAPSED = 0x30,
|
|
SYS_TICKFREQ = 0x31,
|
|
|
|
MaxStandardOp = 0xFF,
|
|
|
|
SYS_GEM5_PSEUDO_OP = 0x100
|
|
};
|
|
|
|
ArmSemihosting(const ArmSemihostingParams &p);
|
|
|
|
/** Perform an Arm Semihosting call from aarch64 code. */
|
|
bool call64(ThreadContext *tc, bool gem5_ops);
|
|
/** Perform an Arm Semihosting call from aarch32 code. */
|
|
bool call32(ThreadContext *tc, bool gem5_ops);
|
|
|
|
public: // SimObject and related interfaces
|
|
void serialize(CheckpointOut &cp) const override;
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
protected: // Configuration
|
|
const std::string cmdLine;
|
|
const Addr memReserve;
|
|
const Addr stackSize;
|
|
|
|
/**
|
|
* Base time when the simulation started. This is used to
|
|
* calculate the time of date when the guest call SYS_TIME.
|
|
*/
|
|
const time_t timeBase;
|
|
|
|
/** Number of bits to right shift gem5 ticks to fit in a uint32_t */
|
|
const unsigned tickShift;
|
|
|
|
protected: // Internal state
|
|
typedef uint64_t SemiErrno;
|
|
SemiErrno semiErrno;
|
|
|
|
protected: // File IO
|
|
/**
|
|
* Internal state for open files
|
|
*
|
|
* This class describes the internal state of a file opened
|
|
* through the semihosting interface.
|
|
*
|
|
* A file instance is normally created using one of the
|
|
* ArmSemihosting::FileBase::create() factory methods. These
|
|
* methods handle some the magic file names in the Arm Semihosting
|
|
* specification and instantiate the right implementation. For the
|
|
* same, when unserializing a checkpoint, the create method must
|
|
* be used to unserialize a new instance of a file descriptor.
|
|
*/
|
|
class FileBase : public Serializable
|
|
{
|
|
public:
|
|
FileBase(ArmSemihosting &_parent, const char *name, const char *_mode)
|
|
: parent(_parent), _name(name), mode(_mode) {}
|
|
virtual ~FileBase() {};
|
|
|
|
FileBase() = delete;
|
|
FileBase(FileBase &) = delete;
|
|
|
|
static std::unique_ptr<FileBase> create(
|
|
ArmSemihosting &parent, const std::string &fname,
|
|
const char *mode);
|
|
static std::unique_ptr<FileBase> create(
|
|
ArmSemihosting &parent, CheckpointIn &cp, const std::string &sec);
|
|
|
|
void serialize(CheckpointOut &cp) const override;
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
const std::string &fileName() { return _name; }
|
|
|
|
public:
|
|
/** @{
|
|
* Semihosting file IO interfaces
|
|
*
|
|
* These interfaces implement common IO functionality in the
|
|
* Semihosting interface.
|
|
*
|
|
* All functions return a negative value that corresponds to a
|
|
* UNIX errno value when they fail and >=0 on success.
|
|
*/
|
|
|
|
/**
|
|
* Open the the file.
|
|
*
|
|
* @return <0 on error (-errno), 0 on success.
|
|
*/
|
|
virtual int64_t open() { return 0; }
|
|
|
|
/**
|
|
* Close the file.
|
|
*
|
|
* @return <0 on error (-errno), 0 on success.
|
|
*/
|
|
virtual int64_t close() { return 0; }
|
|
|
|
/**
|
|
* Check if a file corresponds to a TTY device.
|
|
*
|
|
* @return True if the file is a TTY, false otherwise.
|
|
*/
|
|
virtual bool isTTY() const { return false; }
|
|
|
|
/**
|
|
* Read data from file.
|
|
*
|
|
* @return <0 on error (-errno), bytes read on success (0 for EOF).
|
|
*/
|
|
virtual int64_t read(uint8_t *buffer, uint64_t size);
|
|
|
|
/**
|
|
* Write data to file.
|
|
*
|
|
* @return <0 on error (-errno), bytes written on success.
|
|
*/
|
|
virtual int64_t write(const uint8_t *buffer, uint64_t size);
|
|
|
|
/**
|
|
* Seek to an absolute position in the file.
|
|
*
|
|
* @param pos Byte offset from start of file.
|
|
* @return <0 on error (-errno), 0 on success.
|
|
*/
|
|
virtual int64_t seek(uint64_t pos);
|
|
|
|
/**
|
|
* Get the length of a file in bytes.
|
|
*
|
|
* @return <0 on error (-errno), length on success
|
|
*/
|
|
virtual int64_t flen();
|
|
|
|
/** @} */
|
|
|
|
protected:
|
|
ArmSemihosting &parent;
|
|
std::string _name;
|
|
std::string mode;
|
|
};
|
|
|
|
/** Implementation of the ':semihosting-features' magic file. */
|
|
class FileFeatures : public FileBase
|
|
{
|
|
public:
|
|
FileFeatures(ArmSemihosting &_parent,
|
|
const char *name, const char *mode);
|
|
|
|
void serialize(CheckpointOut &cp) const override;
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
int64_t read(uint8_t *buffer, uint64_t size) override;
|
|
int64_t seek(uint64_t pos) override;
|
|
|
|
protected:
|
|
size_t pos;
|
|
};
|
|
|
|
class File : public FileBase
|
|
{
|
|
public:
|
|
File(ArmSemihosting &_parent, const char *name, const char *mode);
|
|
~File();
|
|
|
|
void serialize(CheckpointOut &cp) const override;
|
|
void unserialize(CheckpointIn &cp) override;
|
|
|
|
int64_t open() override { return openImpl(false); }
|
|
int64_t close() override;
|
|
bool isTTY() const override;
|
|
int64_t read(uint8_t *buffer, uint64_t size) override;
|
|
int64_t write(const uint8_t *buffer, uint64_t size) override;
|
|
int64_t seek(uint64_t pos) override;
|
|
int64_t flen() override;
|
|
|
|
protected:
|
|
int64_t openImpl(bool unserialize);
|
|
bool needClose() const { return !isTTY(); }
|
|
|
|
FILE *file;
|
|
};
|
|
|
|
std::string filesRootDir;
|
|
std::vector<std::unique_ptr<FileBase>> files;
|
|
using Handle = size_t;
|
|
FILE *stdin;
|
|
FILE *stdout;
|
|
FILE *stderr;
|
|
|
|
protected: // Helper functions
|
|
unsigned
|
|
calcTickShift() const
|
|
{
|
|
int msb = findMsbSet(sim_clock::Frequency);
|
|
return msb > 31 ? msb - 31 : 0;
|
|
}
|
|
uint64_t
|
|
semiTick(Tick tick) const
|
|
{
|
|
return tick >> tickShift;
|
|
}
|
|
void semiExit(uint64_t code, uint64_t subcode);
|
|
std::string readString(ThreadContext *tc, Addr ptr, size_t len);
|
|
|
|
public:
|
|
typedef std::pair<uint64_t, SemiErrno> RetErrno;
|
|
|
|
private:
|
|
static RetErrno
|
|
retError(SemiErrno e)
|
|
{
|
|
return RetErrno((uint64_t)-1, e);
|
|
}
|
|
|
|
static RetErrno
|
|
retOK(uint64_t r)
|
|
{
|
|
return RetErrno(r, 0);
|
|
}
|
|
|
|
/**
|
|
* Semihosting call information structure.
|
|
*
|
|
* This structure describes how a semi-hosting call is
|
|
* implemented. It contains debug information (e.g., the name of
|
|
* the call), and a way to invoke it in a particular context.
|
|
*/
|
|
struct SemiCall
|
|
{
|
|
/** Call name */
|
|
const char *name;
|
|
|
|
// A type for member functions implementing semihosting calls.
|
|
template <typename ...Args>
|
|
using Implementation =
|
|
RetErrno (ArmSemihosting::*)(ThreadContext *tc, Args... args);
|
|
|
|
// Since guest ABI doesn't know how to call member function pointers,
|
|
// this template builds a wrapper that takes care of that.
|
|
template <typename ...Args>
|
|
static inline std::function<RetErrno(ThreadContext *tc, Args... args)>
|
|
wrapImpl(ArmSemihosting *sh, Implementation<Args...> impl)
|
|
{
|
|
return [sh, impl](ThreadContext *tc, Args... args) {
|
|
return (sh->*impl)(tc, args...);
|
|
};
|
|
}
|
|
|
|
// A type for functions which dispatch semihosting calls through the
|
|
// guest ABI mechanism.
|
|
using Dispatcher =
|
|
std::function<RetErrno(ArmSemihosting *sh, ThreadContext *tc)>;
|
|
using Dumper = std::function<std::string(ThreadContext *tc)>;
|
|
|
|
// Dispatchers for 32 and 64 bits.
|
|
Dispatcher call32;
|
|
Dispatcher call64;
|
|
|
|
// Dumpers which print semihosting calls and their arguments.
|
|
Dumper dump32;
|
|
Dumper dump64;
|
|
|
|
// A function which builds a dispatcher for a semihosting call.
|
|
template <typename Abi, typename ...Args>
|
|
static inline Dispatcher
|
|
buildDispatcher(Implementation<Args...> impl)
|
|
{
|
|
// This lambda is the dispatcher we're building.
|
|
return [impl](ArmSemihosting *sh, ThreadContext *tc) {
|
|
auto wrapper = wrapImpl(sh, impl);
|
|
return invokeSimcall<Abi>(tc, wrapper);
|
|
};
|
|
}
|
|
|
|
// A function which builds a dumper for a semihosting call.
|
|
template <typename Abi, typename ...Args>
|
|
static inline Dumper
|
|
buildDumper(const char *name, Implementation<Args...> impl)
|
|
{
|
|
// This lambda is the dumper we're building.
|
|
return [name](ThreadContext *tc) -> std::string {
|
|
return dumpSimcall<Abi, RetErrno, Args...>(name, tc);
|
|
};
|
|
}
|
|
|
|
// When there's one implementation, use it for both 32 and 64 bits.
|
|
template <typename ...Args>
|
|
SemiCall(const char *_name, Implementation<Args...> common) :
|
|
name(_name), call32(buildDispatcher<Abi32>(common)),
|
|
call64(buildDispatcher<Abi64>(common)),
|
|
dump32(buildDumper<Abi32>(_name, common)),
|
|
dump64(buildDumper<Abi64>(_name, common))
|
|
{}
|
|
|
|
// When there are two, use one for 32 bits and one for 64 bits.
|
|
template <typename ...Args32, typename ...Args64>
|
|
SemiCall(const char *_name, Implementation<Args32...> impl32,
|
|
Implementation<Args64...> impl64) :
|
|
name(_name), call32(buildDispatcher<Abi32>(impl32)),
|
|
call64(buildDispatcher<Abi64>(impl64)),
|
|
dump32(buildDumper<Abi32>(_name, impl32)),
|
|
dump64(buildDumper<Abi64>(_name, impl64))
|
|
{}
|
|
};
|
|
|
|
RetErrno callOpen(ThreadContext *tc, const Addr name_base,
|
|
int fmode, size_t name_size);
|
|
RetErrno callClose(ThreadContext *tc, Handle handle);
|
|
RetErrno callWriteC(ThreadContext *tc, InPlaceArg c);
|
|
RetErrno callWrite0(ThreadContext *tc, InPlaceArg str);
|
|
RetErrno callWrite(ThreadContext *tc, Handle handle,
|
|
Addr buffer, size_t size);
|
|
RetErrno callRead(ThreadContext *tc, Handle handle,
|
|
Addr buffer, size_t size);
|
|
RetErrno callReadC(ThreadContext *tc);
|
|
RetErrno callIsError(ThreadContext *tc, int64_t status);
|
|
RetErrno callIsTTY(ThreadContext *tc, Handle handle);
|
|
RetErrno callSeek(ThreadContext *tc, Handle handle, uint64_t pos);
|
|
RetErrno callFLen(ThreadContext *tc, Handle handle);
|
|
RetErrno callTmpNam(ThreadContext *tc, Addr buffer,
|
|
uint64_t id, size_t size);
|
|
RetErrno callRemove(ThreadContext *tc, Addr name_base, size_t name_size);
|
|
RetErrno callRename(ThreadContext *tc, Addr from_addr, size_t from_size,
|
|
Addr to_addr, size_t to_size);
|
|
RetErrno callClock(ThreadContext *tc);
|
|
RetErrno callTime(ThreadContext *tc);
|
|
RetErrno callSystem(ThreadContext *tc, Addr cmd_addr, size_t cmd_size);
|
|
RetErrno callErrno(ThreadContext *tc);
|
|
RetErrno callGetCmdLine(ThreadContext *tc, Addr addr, InPlaceArg size_arg);
|
|
|
|
void gatherHeapInfo(ThreadContext *tc, bool aarch64,
|
|
Addr &heap_base, Addr &heap_limit,
|
|
Addr &stack_base, Addr &stack_limit);
|
|
RetErrno callHeapInfo32(ThreadContext *tc, Addr block_addr);
|
|
RetErrno callHeapInfo64(ThreadContext *tc, Addr block_addr);
|
|
RetErrno callExit32(ThreadContext *tc, InPlaceArg code);
|
|
RetErrno callExit64(ThreadContext *tc, uint64_t code, uint64_t subcode);
|
|
RetErrno callExitExtended(ThreadContext *tc, uint64_t code,
|
|
uint64_t subcode);
|
|
|
|
RetErrno callElapsed32(ThreadContext *tc, InPlaceArg low, InPlaceArg high);
|
|
RetErrno callElapsed64(ThreadContext *tc, InPlaceArg ticks);
|
|
RetErrno callTickFreq(ThreadContext *tc);
|
|
|
|
RetErrno callGem5PseudoOp32(ThreadContext *tc, uint32_t encoded_func);
|
|
RetErrno callGem5PseudoOp64(ThreadContext *tc, uint64_t encoded_func);
|
|
|
|
template <typename Abi>
|
|
void
|
|
unrecognizedCall(ThreadContext *tc, const char *format, uint64_t op)
|
|
{
|
|
warn(format, op);
|
|
std::function<RetErrno(ThreadContext *tc)> retErr =
|
|
[](ThreadContext *tc) { return retError(EINVAL); };
|
|
invokeSimcall<Abi>(tc, retErr);
|
|
}
|
|
|
|
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;
|
|
|
|
// used in callTmpNam() to deterministically generate a temp filename
|
|
uint16_t tmpNameIndex = 0;
|
|
|
|
};
|
|
|
|
std::ostream &operator << (
|
|
std::ostream &os, const ArmSemihosting::InPlaceArg &ipa);
|
|
|
|
GEM5_DEPRECATED_NAMESPACE(GuestABI, guest_abi);
|
|
namespace guest_abi
|
|
{
|
|
|
|
template <typename Arg>
|
|
struct Argument<ArmSemihosting::Abi64, Arg,
|
|
typename std::enable_if_t<std::is_integral_v<Arg>>>
|
|
{
|
|
static Arg
|
|
get(ThreadContext *tc, ArmSemihosting::Abi64::State &state)
|
|
{
|
|
return state.get(tc);
|
|
}
|
|
};
|
|
|
|
template <typename Arg>
|
|
struct Argument<ArmSemihosting::Abi32, Arg,
|
|
typename std::enable_if_t<std::is_integral_v<Arg>>>
|
|
{
|
|
static Arg
|
|
get(ThreadContext *tc, ArmSemihosting::Abi32::State &state)
|
|
{
|
|
if (std::is_signed_v<Arg>)
|
|
return sext<32>(state.get(tc));
|
|
else
|
|
return state.get(tc);
|
|
}
|
|
};
|
|
|
|
template <typename Abi>
|
|
struct Argument<Abi, ArmSemihosting::InPlaceArg, typename std::enable_if_t<
|
|
std::is_base_of_v<ArmSemihosting::AbiBase, Abi>>>
|
|
{
|
|
static ArmSemihosting::InPlaceArg
|
|
get(ThreadContext *tc, typename Abi::State &state)
|
|
{
|
|
return ArmSemihosting::InPlaceArg(
|
|
state.getAddr(), sizeof(typename Abi::State::ArgType));
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct Result<ArmSemihosting::Abi32, ArmSemihosting::RetErrno>
|
|
{
|
|
static void
|
|
store(ThreadContext *tc, const ArmSemihosting::RetErrno &err)
|
|
{
|
|
tc->setReg(ArmISA::int_reg::R0, err.first);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct Result<ArmSemihosting::Abi64, ArmSemihosting::RetErrno>
|
|
{
|
|
static void
|
|
store(ThreadContext *tc, const ArmSemihosting::RetErrno &err)
|
|
{
|
|
tc->setReg(ArmISA::int_reg::X0, err.first);
|
|
}
|
|
};
|
|
|
|
} // namespace guest_abi
|
|
} // namespace gem5
|
|
|
|
#endif // __ARCH_ARM_SEMIHOSTING_HH__
|