#include "sim/system.hh"
const std::map<uint32_t, ArmSemihosting::SemiCall> ArmSemihosting::calls{
- { SYS_OPEN, { "SYS_OPEN", &ArmSemihosting::callOpen, 3, 3 } },
- { SYS_CLOSE, { "SYS_CLOSE", &ArmSemihosting::callClose, 1, 1 } },
-
- // Write(C|0) are special since we want to read the character
- // manually. We therefore declare them as having 0 params.
- { SYS_WRITEC, { "SYS_WRITEC", &ArmSemihosting::callWriteC, 0, 0 } },
- { SYS_WRITE0, { "SYS_WRITE0", &ArmSemihosting::callWrite0, 1, 1 } },
-
- { SYS_WRITE, { "SYS_WRITE", &ArmSemihosting::callWrite, 3, 3 } },
- { SYS_READ, { "SYS_READ", &ArmSemihosting::callRead, 3, 3 } },
- { SYS_READC, { "SYS_READC", &ArmSemihosting::callReadC, 0, 0 } },
- { SYS_ISERROR, { "SYS_ISERROR", &ArmSemihosting::callIsError, 1, 1 } },
- { SYS_ISTTY, { "SYS_ISTTY", &ArmSemihosting::callIsTTY, 1, 1 } },
- { SYS_SEEK, { "SYS_SEEK", &ArmSemihosting::callSeek, 2, 2 } },
- { SYS_FLEN, { "SYS_FLEN", &ArmSemihosting::callFLen, 1, 1 } },
- { SYS_TMPNAM, { "SYS_TMPNAM", &ArmSemihosting::callTmpNam, 3, 3 } },
- { SYS_REMOVE, { "SYS_REMOVE", &ArmSemihosting::callRemove, 2, 2} },
- { SYS_RENAME, { "SYS_RENAME", &ArmSemihosting::callRename, 4, 4} },
- { SYS_CLOCK, { "SYS_CLOCK", &ArmSemihosting::callClock, 0, 0} },
- { SYS_TIME, { "SYS_TIME", &ArmSemihosting::callTime, 0, 0} },
- { SYS_SYSTEM, { "SYS_SYSTEM", &ArmSemihosting::callSystem, 2, 2} },
- { SYS_ERRNO, { "SYS_ERRNO", &ArmSemihosting::callErrno, 0, 0 } },
+ { SYS_OPEN, { "SYS_OPEN", &ArmSemihosting::callOpen } },
+ { SYS_CLOSE, { "SYS_CLOSE", &ArmSemihosting::callClose } },
+ { SYS_WRITEC, { "SYS_WRITEC", &ArmSemihosting::callWriteC } },
+ { SYS_WRITE0, { "SYS_WRITE0", &ArmSemihosting::callWrite0 } },
+ { SYS_WRITE, { "SYS_WRITE", &ArmSemihosting::callWrite } },
+ { SYS_READ, { "SYS_READ", &ArmSemihosting::callRead } },
+ { SYS_READC, { "SYS_READC", &ArmSemihosting::callReadC } },
+ { SYS_ISERROR, { "SYS_ISERROR", &ArmSemihosting::callIsError } },
+ { SYS_ISTTY, { "SYS_ISTTY", &ArmSemihosting::callIsTTY } },
+ { SYS_SEEK, { "SYS_SEEK", &ArmSemihosting::callSeek } },
+ { SYS_FLEN, { "SYS_FLEN", &ArmSemihosting::callFLen } },
+ { SYS_TMPNAM, { "SYS_TMPNAM", &ArmSemihosting::callTmpNam } },
+ { SYS_REMOVE, { "SYS_REMOVE", &ArmSemihosting::callRemove } },
+ { SYS_RENAME, { "SYS_RENAME", &ArmSemihosting::callRename } },
+ { SYS_CLOCK, { "SYS_CLOCK", &ArmSemihosting::callClock } },
+ { SYS_TIME, { "SYS_TIME", &ArmSemihosting::callTime } },
+ { SYS_SYSTEM, { "SYS_SYSTEM", &ArmSemihosting::callSystem } },
+ { SYS_ERRNO, { "SYS_ERRNO", &ArmSemihosting::callErrno } },
{ SYS_GET_CMDLINE,
- { "SYS_GET_CMDLINE", &ArmSemihosting::callGetCmdLine, 2, 2} },
- { SYS_HEAPINFO, { "SYS_HEAPINFO", &ArmSemihosting::callHeapInfo, 1, 1} },
+ { "SYS_GET_CMDLINE", &ArmSemihosting::callGetCmdLine } },
+ { SYS_HEAPINFO, { "SYS_HEAPINFO", &ArmSemihosting::callHeapInfo32,
+ &ArmSemihosting::callHeapInfo64 } },
- // Exit is special and requires custom handling in aarch32.
- { SYS_EXIT, { "SYS_EXIT", &ArmSemihosting::callExit, 0, 2 } },
+ { SYS_EXIT, { "SYS_EXIT", &ArmSemihosting::callExit32,
+ &ArmSemihosting::callExit64} },
{ SYS_EXIT_EXTENDED,
- { "SYS_EXIT_EXTENDED", &ArmSemihosting::callExitExtended, 2, 2 } },
+ { "SYS_EXIT_EXTENDED", &ArmSemihosting::callExitExtended } },
- { SYS_ELAPSED, { "SYS_ELAPSED", &ArmSemihosting::callElapsed, 0, 0 } },
- { SYS_TICKFREQ, { "SYS_TICKFREQ", &ArmSemihosting::callTickFreq, 0, 0 } },
+ { SYS_ELAPSED, { "SYS_ELAPSED", &ArmSemihosting::callElapsed32,
+ &ArmSemihosting::callElapsed64 } },
+ { SYS_TICKFREQ, { "SYS_TICKFREQ", &ArmSemihosting::callTickFreq } },
};
const std::vector<const char *> ArmSemihosting::fmodes{
tickShift);
}
-uint64_t
-ArmSemihosting::call64(ThreadContext *tc, uint32_t op, uint64_t param)
+void
+ArmSemihosting::call64(ThreadContext *tc)
{
- const SemiCall *call = getCall(op, true);
- if (!call) {
- warn("Unknown aarch64 semihosting call: op = 0x%x, param = 0x%x",
- op, param);
-
- return (uint64_t)-1;
- } else if (!call->implemented64()) {
- warn("Unimplemented aarch64 semihosting call: "
- "%s (op = 0x%x, param = 0x%x)",
- call->name, op, param);
-
- return (uint64_t)-1;
- }
+ RegVal op = tc->readIntReg(ArmISA::INTREG_X0 & mask(32));
- std::vector<uint64_t> argv(call->argc64 + 1);
- PortProxy &proxy = physProxy(tc);
- ByteOrder endian = ArmISA::byteOrder(tc);
-
- DPRINTF(Semihosting, "Semihosting call64: %s(0x%x)\n", call->name, param);
- argv[0] = param;
- for (int i = 0; i < call->argc64; ++i) {
- argv[i + 1] = proxy.read<uint64_t>(param + i * 8, endian);
- DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]);
+ auto it = calls.find(op);
+ if (it == calls.end()) {
+ unrecognizedCall<Abi64>(
+ tc, "Unknown aarch64 semihosting call: op = 0x%x", op);
+ return;
}
+ const SemiCall &call = it->second;
- auto ret_errno = (this->*call->call)(tc, true, argv);
- semiErrno = ret_errno.second;
- DPRINTF(Semihosting, "\t ->: 0x%x, %i\n",
- ret_errno.first, ret_errno.second);
- return ret_errno.first;
+ DPRINTF(Semihosting, "Semihosting call64: %s\n", call.dump64(tc));
+ auto err = call.call64(this, tc);
+ semiErrno = err.second;
+ DPRINTF(Semihosting, "\t ->: 0x%x, %i\n", err.first, err.second);
}
-uint32_t
-ArmSemihosting::call32(ThreadContext *tc, uint32_t op, uint32_t param)
+void
+ArmSemihosting::call32(ThreadContext *tc)
{
- const SemiCall *call = getCall(op, false);
- if (!call) {
- warn("Unknown aarch32 semihosting call: op = 0x%x, param = 0x%x",
- op, param);
-
- return (uint32_t)-1;
- } else if (!call->implemented32()) {
- warn("Unimplemented aarch32 semihosting call: "
- "%s (op = 0x%x, param = 0x%x)",
- call->name, op, param);
-
- return (uint32_t)-1;
- }
+ RegVal op = tc->readIntReg(ArmISA::INTREG_R0);
- std::vector<uint64_t> argv(call->argc32 + 1);
- PortProxy &proxy = physProxy(tc);
- ByteOrder endian = ArmISA::byteOrder(tc);
-
- DPRINTF(Semihosting, "Semihosting call32: %s(0x%x)\n", call->name, param);
- argv[0] = param;
- for (int i = 0; i < call->argc32; ++i) {
- argv[i + 1] = proxy.read<uint32_t>(param + i * 4, endian);
- DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]);
+ auto it = calls.find(op);
+ if (it == calls.end()) {
+ unrecognizedCall<Abi32>(
+ tc, "Unknown aarch32 semihosting call: op = 0x%x", op);
+ return;
}
+ const SemiCall &call = it->second;
- auto ret_errno = (this->*call->call)(tc, false, argv);
- semiErrno = ret_errno.second;
- DPRINTF(Semihosting, "\t ->: 0x%x, %i\n",
- ret_errno.first, ret_errno.second);
- return ret_errno.first;
+ DPRINTF(Semihosting, "Semihosting call32: %s\n", call.dump32(tc));
+ auto err = call.call32(this, tc);
+ semiErrno = err.second;
+ DPRINTF(Semihosting, "\t ->: 0x%x, %i\n", err.first, err.second);
}
void
PortProxy &
ArmSemihosting::physProxy(ThreadContext *tc)
{
+ static std::unique_ptr<PortProxy> phys_proxy_s;
+ static System *secure_sys = nullptr;
+
if (ArmISA::inSecureState(tc)) {
- if (!physProxyS) {
- System *sys = tc->getSystemPtr();
- physProxyS.reset(new SecurePortProxy(
- sys->getSystemPort(),
- sys->cacheLineSize()));
+ System *sys = tc->getSystemPtr();
+ if (sys != secure_sys) {
+ phys_proxy_s.reset(new SecurePortProxy(sys->getSystemPort(),
+ sys->cacheLineSize()));
}
- return *physProxyS;
+ secure_sys = sys;
+ return *phys_proxy_s;
} else {
return tc->getPhysProxy();
}
}
ArmSemihosting::RetErrno
-ArmSemihosting::callOpen(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callOpen(ThreadContext *tc, const Addr name_base,
+ int fmode, size_t name_size)
{
- const Addr name_base = argv[1];
- const char *mode = argv[2] < fmodes.size() ? fmodes[argv[2]] : nullptr;
- const Addr name_size = argv[3];
+ const char *mode = fmode < fmodes.size() ? fmodes[fmode] : nullptr;
DPRINTF(Semihosting, "Semihosting SYS_OPEN(0x%x, %i[%s], %i)\n",
- name_base, argv[2], mode ? mode : "-", name_size);
+ name_base, fmode, mode ? mode : "-", name_size);
if (!mode || !name_base)
return retError(EINVAL);
FileBase::create(*this, fname, mode);
int64_t ret = file->open();
DPRINTF(Semihosting, "Semihosting SYS_OPEN(\"%s\", %i[%s]): %i\n",
- fname, argv[2], mode, ret);
+ fname, fmode, mode, ret);
if (ret < 0) {
return retError(-ret);
} else {
}
ArmSemihosting::RetErrno
-ArmSemihosting::callClose(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callClose(ThreadContext *tc, uint64_t handle)
{
- if (argv[1] > files.size()) {
+ if (handle > files.size()) {
DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i): Illegal file\n");
return retError(EBADF);
}
- std::unique_ptr<FileBase> &file = files[argv[1]];
+ std::unique_ptr<FileBase> &file = files[handle];
int64_t error = file->close();
DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i[%s]): %i\n",
- argv[1], file->fileName(), error);
+ handle, file->fileName(), error);
if (error < 0) {
return retError(-error);
} else {
// Zap the pointer and free the entry in the file table as
// well.
- files[argv[1]].reset();
+ files[handle].reset();
return retOK(0);
}
}
ArmSemihosting::RetErrno
-ArmSemihosting::callWriteC(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callWriteC(ThreadContext *tc, InPlaceArg arg)
{
- const char c = physProxy(tc).read<char>(argv[0]);
+ const char c = physProxy(tc).read<char>(arg.addr);
DPRINTF(Semihosting, "Semihosting SYS_WRITEC('%c')\n", c);
std::cout.put(c);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callWrite0(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callWrite0(ThreadContext *tc, InPlaceArg arg)
{
DPRINTF(Semihosting, "Semihosting SYS_WRITE0(...)\n");
PortProxy &proxy = physProxy(tc);
- for (Addr addr = (Addr)argv[0]; ; ++addr) {
- char data = proxy.read<char>(addr);
- if (data == 0)
- break;
-
- std::cout.put(data);
- }
+ std::string str;
+ proxy.readString(str, arg.addr);
+ std::cout.write(str.c_str(), str.size());
return retOK(0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callWrite(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callWrite(ThreadContext *tc, uint64_t handle, Addr addr,
+ size_t size)
{
- if (argv[1] > files.size() || !files[argv[1]])
- return RetErrno(argv[3], EBADF);
+ if (handle > files.size() || !files[handle])
+ return RetErrno(size, EBADF);
- std::vector<uint8_t> buffer(argv[3]);
- physProxy(tc).readBlob(argv[2], buffer.data(), buffer.size());
+ std::vector<uint8_t> buffer(size);
+ physProxy(tc).readBlob(addr, buffer.data(), buffer.size());
- int64_t ret = files[argv[1]]->write(buffer.data(), buffer.size());
+ int64_t ret = files[handle]->write(buffer.data(), buffer.size());
if (ret < 0) {
// No bytes written (we're returning the number of bytes not
// written)
- return RetErrno(argv[3], -ret);
+ return RetErrno(size, -ret);
} else {
// Return the number of bytes not written
- return RetErrno(argv[3] - ret, 0);
+ return RetErrno(size - ret, 0);
}
}
ArmSemihosting::RetErrno
-ArmSemihosting::callRead(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callRead(ThreadContext *tc, uint64_t handle, Addr addr,
+ size_t size)
{
- if (argv[1] > files.size() || !files[argv[1]])
- return RetErrno(argv[3], EBADF);
+ if (handle > files.size() || !files[handle])
+ return RetErrno(size, EBADF);
- std::vector<uint8_t> buffer(argv[3]);
- int64_t ret = files[argv[1]]->read(buffer.data(), buffer.size());
+ std::vector<uint8_t> buffer(size);
+ int64_t ret = files[handle]->read(buffer.data(), buffer.size());
if (ret < 0) {
- return RetErrno(argv[3], -ret);
+ return RetErrno(size, -ret);
} else {
panic_if(ret > buffer.size(), "Read longer than buffer size.");
- physProxy(tc).writeBlob(argv[2], buffer.data(), ret);
+ physProxy(tc).writeBlob(addr, buffer.data(), ret);
// Return the number of bytes not written
- return retOK(argv[3] - ret);
+ return retOK(size - ret);
}
}
ArmSemihosting::RetErrno
-ArmSemihosting::callReadC(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callReadC(ThreadContext *tc)
{
return retOK((char)std::cin.get());
}
ArmSemihosting::RetErrno
-ArmSemihosting::callIsError(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callIsError(ThreadContext *tc, int64_t status)
{
- // Sign extend from a 32 bit integer in aarch32 since the argument
- // reader zero extends to a uint64_t.
- const int64_t status = (int64_t)(aarch64 ? argv[1] :sext<32>(argv[1]));
- // Assume there was an error if the status value is negative.
return retOK(status < 0 ? 1 : 0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callIsTTY(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callIsTTY(ThreadContext *tc, uint64_t handle)
{
- if (argv[1] > files.size() || !files[argv[1]])
+ if (handle > files.size() || !files[handle])
return retError(EBADF);
- int64_t ret = files[argv[1]]->isTTY();
+ int64_t ret = files[handle]->isTTY();
if (ret < 0) {
return retError(-ret);
} else {
}
ArmSemihosting::RetErrno
-ArmSemihosting::callSeek(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callSeek(ThreadContext *tc, uint64_t handle, uint64_t pos)
{
- if (argv[1] > files.size() || !files[argv[1]])
+ if (handle > files.size() || !files[handle])
return retError(EBADF);
- int64_t ret = files[argv[1]]->seek(argv[2]);
+ int64_t ret = files[handle]->seek(pos);
if (ret < 0) {
return retError(-ret);
} else {
}
ArmSemihosting::RetErrno
-ArmSemihosting::callFLen(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callFLen(ThreadContext *tc, uint64_t handle)
{
- if (argv[1] > files.size() || !files[argv[1]])
+ if (handle > files.size() || !files[handle])
return retError(EBADF);
- int64_t ret = files[argv[1]]->flen();
+ int64_t ret = files[handle]->flen();
if (ret < 0) {
return retError(-ret);
} else {
}
ArmSemihosting::RetErrno
-ArmSemihosting::callTmpNam(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callTmpNam(ThreadContext *tc, Addr addr, uint64_t id,
+ size_t size)
{
- const Addr guest_buf = argv[1];
- //const uint64_t id = argv[2];
- const uint64_t max_len = argv[3];
-
std::vector<char> buf(L_tmpnam);
char *path = tmpnam(buf.data());
if (!path)
return retError(EINVAL);
const size_t path_len = strlen(path);
- if (path_len >= max_len)
+ if (path_len >= size)
return retError(ENOSPC);
- physProxy(tc).writeBlob(guest_buf, path, path_len + 1);
+ physProxy(tc).writeBlob(addr, path, path_len + 1);
return retOK(0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callRemove(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callRemove(ThreadContext *tc, Addr name_base, size_t name_size)
{
- std::string fname = readString(tc, argv[1], argv[2]);
+ std::string fname = readString(tc, name_base, name_size);
if (remove(fname.c_str()) != 0) {
return retError(errno);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callRename(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callRename(ThreadContext *tc, Addr from_addr, size_t from_size,
+ Addr to_addr, size_t to_size)
{
- std::string from = readString(tc, argv[1], argv[2]);
- std::string to = readString(tc, argv[3], argv[4]);
+ std::string from = readString(tc, from_addr, from_size);
+ std::string to = readString(tc, to_addr, to_size);
if (rename(from.c_str(), to.c_str()) != 0) {
return retError(errno);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callClock(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callClock(ThreadContext *tc)
{
return retOK(curTick() / (SimClock::Int::s / 100));
}
ArmSemihosting::RetErrno
-ArmSemihosting::callTime(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callTime(ThreadContext *tc)
{
return retOK(timeBase + round(curTick() / SimClock::Float::s));
}
ArmSemihosting::RetErrno
-ArmSemihosting::callSystem(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callSystem(ThreadContext *tc, Addr cmd_addr, size_t cmd_size)
{
- const std::string cmd = readString(tc, argv[1], argv[2]);
+ const std::string cmd = readString(tc, cmd_addr, cmd_size);
warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
cmd);
return retError(EINVAL);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callErrno(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callErrno(ThreadContext *tc)
{
// Preserve errno by returning it in errno as well.
return RetErrno(semiErrno, semiErrno);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callGetCmdLine(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
-{
- if (cmdLine.size() + 1 < argv[2]) {
- PortProxy &proxy = physProxy(tc);
- ByteOrder endian = ArmISA::byteOrder(tc);
- proxy.writeBlob((Addr)argv[1], cmdLine.c_str(), cmdLine.size() + 1);
-
- if (aarch64)
- proxy.write<uint64_t>(argv[0] + 1 * 8, cmdLine.size(), endian);
- else
- proxy.write<uint32_t>(argv[0] + 1 * 4, cmdLine.size(), endian);
+ArmSemihosting::callGetCmdLine(ThreadContext *tc, Addr addr,
+ InPlaceArg size_arg)
+{
+ PortProxy &proxy = physProxy(tc);
+ ByteOrder endian = ArmISA::byteOrder(tc);
+ size_t size = size_arg.read(tc, endian);
+
+ if (cmdLine.size() + 1 < size) {
+ proxy.writeBlob(addr, cmdLine.c_str(), cmdLine.size() + 1);
+ size_arg.write(tc, cmdLine.size(), endian);
return retOK(0);
} else {
return retError(0);
}
}
-ArmSemihosting::RetErrno
-ArmSemihosting::callHeapInfo(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+void
+ArmSemihosting::gatherHeapInfo(ThreadContext *tc, bool aarch64,
+ Addr &heap_base, Addr &heap_limit,
+ Addr &stack_base, Addr &stack_limit)
{
const PhysicalMemory &phys = tc->getSystemPtr()->getPhysMem();
const AddrRangeList memories = phys.getConfAddrRanges();
fatal_if(mem_start + stackSize >= mem_end,
"Physical memory too small to fit desired stack and a heap.");
- const Addr heap_base = mem_start;
- const Addr heap_limit = mem_end - stackSize + 1;
- const Addr stack_base = (mem_end + 1) & ~0x7ULL; // 8 byte stack alignment
- const Addr stack_limit = heap_limit;
-
+ heap_base = mem_start;
+ heap_limit = mem_end - stackSize + 1;
+ stack_base = (mem_end + 1) & ~0x7ULL; // 8 byte stack alignment
+ stack_limit = heap_limit;
inform("Reporting heap/stack info to guest:\n"
"\tHeap base: 0x%x\n"
"\tStack base: 0x%x\n"
"\tStack limit: 0x%x\n",
heap_base, heap_limit, stack_base, stack_limit);
+}
- Addr base = argv[1];
- PortProxy &proxy = physProxy(tc);
- ByteOrder endian = ArmISA::byteOrder(tc);
- if (aarch64) {
- proxy.write<uint64_t>(base + 0 * 8, heap_base, endian);
- proxy.write<uint64_t>(base + 1 * 8, heap_limit, endian);
- proxy.write<uint64_t>(base + 2 * 8, stack_base, endian);
- proxy.write<uint64_t>(base + 3 * 8, stack_limit, endian);
- } else {
- proxy.write<uint32_t>(base + 0 * 4, heap_base, endian);
- proxy.write<uint32_t>(base + 1 * 4, heap_limit, endian);
- proxy.write<uint32_t>(base + 2 * 4, stack_base, endian);
- proxy.write<uint32_t>(base + 3 * 4, stack_limit, endian);
- }
+ArmSemihosting::RetErrno
+ArmSemihosting::callHeapInfo32(ThreadContext *tc, Addr block_addr)
+{
+ uint64_t heap_base, heap_limit, stack_base, stack_limit;
+ gatherHeapInfo(tc, false, heap_base, heap_limit, stack_base, stack_limit);
+
+ std::array<uint32_t, 4> block = {
+ (uint32_t)heap_base, (uint32_t)heap_limit,
+ (uint32_t)stack_base, (uint32_t)stack_limit
+ };
+ physProxy(tc).write(block_addr, block, ArmISA::byteOrder(tc));
return retOK(0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callExit(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callHeapInfo64(ThreadContext *tc, Addr block_addr)
{
- if (aarch64) {
- semiExit(argv[1], argv[2]);
- } else {
- semiExit(argv[0], 0);
- }
+ uint64_t heap_base, heap_limit, stack_base, stack_limit;
+ gatherHeapInfo(tc, true, heap_base, heap_limit, stack_base, stack_limit);
+
+ std::array<uint64_t, 4> block = {
+ heap_base, heap_limit, stack_base, stack_limit
+ };
+ physProxy(tc).write(block_addr, block, ArmISA::byteOrder(tc));
return retOK(0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callExitExtended(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callExit32(ThreadContext *tc, InPlaceArg code)
{
- semiExit(argv[1], argv[2]);
+ semiExit(code.addr, 0);
+ return retOK(0);
+}
+
+ArmSemihosting::RetErrno
+ArmSemihosting::callExit64(ThreadContext *tc, uint64_t code, uint64_t subcode)
+{
+ semiExit(code, subcode);
+ return retOK(0);
+}
+ArmSemihosting::RetErrno
+ArmSemihosting::callExitExtended(ThreadContext *tc,
+ uint64_t code, uint64_t subcode)
+{
+ semiExit(code, subcode);
return retOK(0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callElapsed(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callElapsed32(ThreadContext *tc, InPlaceArg low,
+ InPlaceArg high)
{
- PortProxy &proxy = physProxy(tc);
ByteOrder endian = ArmISA::byteOrder(tc);
- const uint64_t tick = semiTick(curTick());
+ uint64_t tick = semiTick(curTick());
- if (aarch64) {
- proxy.write<uint64_t>(argv[0], tick, endian);
- } else {
- proxy.write<uint32_t>(argv[0] + 0 * 4, tick, endian);
- proxy.write<uint32_t>(argv[0] + 1 * 4, tick >> 32, endian);
- }
+ low.write(tc, tick, endian);
+ high.write(tc, tick >> 32, endian);
return retOK(0);
}
ArmSemihosting::RetErrno
-ArmSemihosting::callTickFreq(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv)
+ArmSemihosting::callElapsed64(ThreadContext *tc, InPlaceArg ticks)
{
- return retOK(semiTick(SimClock::Frequency));
+ ticks.write(tc, semiTick(curTick()), ArmISA::byteOrder(tc));
+ return retOK(0);
}
-const ArmSemihosting::SemiCall *
-ArmSemihosting::getCall(uint32_t op, bool aarch64)
+
+ArmSemihosting::RetErrno
+ArmSemihosting::callTickFreq(ThreadContext *tc)
{
- auto it = calls.find(op);
- if (it == calls.end())
- return nullptr;
- else {
- return &it->second;
- }
+ return retOK(semiTick(SimClock::Frequency));
}
FILE *
}
}
+std::ostream &
+operator << (std::ostream &os, const ArmSemihosting::InPlaceArg &ipa)
+{
+ ccprintf(os, "[%#x-%#x)", ipa.addr, ipa.addr + ipa.size - 1);
+ return os;
+}
+
ArmSemihosting *
ArmSemihostingParams::create()
#define __ARCH_ARM_SEMIHOSTING_HH__
#include <cstdio>
+#include <functional>
#include <map>
#include <memory>
#include <utility>
#include <vector>
+#include "arch/arm/intregs.hh"
+#include "arch/arm/utility.hh"
+#include "cpu/thread_context.hh"
+#include "mem/port_proxy.hh"
+#include "sim/guest_abi.hh"
#include "sim/sim_object.hh"
struct ArmSemihostingParams;
-class PortProxy;
class SerialDevice;
-class ThreadContext;
/**
* Semihosting for AArch32 and AArch64
{
public:
+ static PortProxy &physProxy(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::physProxy(tc).read<Arg>(
+ argPointer, endian);
+ argPointer += sizeof(Arg);
+ return arg;
+ }
+
+ using ArgType = Arg;
+ };
+ };
+
+ struct Abi64 : public AbiBase
+ {
+ 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->readIntReg(ArmISA::INTREG_X1))
+ {}
+ };
+ };
+
+ struct Abi32 : public AbiBase
+ {
+ 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->readIntReg(ArmISA::INTREG_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::physProxy(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::physProxy(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,
ArmSemihosting(const ArmSemihostingParams *p);
/** Perform an Arm Semihosting call from aarch64 code. */
- uint64_t call64(ThreadContext *tc, uint32_t op, uint64_t param);
+ void call64(ThreadContext *tc);
/** Perform an Arm Semihosting call from aarch32 code. */
- uint32_t call32(ThreadContext *tc, uint32_t op, uint32_t param);
+ void call32(ThreadContext *tc);
public: // SimObject and related interfaces
void serialize(CheckpointOut &cp) const override;
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 {
+ unsigned
+ calcTickShift() const
+ {
int msb = findMsbSet(SimClock::Frequency);
return msb > 31 ? msb - 31 : 0;
}
- uint64_t semiTick(Tick tick) const {
+ uint64_t
+ semiTick(Tick tick) const
+ {
return tick >> tickShift;
}
void semiExit(uint64_t code, uint64_t subcode);
- PortProxy &physProxy(ThreadContext *tc);
std::string readString(ThreadContext *tc, Addr ptr, size_t len);
- std::unique_ptr<PortProxy> physProxyS;
+ public:
+ typedef std::pair<uint64_t, SemiErrno> RetErrno;
private:
- typedef std::pair<uint64_t, SemiErrno> RetErrno;
- static RetErrno retError(SemiErrno e) {
+ static RetErrno
+ retError(SemiErrno e)
+ {
return RetErrno((uint64_t)-1, e);
}
- static RetErrno retOK(uint64_t r) {
+ static RetErrno
+ retOK(uint64_t r)
+ {
return RetErrno(r, 0);
}
*
* This structure describes how a semi-hosting call is
* implemented. It contains debug information (e.g., the name of
- * the call), a pointer to the implementation, and information
- * needed to read its parameters from guest memory.
+ * the call), and a way to invoke it in a particular context.
*/
struct SemiCall
{
/** Call name */
const char *name;
- /**
- * Pointer to call implementation
- *
- * @param tc ThreadContext pointer for caller
- * @param aarch64 True if in aarc64 mode, false otherwise.
- * @parma argv Argument vector. argv[0] always corresponds to
- * the pointer to the argument list. Remaining
- * entries are read as consecutive words starting
- * at the address pointed to by argv[0].
- * @return a (return value, errno) pair
- */
- RetErrno (ArmSemihosting::*call)(ThreadContext *tc, bool aarch64,
- std::vector<uint64_t> &argv);
-
- /** Number of aarch32 arguments to read from guest memory. -1
- * if unimplemented.*/
- int argc32;
- /** Number of aarch32 arguments to read from guest memory. -1
- * if unimplemented.*/
- int argc64;
-
- /** Is call implemented in aarch32? */
- bool implemented32() const { return call && argc32 >= 0; }
- /** Is call implemented in aarch64? */
- bool implemented64() const { return call && argc64 >= 0; }
+ // 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))
+ {}
};
-#define SEMI_CALL(N) \
- RetErrno call ## N (ThreadContext *tc, \
- bool aarch64, std::vector<uint64_t> &argv)
-
- SEMI_CALL(Open);
- SEMI_CALL(Close);
- SEMI_CALL(WriteC);
- SEMI_CALL(Write0);
- SEMI_CALL(Write);
- SEMI_CALL(Read);
- SEMI_CALL(ReadC);
- SEMI_CALL(IsError);
- SEMI_CALL(IsTTY);
- SEMI_CALL(Seek);
- SEMI_CALL(FLen);
- SEMI_CALL(TmpNam);
- SEMI_CALL(Remove);
- SEMI_CALL(Rename);
- SEMI_CALL(Clock);
- SEMI_CALL(Time);
- SEMI_CALL(System);
- SEMI_CALL(Errno);
- SEMI_CALL(GetCmdLine);
- SEMI_CALL(HeapInfo);
- SEMI_CALL(Exit);
- SEMI_CALL(ExitExtended);
-
- SEMI_CALL(Elapsed);
- SEMI_CALL(TickFreq);
-
-#undef SEMI_CALL
-
- static const SemiCall *getCall(uint32_t op, bool aarch64);
+ 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);
+
+ 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<const std::string, FILE *> stdioMap;
};
+std::ostream &operator << (
+ std::ostream &os, const ArmSemihosting::InPlaceArg &ipa);
+
+namespace GuestABI
+{
+
+template <typename Arg>
+struct Argument<ArmSemihosting::Abi64, Arg,
+ typename std::enable_if<std::is_integral<Arg>::value>::type>
+{
+ 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<std::is_integral<Arg>::value>::type>
+{
+ static Arg
+ get(ThreadContext *tc, ArmSemihosting::Abi32::State &state)
+ {
+ if (std::is_signed<Arg>::value)
+ return sext<32>(state.get(tc));
+ else
+ return state.get(tc);
+ }
+};
+
+template <typename Abi>
+struct Argument<Abi, ArmSemihosting::InPlaceArg, typename std::enable_if<
+ std::is_base_of<ArmSemihosting::AbiBase, Abi>::value>::type>
+{
+ 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->setIntReg(ArmISA::INTREG_R0, err.first);
+ }
+};
+
+template <>
+struct Result<ArmSemihosting::Abi64, ArmSemihosting::RetErrno>
+{
+ static void
+ store(ThreadContext *tc, const ArmSemihosting::RetErrno &err)
+ {
+ tc->setIntReg(ArmISA::INTREG_X0, err.first);
+ }
+};
+
+} // namespace GuestABI
+
#endif // __ARCH_ARM_SEMIHOSTING_HH__