From 695583709b8c09d32f86057e63868bc125f1a86b Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Wed, 26 Feb 2020 04:44:55 -0800 Subject: [PATCH] arm: Make the semihosting implementation use GuestABI. Remove the ability to not have an implementation for a semihosting call in 32 or 64 bit mode since that was not actually being used. It can be reintroduced if needed in the future. Turn the physProxy helper function into a static function which maintains a single secure port proxy. That makes the proxy available outside of the ArmSemihosting class itself. Change-Id: Ie99e7d79c08c039384250fab0c98117554c93128 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/25946 Tested-by: Gem5 Cloud Project GCB service account <345032938727@cloudbuild.gserviceaccount.com> Tested-by: kokoro Reviewed-by: Giacomo Travaglini Maintainer: Giacomo Travaglini --- src/arch/arm/isa/insts/misc.isa | 6 +- src/arch/arm/isa/insts/misc64.isa | 4 +- src/arch/arm/semihosting.cc | 440 +++++++++++++----------------- src/arch/arm/semihosting.hh | 389 +++++++++++++++++++++----- src/arch/arm/system.cc | 16 +- src/arch/arm/system.hh | 6 +- 6 files changed, 526 insertions(+), 335 deletions(-) diff --git a/src/arch/arm/isa/insts/misc.isa b/src/arch/arm/isa/insts/misc.isa index 396930f17..88c473d6a 100644 --- a/src/arch/arm/isa/insts/misc.isa +++ b/src/arch/arm/isa/insts/misc.isa @@ -43,7 +43,7 @@ let {{ const auto semihost_imm = Thumb? 0xAB : 0x123456; if (ArmSystem::haveSemihosting(tc) && imm == semihost_imm) { - R0 = ArmSystem::callSemihosting32(tc, R0, R1); + ArmSystem::callSemihosting32(tc); } else { fault = std::make_shared(machInst, imm); } @@ -66,7 +66,7 @@ let {{ const auto semihost_imm = Thumb? 0x3C : 0xF000; if (ArmSystem::haveSemihosting(tc) && imm == semihost_imm) { - R0 = ArmSystem::callSemihosting32(tc, R0, R1); + ArmSystem::callSemihosting32(tc); } else { // HLT instructions aren't implemented, so treat them as undefined // instructions. @@ -80,7 +80,7 @@ let {{ "predicate_test": predicateTest, "thumb_semihost": '0x3C', "arm_semihost": '0xF000' }, - ["IsNonSpeculative"]) + ["IsNonSpeculative", "IsSerializeAfter"]) header_output += ImmOpDeclare.subst(hltIop) decoder_output += SemihostConstructor.subst(hltIop) exec_output += PredOpExecute.subst(hltIop) diff --git a/src/arch/arm/isa/insts/misc64.isa b/src/arch/arm/isa/insts/misc64.isa index 88c68e623..e2cfb41dd 100644 --- a/src/arch/arm/isa/insts/misc64.isa +++ b/src/arch/arm/isa/insts/misc64.isa @@ -186,7 +186,7 @@ let {{ hltCode = ''' ThreadContext *tc = xc->tcBase(); if (ArmSystem::haveSemihosting(tc) && imm == 0xF000) { - X0 = ArmSystem::callSemihosting64(tc, X0 & mask(32), X1); + ArmSystem::callSemihosting64(tc); } else { // HLT instructions aren't implemented, so treat them as undefined // instructions. @@ -197,7 +197,7 @@ let {{ ''' hltIop = InstObjParams("hlt", "Hlt64", "ImmOp64", - hltCode, ["IsNonSpeculative"]) + hltCode, ["IsNonSpeculative", "IsSerializeAfter"]) header_output += ImmOp64Declare.subst(hltIop) decoder_output += SemihostConstructor64.subst(hltIop) exec_output += BasicExecute.subst(hltIop) diff --git a/src/arch/arm/semihosting.cc b/src/arch/arm/semihosting.cc index 4f897e208..5ea894af3 100644 --- a/src/arch/arm/semihosting.cc +++ b/src/arch/arm/semihosting.cc @@ -52,39 +52,37 @@ #include "sim/system.hh" const std::map 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 ArmSemihosting::fmodes{ @@ -155,74 +153,42 @@ ArmSemihosting::ArmSemihosting(const ArmSemihostingParams *p) 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 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(param + i * 8, endian); - DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]); + auto it = calls.find(op); + if (it == calls.end()) { + unrecognizedCall( + 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 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(param + i * 4, endian); - DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]); + auto it = calls.find(op); + if (it == calls.end()) { + unrecognizedCall( + 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 @@ -255,14 +221,17 @@ ArmSemihosting::unserialize(CheckpointIn &cp) PortProxy & ArmSemihosting::physProxy(ThreadContext *tc) { + static std::unique_ptr 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(); } @@ -281,15 +250,13 @@ ArmSemihosting::readString(ThreadContext *tc, Addr ptr, size_t len) } ArmSemihosting::RetErrno -ArmSemihosting::callOpen(ThreadContext *tc, bool aarch64, - std::vector &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); @@ -301,7 +268,7 @@ ArmSemihosting::callOpen(ThreadContext *tc, bool aarch64, 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 { @@ -311,33 +278,31 @@ ArmSemihosting::callOpen(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callClose(ThreadContext *tc, bool aarch64, - std::vector &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 &file = files[argv[1]]; + std::unique_ptr &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 &argv) +ArmSemihosting::callWriteC(ThreadContext *tc, InPlaceArg arg) { - const char c = physProxy(tc).read(argv[0]); + const char c = physProxy(tc).read(arg.addr); DPRINTF(Semihosting, "Semihosting SYS_WRITEC('%c')\n", c); std::cout.put(c); @@ -346,90 +311,78 @@ ArmSemihosting::callWriteC(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callWrite0(ThreadContext *tc, bool aarch64, - std::vector &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(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 &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 buffer(argv[3]); - physProxy(tc).readBlob(argv[2], buffer.data(), buffer.size()); + std::vector 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 &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 buffer(argv[3]); - int64_t ret = files[argv[1]]->read(buffer.data(), buffer.size()); + std::vector 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 &argv) +ArmSemihosting::callReadC(ThreadContext *tc) { return retOK((char)std::cin.get()); } ArmSemihosting::RetErrno -ArmSemihosting::callIsError(ThreadContext *tc, bool aarch64, - std::vector &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 &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 { @@ -438,13 +391,12 @@ ArmSemihosting::callIsTTY(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callSeek(ThreadContext *tc, bool aarch64, - std::vector &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 { @@ -453,13 +405,12 @@ ArmSemihosting::callSeek(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callFLen(ThreadContext *tc, bool aarch64, - std::vector &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 { @@ -468,31 +419,26 @@ ArmSemihosting::callFLen(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callTmpNam(ThreadContext *tc, bool aarch64, - std::vector &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 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 &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); @@ -502,11 +448,11 @@ ArmSemihosting::callRemove(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callRename(ThreadContext *tc, bool aarch64, - std::vector &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); @@ -516,24 +462,21 @@ ArmSemihosting::callRename(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callClock(ThreadContext *tc, bool aarch64, - std::vector &argv) +ArmSemihosting::callClock(ThreadContext *tc) { return retOK(curTick() / (SimClock::Int::s / 100)); } ArmSemihosting::RetErrno -ArmSemihosting::callTime(ThreadContext *tc, bool aarch64, - std::vector &argv) +ArmSemihosting::callTime(ThreadContext *tc) { return retOK(timeBase + round(curTick() / SimClock::Float::s)); } ArmSemihosting::RetErrno -ArmSemihosting::callSystem(ThreadContext *tc, bool aarch64, - std::vector &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); @@ -541,35 +484,33 @@ ArmSemihosting::callSystem(ThreadContext *tc, bool aarch64, } ArmSemihosting::RetErrno -ArmSemihosting::callErrno(ThreadContext *tc, bool aarch64, - std::vector &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 &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(argv[0] + 1 * 8, cmdLine.size(), endian); - else - proxy.write(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 &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(); @@ -594,11 +535,10 @@ ArmSemihosting::callHeapInfo(ThreadContext *tc, bool aarch64, 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" @@ -606,44 +546,56 @@ ArmSemihosting::callHeapInfo(ThreadContext *tc, bool aarch64, "\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(base + 0 * 8, heap_base, endian); - proxy.write(base + 1 * 8, heap_limit, endian); - proxy.write(base + 2 * 8, stack_base, endian); - proxy.write(base + 3 * 8, stack_limit, endian); - } else { - proxy.write(base + 0 * 4, heap_base, endian); - proxy.write(base + 1 * 4, heap_limit, endian); - proxy.write(base + 2 * 4, stack_base, endian); - proxy.write(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 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 &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 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 &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); } @@ -660,40 +612,31 @@ ArmSemihosting::semiExit(uint64_t code, uint64_t subcode) ArmSemihosting::RetErrno -ArmSemihosting::callElapsed(ThreadContext *tc, bool aarch64, - std::vector &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(argv[0], tick, endian); - } else { - proxy.write(argv[0] + 0 * 4, tick, endian); - proxy.write(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 &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 * @@ -989,6 +932,13 @@ ArmSemihosting::File::unserialize(CheckpointIn &cp) } } +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() diff --git a/src/arch/arm/semihosting.hh b/src/arch/arm/semihosting.hh index 104b479c5..4a8aa2bd9 100644 --- a/src/arch/arm/semihosting.hh +++ b/src/arch/arm/semihosting.hh @@ -39,17 +39,21 @@ #define __ARCH_ARM_SEMIHOSTING_HH__ #include +#include #include #include #include #include +#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 @@ -69,6 +73,115 @@ class ArmSemihosting : public SimObject { public: + static PortProxy &physProxy(ThreadContext *tc); + + struct AbiBase + { + template + 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( + argPointer, endian); + argPointer += sizeof(Arg); + return arg; + } + + using ArgType = Arg; + }; + }; + + struct Abi64 : public AbiBase + { + class State : public StateBase + { + public: + // For 64 bit semihosting, the params are pointer to by X1. + explicit State(const ThreadContext *tc) : + StateBase(tc, tc->readIntReg(ArmISA::INTREG_X1)) + {} + }; + }; + + struct Abi32 : public AbiBase + { + class State : public StateBase + { + public: + // For 32 bit semihosting, the params are pointer to by R1. + explicit State(const ThreadContext *tc) : + StateBase(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(addr, endian); + else if (size == 4) + return proxy.read(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(addr, val, endian); + else if (size == 4) + proxy.write(addr, val, endian); + else + panic("Unexpected semihosting argument size %d.", size); + } + }; + enum Operation { SYS_OPEN = 0x01, SYS_CLOSE = 0x02, @@ -101,9 +214,9 @@ class ArmSemihosting : public SimObject 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; @@ -274,31 +387,39 @@ class ArmSemihosting : public SimObject std::string filesRootDir; std::vector> 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 physProxyS; + public: + typedef std::pair RetErrno; private: - typedef std::pair 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); } @@ -307,74 +428,135 @@ class ArmSemihosting : public SimObject * * 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 &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 + 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 + static inline std::function + wrapImpl(ArmSemihosting *sh, Implementation 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; + using Dumper = std::function; + + // 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 + static inline Dispatcher + buildDispatcher(Implementation impl) + { + // This lambda is the dispatcher we're building. + return [impl](ArmSemihosting *sh, ThreadContext *tc) { + auto wrapper = wrapImpl(sh, impl); + return invokeSimcall(tc, wrapper); + }; + } + + // A function which builds a dumper for a semihosting call. + template + static inline Dumper + buildDumper(const char *name, Implementation impl) + { + // This lambda is the dumper we're building. + return [name](ThreadContext *tc) -> std::string { + return dumpSimcall(name, tc); + }; + } + + // When there's one implementation, use it for both 32 and 64 bits. + template + SemiCall(const char *_name, Implementation common) : + name(_name), call32(buildDispatcher(common)), + call64(buildDispatcher(common)), + dump32(buildDumper(_name, common)), + dump64(buildDumper(_name, common)) + {} + + // When there are two, use one for 32 bits and one for 64 bits. + template + SemiCall(const char *_name, Implementation impl32, + Implementation impl64) : + name(_name), call32(buildDispatcher(impl32)), + call64(buildDispatcher(impl64)), + dump32(buildDumper(_name, impl32)), + dump64(buildDumper(_name, impl64)) + {} }; -#define SEMI_CALL(N) \ - RetErrno call ## N (ThreadContext *tc, \ - bool aarch64, std::vector &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 + void + unrecognizedCall(ThreadContext *tc, const char *format, uint64_t op) + { + warn(format, op); + std::function retErr = + [](ThreadContext *tc) { return retError(EINVAL); }; + invokeSimcall(tc, retErr); + } + static FILE *getSTDIO(const char *stream_name, const std::string &name, const char *mode); @@ -385,4 +567,69 @@ class ArmSemihosting : public SimObject static const std::map stdioMap; }; +std::ostream &operator << ( + std::ostream &os, const ArmSemihosting::InPlaceArg &ipa); + +namespace GuestABI +{ + +template +struct Argument::value>::type> +{ + static Arg + get(ThreadContext *tc, ArmSemihosting::Abi64::State &state) + { + return state.get(tc); + } +}; + +template +struct Argument::value>::type> +{ + static Arg + get(ThreadContext *tc, ArmSemihosting::Abi32::State &state) + { + if (std::is_signed::value) + return sext<32>(state.get(tc)); + else + return state.get(tc); + } +}; + +template +struct Argument::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 +{ + static void + store(ThreadContext *tc, const ArmSemihosting::RetErrno &err) + { + tc->setIntReg(ArmISA::INTREG_R0, err.first); + } +}; + +template <> +struct Result +{ + static void + store(ThreadContext *tc, const ArmSemihosting::RetErrno &err) + { + tc->setIntReg(ArmISA::INTREG_X0, err.first); + } +}; + +} // namespace GuestABI + #endif // __ARCH_ARM_SEMIHOSTING_HH__ diff --git a/src/arch/arm/system.cc b/src/arch/arm/system.cc index 6094121cc..906cc9eda 100644 --- a/src/arch/arm/system.cc +++ b/src/arch/arm/system.cc @@ -179,20 +179,16 @@ ArmSystem::haveSemihosting(ThreadContext *tc) return FullSystem && getArmSystem(tc)->haveSemihosting(); } -uint64_t -ArmSystem::callSemihosting64(ThreadContext *tc, - uint32_t op, uint64_t param) +void +ArmSystem::callSemihosting64(ThreadContext *tc) { - ArmSystem *sys = getArmSystem(tc); - return sys->semihosting->call64(tc, op, param); + getArmSystem(tc)->semihosting->call64(tc); } -uint32_t -ArmSystem::callSemihosting32(ThreadContext *tc, - uint32_t op, uint32_t param) +void +ArmSystem::callSemihosting32(ThreadContext *tc) { - ArmSystem *sys = getArmSystem(tc); - return sys->semihosting->call32(tc, op, param); + getArmSystem(tc)->semihosting->call32(tc); } void diff --git a/src/arch/arm/system.hh b/src/arch/arm/system.hh index d9268f093..ae83da07d 100644 --- a/src/arch/arm/system.hh +++ b/src/arch/arm/system.hh @@ -314,12 +314,10 @@ class ArmSystem : public System static bool haveSemihosting(ThreadContext *tc); /** Make a Semihosting call from aarch64 */ - static uint64_t callSemihosting64(ThreadContext *tc, - uint32_t op, uint64_t param); + static void callSemihosting64(ThreadContext *tc); /** Make a Semihosting call from aarch32 */ - static uint32_t callSemihosting32(ThreadContext *tc, - uint32_t op, uint32_t param); + static void callSemihosting32(ThreadContext *tc); /** Make a call to notify the power controller of STANDBYWFI assertion */ static void callSetStandByWfi(ThreadContext *tc); -- 2.30.2