112531Sandreas.sandberg@arm.com/*
212531Sandreas.sandberg@arm.com * Copyright (c) 2018 ARM Limited
312531Sandreas.sandberg@arm.com * All rights reserved
412531Sandreas.sandberg@arm.com *
512531Sandreas.sandberg@arm.com * The license below extends only to copyright in the software and shall
612531Sandreas.sandberg@arm.com * not be construed as granting a license to any other intellectual
712531Sandreas.sandberg@arm.com * property including but not limited to intellectual property relating
812531Sandreas.sandberg@arm.com * to a hardware implementation of the functionality of the software
912531Sandreas.sandberg@arm.com * licensed hereunder.  You may use the software subject to the license
1012531Sandreas.sandberg@arm.com * terms below provided that you ensure that this notice is replicated
1112531Sandreas.sandberg@arm.com * unmodified and in its entirety in all distributions of the software,
1212531Sandreas.sandberg@arm.com * modified or unmodified, in source code or in binary form.
1312531Sandreas.sandberg@arm.com *
1412531Sandreas.sandberg@arm.com * Redistribution and use in source and binary forms, with or without
1512531Sandreas.sandberg@arm.com * modification, are permitted provided that the following conditions are
1612531Sandreas.sandberg@arm.com * met: redistributions of source code must retain the above copyright
1712531Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer;
1812531Sandreas.sandberg@arm.com * redistributions in binary form must reproduce the above copyright
1912531Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer in the
2012531Sandreas.sandberg@arm.com * documentation and/or other materials provided with the distribution;
2112531Sandreas.sandberg@arm.com * neither the name of the copyright holders nor the names of its
2212531Sandreas.sandberg@arm.com * contributors may be used to endorse or promote products derived from
2312531Sandreas.sandberg@arm.com * this software without specific prior written permission.
2412531Sandreas.sandberg@arm.com *
2512531Sandreas.sandberg@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2612531Sandreas.sandberg@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2712531Sandreas.sandberg@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2812531Sandreas.sandberg@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2912531Sandreas.sandberg@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3012531Sandreas.sandberg@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3112531Sandreas.sandberg@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3212531Sandreas.sandberg@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3312531Sandreas.sandberg@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3412531Sandreas.sandberg@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3512531Sandreas.sandberg@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3612531Sandreas.sandberg@arm.com *
3712531Sandreas.sandberg@arm.com * Authors: Andreas Sandberg
3812531Sandreas.sandberg@arm.com */
3912531Sandreas.sandberg@arm.com
4012531Sandreas.sandberg@arm.com#include "arch/arm/semihosting.hh"
4112531Sandreas.sandberg@arm.com
4212531Sandreas.sandberg@arm.com#include <cstdio>
4312531Sandreas.sandberg@arm.com
4412531Sandreas.sandberg@arm.com#include "arch/arm/utility.hh"
4512531Sandreas.sandberg@arm.com#include "base/logging.hh"
4612531Sandreas.sandberg@arm.com#include "base/time.hh"
4712531Sandreas.sandberg@arm.com#include "debug/Semihosting.hh"
4812531Sandreas.sandberg@arm.com#include "dev/serial/serial.hh"
4912531Sandreas.sandberg@arm.com#include "mem/physical.hh"
5014007Sgabeblack@google.com#include "mem/secure_port_proxy.hh"
5112531Sandreas.sandberg@arm.com#include "params/ArmSemihosting.hh"
5212531Sandreas.sandberg@arm.com#include "sim/byteswap.hh"
5312531Sandreas.sandberg@arm.com#include "sim/sim_exit.hh"
5412531Sandreas.sandberg@arm.com#include "sim/system.hh"
5512531Sandreas.sandberg@arm.com
5612531Sandreas.sandberg@arm.comconst std::map<uint32_t, ArmSemihosting::SemiCall> ArmSemihosting::calls{
5712531Sandreas.sandberg@arm.com    { 0x01, { "SYS_OPEN", &ArmSemihosting::callOpen, 3, 3 } },
5812531Sandreas.sandberg@arm.com    { 0x02, { "SYS_CLOSE", &ArmSemihosting::callClose, 1, 1 } },
5912531Sandreas.sandberg@arm.com
6012531Sandreas.sandberg@arm.com    // Write(C|0) are special since we want to read the character
6112531Sandreas.sandberg@arm.com    // manually. We therefore declare them as having 0 params.
6212531Sandreas.sandberg@arm.com    { 0x03, { "SYS_WRITEC", &ArmSemihosting::callWriteC, 0, 0 } },
6312531Sandreas.sandberg@arm.com    { 0x04, { "SYS_WRITE0", &ArmSemihosting::callWrite0, 1, 1 } },
6412531Sandreas.sandberg@arm.com
6512531Sandreas.sandberg@arm.com    { 0x05, { "SYS_WRITE", &ArmSemihosting::callWrite, 3, 3 } },
6612531Sandreas.sandberg@arm.com    { 0x06, { "SYS_READ", &ArmSemihosting::callRead, 3, 3 } },
6712531Sandreas.sandberg@arm.com    { 0x07, { "SYS_READC", &ArmSemihosting::callReadC, 0, 0 } },
6812531Sandreas.sandberg@arm.com    { 0x08, { "SYS_ISERROR", &ArmSemihosting::callIsError, 1, 1 } },
6912531Sandreas.sandberg@arm.com    { 0x09, { "SYS_ISTTY", &ArmSemihosting::callIsTTY, 1, 1 } },
7012531Sandreas.sandberg@arm.com    { 0x0A, { "SYS_SEEK", &ArmSemihosting::callSeek, 2, 2 } },
7112531Sandreas.sandberg@arm.com    { 0x0C, { "SYS_FLEN", &ArmSemihosting::callFLen, 1, 1 } },
7212531Sandreas.sandberg@arm.com    { 0x0D, { "SYS_TMPNAM", &ArmSemihosting::callTmpNam, 3, 3 } },
7312531Sandreas.sandberg@arm.com    { 0x0E, { "SYS_REMOVE", &ArmSemihosting::callRemove, 2, 2} },
7412531Sandreas.sandberg@arm.com    { 0x0F, { "SYS_RENAME", &ArmSemihosting::callRename, 4, 4} },
7512531Sandreas.sandberg@arm.com    { 0x10, { "SYS_CLOCK", &ArmSemihosting::callClock, 0, 0} },
7612531Sandreas.sandberg@arm.com    { 0x11, { "SYS_TIME", &ArmSemihosting::callTime, 0, 0} },
7712531Sandreas.sandberg@arm.com    { 0x12, { "SYS_SYSTEM", &ArmSemihosting::callSystem, 2, 2} },
7812531Sandreas.sandberg@arm.com    { 0x13, { "SYS_ERRNO", &ArmSemihosting::callErrno, 0, 0 } },
7912699Sandreas.sandberg@arm.com    { 0x15, { "SYS_GET_CMDLINE", &ArmSemihosting::callGetCmdLine, 2, 2} },
8012531Sandreas.sandberg@arm.com    { 0x16, { "SYS_HEAPINFO", &ArmSemihosting::callHeapInfo, 1, 1} },
8112531Sandreas.sandberg@arm.com
8212531Sandreas.sandberg@arm.com    // Exit is special and requires custom handling in aarch32.
8312531Sandreas.sandberg@arm.com    { 0x18, { "SYS_EXIT", &ArmSemihosting::callExit, 0, 2 } },
8412531Sandreas.sandberg@arm.com    { 0x20, { "SYS_EXIT_EXTENDED", &ArmSemihosting::callExitExtended, 2, 2 } },
8512531Sandreas.sandberg@arm.com
8612531Sandreas.sandberg@arm.com    { 0x30, { "SYS_ELAPSED", &ArmSemihosting::callElapsed, 0, 0 } },
8712531Sandreas.sandberg@arm.com    { 0x31, { "SYS_TICKFREQ", &ArmSemihosting::callTickFreq, 0, 0 } },
8812531Sandreas.sandberg@arm.com};
8912531Sandreas.sandberg@arm.com
9012531Sandreas.sandberg@arm.comconst std::vector<const char *> ArmSemihosting::fmodes{
9112531Sandreas.sandberg@arm.com    "r", "rb", "r+", "r+b",
9212531Sandreas.sandberg@arm.com    "w", "wb", "w+", "w+b",
9312531Sandreas.sandberg@arm.com    "a", "ab", "a+", "a+b",
9412531Sandreas.sandberg@arm.com};
9512531Sandreas.sandberg@arm.com
9612531Sandreas.sandberg@arm.comconst std::map<uint64_t, const char *> ArmSemihosting::exitCodes{
9712531Sandreas.sandberg@arm.com    { 0x20000, "semi:ADP_Stopped_BranchThroughZero" },
9812531Sandreas.sandberg@arm.com    { 0x20001, "semi:ADP_Stopped_UndefinedInstr" },
9912531Sandreas.sandberg@arm.com    { 0x20002, "semi:ADP_Stopped_SoftwareInterrupt" },
10012531Sandreas.sandberg@arm.com    { 0x20003, "semi:ADP_Stopped_PrefetchAbort" },
10112531Sandreas.sandberg@arm.com    { 0x20004, "semi:ADP_Stopped_DataAbort" },
10212531Sandreas.sandberg@arm.com    { 0x20005, "semi:ADP_Stopped_AddressException" },
10312531Sandreas.sandberg@arm.com    { 0x20006, "semi:ADP_Stopped_IRQ" },
10412531Sandreas.sandberg@arm.com    { 0x20007, "semi:ADP_Stopped_FIQ" },
10512531Sandreas.sandberg@arm.com
10612531Sandreas.sandberg@arm.com    { 0x20020, "semi:ADP_Stopped_BreakPoint" },
10712531Sandreas.sandberg@arm.com    { 0x20021, "semi:ADP_Stopped_WatchPoint" },
10812531Sandreas.sandberg@arm.com    { 0x20022, "semi:ADP_Stopped_StepComplete" },
10912531Sandreas.sandberg@arm.com    { 0x20023, "semi:ADP_Stopped_RunTimeErrorUnknown" },
11012531Sandreas.sandberg@arm.com    { 0x20024, "semi:ADP_Stopped_InternalError" },
11112531Sandreas.sandberg@arm.com    { 0x20025, "semi:ADP_Stopped_UserInterruption" },
11212531Sandreas.sandberg@arm.com    { 0x20026, "semi:ADP_Stopped_ApplicationExit" },
11312531Sandreas.sandberg@arm.com    { 0x20027, "semi:ADP_Stopped_StackOverflow" },
11412531Sandreas.sandberg@arm.com    { 0x20028, "semi:ADP_Stopped_DivisionByZero" },
11512531Sandreas.sandberg@arm.com    { 0x20029, "semi:ADP_Stopped_DivisionByZero" },
11612531Sandreas.sandberg@arm.com};
11712531Sandreas.sandberg@arm.com
11812531Sandreas.sandberg@arm.com
11912531Sandreas.sandberg@arm.comconst std::vector<uint8_t> ArmSemihosting::features{
12012531Sandreas.sandberg@arm.com    0x53, 0x48, 0x46, 0x42, // Magic
12112531Sandreas.sandberg@arm.com    0x3,                    // EXT_EXIT_EXTENDED, EXT_STDOUT_STDERR
12212531Sandreas.sandberg@arm.com};
12312531Sandreas.sandberg@arm.com
12412698Sandreas.sandberg@arm.comconst std::map<const std::string, FILE *> ArmSemihosting::stdioMap{
12512698Sandreas.sandberg@arm.com    {"cin",    ::stdin},
12612698Sandreas.sandberg@arm.com    {"stdin",  ::stdin},
12712698Sandreas.sandberg@arm.com    {"cout",   ::stdout},
12812698Sandreas.sandberg@arm.com    {"stdout", ::stdout},
12912698Sandreas.sandberg@arm.com    {"cerr",   ::stderr},
13012698Sandreas.sandberg@arm.com    {"stderr", ::stderr},
13112698Sandreas.sandberg@arm.com};
13212698Sandreas.sandberg@arm.com
13312531Sandreas.sandberg@arm.comArmSemihosting::ArmSemihosting(const ArmSemihostingParams *p)
13412531Sandreas.sandberg@arm.com    : SimObject(p),
13512531Sandreas.sandberg@arm.com      cmdLine(p->cmd_line),
13612531Sandreas.sandberg@arm.com      memReserve(p->mem_reserve),
13712531Sandreas.sandberg@arm.com      stackSize(p->stack_size),
13812531Sandreas.sandberg@arm.com      timeBase([p]{ struct tm t = p->time; return mkutctime(&t); }()),
13912531Sandreas.sandberg@arm.com      tickShift(calcTickShift()),
14012698Sandreas.sandberg@arm.com      semiErrno(0),
14112698Sandreas.sandberg@arm.com      stdin(getSTDIO("stdin", p->stdin, "r")),
14212698Sandreas.sandberg@arm.com      stdout(getSTDIO("stdout", p->stdout, "w")),
14312698Sandreas.sandberg@arm.com      stderr(p->stderr == p->stdout ?
14412698Sandreas.sandberg@arm.com             stdout : getSTDIO("stderr", p->stderr, "w"))
14512531Sandreas.sandberg@arm.com{
14612531Sandreas.sandberg@arm.com    // Create an empty place-holder file for position 0 as semi-hosting
14712531Sandreas.sandberg@arm.com    // calls typically expect non-zero file handles.
14812531Sandreas.sandberg@arm.com    files.push_back(nullptr);
14912531Sandreas.sandberg@arm.com
15012531Sandreas.sandberg@arm.com    if (tickShift > 0)
15112531Sandreas.sandberg@arm.com        inform("Semihosting: Shifting elapsed ticks by %i bits.",
15212531Sandreas.sandberg@arm.com               tickShift);
15312531Sandreas.sandberg@arm.com}
15412531Sandreas.sandberg@arm.com
15512531Sandreas.sandberg@arm.comuint64_t
15612531Sandreas.sandberg@arm.comArmSemihosting::call64(ThreadContext *tc, uint32_t op, uint64_t param)
15712531Sandreas.sandberg@arm.com{
15812531Sandreas.sandberg@arm.com    const SemiCall *call = getCall(op, true);
15912531Sandreas.sandberg@arm.com    if (!call) {
16012531Sandreas.sandberg@arm.com        warn("Unknown aarch64 semihosting call: op = 0x%x, param = 0x%x",
16112531Sandreas.sandberg@arm.com             op, param);
16212531Sandreas.sandberg@arm.com
16312531Sandreas.sandberg@arm.com        return (uint64_t)-1;
16412531Sandreas.sandberg@arm.com    } else if (!call->implemented64()) {
16512531Sandreas.sandberg@arm.com        warn("Unimplemented aarch64 semihosting call: "
16612531Sandreas.sandberg@arm.com             "%s (op = 0x%x, param = 0x%x)",
16712531Sandreas.sandberg@arm.com             call->name, op, param);
16812531Sandreas.sandberg@arm.com
16912531Sandreas.sandberg@arm.com        return (uint64_t)-1;
17012531Sandreas.sandberg@arm.com    }
17112531Sandreas.sandberg@arm.com
17212531Sandreas.sandberg@arm.com    std::vector<uint64_t> argv(call->argc64 + 1);
17312533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
17412531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
17512531Sandreas.sandberg@arm.com
17612531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting call64: %s(0x%x)\n", call->name, param);
17712531Sandreas.sandberg@arm.com    argv[0] = param;
17812531Sandreas.sandberg@arm.com    for (int i = 0; i < call->argc64; ++i) {
17913893Sgabeblack@google.com        argv[i + 1] = proxy.read<uint64_t>(param + i * 8, endian);
18012531Sandreas.sandberg@arm.com        DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]);
18112531Sandreas.sandberg@arm.com    }
18212531Sandreas.sandberg@arm.com
18312531Sandreas.sandberg@arm.com    auto ret_errno = (this->*call->call)(tc, true, argv);
18412531Sandreas.sandberg@arm.com    semiErrno = ret_errno.second;
18512531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "\t ->: 0x%x, %i\n",
18612531Sandreas.sandberg@arm.com            ret_errno.first, ret_errno.second);
18712531Sandreas.sandberg@arm.com    return ret_errno.first;
18812531Sandreas.sandberg@arm.com}
18912531Sandreas.sandberg@arm.com
19012531Sandreas.sandberg@arm.comuint32_t
19112531Sandreas.sandberg@arm.comArmSemihosting::call32(ThreadContext *tc, uint32_t op, uint32_t param)
19212531Sandreas.sandberg@arm.com{
19312531Sandreas.sandberg@arm.com    const SemiCall *call = getCall(op, false);
19412531Sandreas.sandberg@arm.com    if (!call) {
19512531Sandreas.sandberg@arm.com        warn("Unknown aarch32 semihosting call: op = 0x%x, param = 0x%x",
19612531Sandreas.sandberg@arm.com             op, param);
19712531Sandreas.sandberg@arm.com
19812531Sandreas.sandberg@arm.com        return (uint32_t)-1;
19912531Sandreas.sandberg@arm.com    } else if (!call->implemented32()) {
20012531Sandreas.sandberg@arm.com        warn("Unimplemented aarch32 semihosting call: "
20112531Sandreas.sandberg@arm.com             "%s (op = 0x%x, param = 0x%x)",
20212531Sandreas.sandberg@arm.com             call->name, op, param);
20312531Sandreas.sandberg@arm.com
20412531Sandreas.sandberg@arm.com        return (uint32_t)-1;
20512531Sandreas.sandberg@arm.com    }
20612531Sandreas.sandberg@arm.com
20712531Sandreas.sandberg@arm.com    std::vector<uint64_t> argv(call->argc32 + 1);
20812533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
20912531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
21012531Sandreas.sandberg@arm.com
21112531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting call32: %s(0x%x)\n", call->name, param);
21212531Sandreas.sandberg@arm.com    argv[0] = param;
21312531Sandreas.sandberg@arm.com    for (int i = 0; i < call->argc32; ++i) {
21413893Sgabeblack@google.com        argv[i + 1] = proxy.read<uint32_t>(param + i * 4, endian);
21512531Sandreas.sandberg@arm.com        DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]);
21612531Sandreas.sandberg@arm.com    }
21712531Sandreas.sandberg@arm.com
21812531Sandreas.sandberg@arm.com    auto ret_errno = (this->*call->call)(tc, false, argv);
21912531Sandreas.sandberg@arm.com    semiErrno = ret_errno.second;
22012531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "\t ->: 0x%x, %i\n",
22112531Sandreas.sandberg@arm.com            ret_errno.first, ret_errno.second);
22212531Sandreas.sandberg@arm.com    return ret_errno.first;
22312531Sandreas.sandberg@arm.com}
22412531Sandreas.sandberg@arm.com
22512531Sandreas.sandberg@arm.comvoid
22612531Sandreas.sandberg@arm.comArmSemihosting::serialize(CheckpointOut &cp) const
22712531Sandreas.sandberg@arm.com{
22812531Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(semiErrno);
22912531Sandreas.sandberg@arm.com
23012531Sandreas.sandberg@arm.com    paramOut(cp, "num_files", files.size());
23112531Sandreas.sandberg@arm.com    for (int i = 0; i < files.size(); i++) {
23212531Sandreas.sandberg@arm.com        // File closed?
23312531Sandreas.sandberg@arm.com        if (!files[i])
23412531Sandreas.sandberg@arm.com            continue;
23512531Sandreas.sandberg@arm.com
23612531Sandreas.sandberg@arm.com        files[i]->serializeSection(cp, csprintf("file%i", i));
23712531Sandreas.sandberg@arm.com    }
23812531Sandreas.sandberg@arm.com}
23912531Sandreas.sandberg@arm.com
24012531Sandreas.sandberg@arm.comvoid
24112531Sandreas.sandberg@arm.comArmSemihosting::unserialize(CheckpointIn &cp)
24212531Sandreas.sandberg@arm.com{
24312531Sandreas.sandberg@arm.com    UNSERIALIZE_SCALAR(semiErrno);
24412531Sandreas.sandberg@arm.com
24512531Sandreas.sandberg@arm.com    size_t num_files;
24612531Sandreas.sandberg@arm.com    paramIn(cp, "num_files", num_files);
24712531Sandreas.sandberg@arm.com    files.resize(num_files);
24812531Sandreas.sandberg@arm.com    for (int i = 0; i < num_files; i++)
24912531Sandreas.sandberg@arm.com        files[i] = FileBase::create(*this, cp, csprintf("file%i", i));
25012531Sandreas.sandberg@arm.com}
25112531Sandreas.sandberg@arm.com
25212533Sandreas.sandberg@arm.comPortProxy &
25312533Sandreas.sandberg@arm.comArmSemihosting::physProxy(ThreadContext *tc)
25412533Sandreas.sandberg@arm.com{
25512533Sandreas.sandberg@arm.com    if (ArmISA::inSecureState(tc)) {
25612533Sandreas.sandberg@arm.com        if (!physProxyS) {
25712533Sandreas.sandberg@arm.com            System *sys = tc->getSystemPtr();
25812533Sandreas.sandberg@arm.com            physProxyS.reset(new SecurePortProxy(
25912533Sandreas.sandberg@arm.com                                 sys->getSystemPort(),
26012533Sandreas.sandberg@arm.com                                 sys->cacheLineSize()));
26112533Sandreas.sandberg@arm.com        }
26212533Sandreas.sandberg@arm.com        return *physProxyS;
26312533Sandreas.sandberg@arm.com    } else {
26412533Sandreas.sandberg@arm.com        return tc->getPhysProxy();
26512533Sandreas.sandberg@arm.com    }
26612533Sandreas.sandberg@arm.com}
26712533Sandreas.sandberg@arm.com
26812533Sandreas.sandberg@arm.com
26912531Sandreas.sandberg@arm.comstd::string
27012531Sandreas.sandberg@arm.comArmSemihosting::readString(ThreadContext *tc, Addr ptr, size_t len)
27112531Sandreas.sandberg@arm.com{
27212531Sandreas.sandberg@arm.com    std::vector<char> buf(len + 1);
27312531Sandreas.sandberg@arm.com
27412531Sandreas.sandberg@arm.com    buf[len] = '\0';
27514010Sgabeblack@google.com    physProxy(tc).readBlob(ptr, buf.data(), len);
27612531Sandreas.sandberg@arm.com
27712531Sandreas.sandberg@arm.com    return std::string(buf.data());
27812531Sandreas.sandberg@arm.com}
27912531Sandreas.sandberg@arm.com
28012531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
28112531Sandreas.sandberg@arm.comArmSemihosting::callOpen(ThreadContext *tc, bool aarch64,
28212531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
28312531Sandreas.sandberg@arm.com{
28412531Sandreas.sandberg@arm.com    const Addr name_base = argv[1];
28512531Sandreas.sandberg@arm.com    const char *mode = argv[2] < fmodes.size() ? fmodes[argv[2]] : nullptr;
28612531Sandreas.sandberg@arm.com    const Addr name_size = argv[3];
28712531Sandreas.sandberg@arm.com
28812531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_OPEN(0x%x, %i[%s], %i)\n",
28912531Sandreas.sandberg@arm.com            name_base, argv[2], mode ? mode : "-", name_size);
29012531Sandreas.sandberg@arm.com    if (!mode || !name_base)
29112531Sandreas.sandberg@arm.com        return retError(EINVAL);
29212531Sandreas.sandberg@arm.com
29312531Sandreas.sandberg@arm.com    std::string fname = readString(tc, name_base, name_size);
29412531Sandreas.sandberg@arm.com
29512531Sandreas.sandberg@arm.com    std::unique_ptr<ArmSemihosting::FileBase> file =
29612531Sandreas.sandberg@arm.com        FileBase::create(*this, fname, mode);
29712531Sandreas.sandberg@arm.com    int64_t ret = file->open();
29812531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_OPEN(\"%s\", %i[%s]): %i\n",
29912531Sandreas.sandberg@arm.com            fname, argv[2], mode, ret);
30012531Sandreas.sandberg@arm.com    if (ret < 0) {
30112531Sandreas.sandberg@arm.com        return retError(-ret);
30212531Sandreas.sandberg@arm.com    } else {
30312531Sandreas.sandberg@arm.com        files.push_back(std::move(file));
30412531Sandreas.sandberg@arm.com        return retOK(files.size() - 1);
30512531Sandreas.sandberg@arm.com    }
30612531Sandreas.sandberg@arm.com}
30712531Sandreas.sandberg@arm.com
30812531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
30912531Sandreas.sandberg@arm.comArmSemihosting::callClose(ThreadContext *tc, bool aarch64,
31012531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
31112531Sandreas.sandberg@arm.com{
31212531Sandreas.sandberg@arm.com    if (argv[1] > files.size()) {
31312531Sandreas.sandberg@arm.com        DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i): Illegal file\n");
31412531Sandreas.sandberg@arm.com        return retError(EBADF);
31512531Sandreas.sandberg@arm.com    }
31612531Sandreas.sandberg@arm.com
31712531Sandreas.sandberg@arm.com    std::unique_ptr<FileBase> &file = files[argv[1]];
31812531Sandreas.sandberg@arm.com    int64_t error = file->close();
31912531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i[%s]): %i\n",
32012531Sandreas.sandberg@arm.com            argv[1], file->fileName(), error);
32112531Sandreas.sandberg@arm.com    if (error < 0) {
32212531Sandreas.sandberg@arm.com        return retError(-error);
32312531Sandreas.sandberg@arm.com    } else {
32412531Sandreas.sandberg@arm.com        // Zap the pointer and free the entry in the file table as
32512531Sandreas.sandberg@arm.com        // well.
32612531Sandreas.sandberg@arm.com        files[argv[1]].reset();
32712531Sandreas.sandberg@arm.com        return retOK(0);
32812531Sandreas.sandberg@arm.com    }
32912531Sandreas.sandberg@arm.com}
33012531Sandreas.sandberg@arm.com
33112531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
33212531Sandreas.sandberg@arm.comArmSemihosting::callWriteC(ThreadContext *tc, bool aarch64,
33312531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
33412531Sandreas.sandberg@arm.com{
33512533Sandreas.sandberg@arm.com    const char c = physProxy(tc).read<char>(argv[0]);
33612531Sandreas.sandberg@arm.com
33712531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_WRITEC('%c')\n", c);
33812531Sandreas.sandberg@arm.com    std::cout.put(c);
33912531Sandreas.sandberg@arm.com
34012531Sandreas.sandberg@arm.com    return retOK(0);
34112531Sandreas.sandberg@arm.com}
34212531Sandreas.sandberg@arm.com
34312531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
34412531Sandreas.sandberg@arm.comArmSemihosting::callWrite0(ThreadContext *tc, bool aarch64,
34512531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
34612531Sandreas.sandberg@arm.com{
34712531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_WRITE0(...)\n");
34812533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
34912531Sandreas.sandberg@arm.com    for (Addr addr = (Addr)argv[0]; ; ++addr) {
35012531Sandreas.sandberg@arm.com        char data = proxy.read<char>(addr);
35112531Sandreas.sandberg@arm.com        if (data == 0)
35212531Sandreas.sandberg@arm.com            break;
35312531Sandreas.sandberg@arm.com
35412531Sandreas.sandberg@arm.com        std::cout.put(data);
35512531Sandreas.sandberg@arm.com    }
35612531Sandreas.sandberg@arm.com
35712531Sandreas.sandberg@arm.com    return retOK(0);
35812531Sandreas.sandberg@arm.com}
35912531Sandreas.sandberg@arm.com
36012531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
36112531Sandreas.sandberg@arm.comArmSemihosting::callWrite(ThreadContext *tc, bool aarch64,
36212531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
36312531Sandreas.sandberg@arm.com{
36412531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
36512531Sandreas.sandberg@arm.com        return RetErrno(argv[3], EBADF);
36612531Sandreas.sandberg@arm.com
36712531Sandreas.sandberg@arm.com    std::vector<uint8_t> buffer(argv[3]);
36812533Sandreas.sandberg@arm.com    physProxy(tc).readBlob(argv[2], buffer.data(), buffer.size());
36912531Sandreas.sandberg@arm.com
37012531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->write(buffer.data(), buffer.size());
37112531Sandreas.sandberg@arm.com    if (ret < 0) {
37212531Sandreas.sandberg@arm.com        // No bytes written (we're returning the number of bytes not
37312531Sandreas.sandberg@arm.com        // written)
37412531Sandreas.sandberg@arm.com        return RetErrno(argv[3], -ret);
37512531Sandreas.sandberg@arm.com    } else {
37612531Sandreas.sandberg@arm.com        // Return the number of bytes not written
37712531Sandreas.sandberg@arm.com        return RetErrno(argv[3] - ret, 0);
37812531Sandreas.sandberg@arm.com    }
37912531Sandreas.sandberg@arm.com}
38012531Sandreas.sandberg@arm.com
38112531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
38212531Sandreas.sandberg@arm.comArmSemihosting::callRead(ThreadContext *tc, bool aarch64,
38312531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
38412531Sandreas.sandberg@arm.com{
38512531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
38612531Sandreas.sandberg@arm.com        return RetErrno(argv[3], EBADF);
38712531Sandreas.sandberg@arm.com
38812531Sandreas.sandberg@arm.com    std::vector<uint8_t> buffer(argv[3]);
38912531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->read(buffer.data(), buffer.size());
39012531Sandreas.sandberg@arm.com    if (ret < 0) {
39112531Sandreas.sandberg@arm.com        return RetErrno(argv[3], -ret);
39212531Sandreas.sandberg@arm.com    } else {
39312531Sandreas.sandberg@arm.com        panic_if(ret > buffer.size(), "Read longer than buffer size.");
39412531Sandreas.sandberg@arm.com
39512533Sandreas.sandberg@arm.com        physProxy(tc).writeBlob(argv[2], buffer.data(), ret);
39612531Sandreas.sandberg@arm.com
39712531Sandreas.sandberg@arm.com        // Return the number of bytes not written
39812531Sandreas.sandberg@arm.com        return retOK(argv[3] - ret);
39912531Sandreas.sandberg@arm.com    }
40012531Sandreas.sandberg@arm.com}
40112531Sandreas.sandberg@arm.com
40212531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
40312531Sandreas.sandberg@arm.comArmSemihosting::callReadC(ThreadContext *tc, bool aarch64,
40412531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
40512531Sandreas.sandberg@arm.com{
40612531Sandreas.sandberg@arm.com    return retOK((char)std::cin.get());
40712531Sandreas.sandberg@arm.com}
40812531Sandreas.sandberg@arm.com
40912531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
41012531Sandreas.sandberg@arm.comArmSemihosting::callIsError(ThreadContext *tc, bool aarch64,
41112531Sandreas.sandberg@arm.com                            std::vector<uint64_t> &argv)
41212531Sandreas.sandberg@arm.com{
41312531Sandreas.sandberg@arm.com    // Sign extend from a 32 bit integer in aarch32 since the argument
41412531Sandreas.sandberg@arm.com    // reader zero extends to a uint64_t.
41512531Sandreas.sandberg@arm.com    const int64_t status = (int64_t)(aarch64 ? argv[1] :sext<32>(argv[1]));
41612531Sandreas.sandberg@arm.com    // Assume there was an error if the status value is negative.
41712531Sandreas.sandberg@arm.com    return retOK(status < 0 ? 1 : 0);
41812531Sandreas.sandberg@arm.com}
41912531Sandreas.sandberg@arm.com
42012531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
42112531Sandreas.sandberg@arm.comArmSemihosting::callIsTTY(ThreadContext *tc, bool aarch64,
42212531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
42312531Sandreas.sandberg@arm.com{
42412531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
42512531Sandreas.sandberg@arm.com        return retError(EBADF);
42612531Sandreas.sandberg@arm.com
42712531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->isTTY();
42812531Sandreas.sandberg@arm.com    if (ret < 0) {
42912531Sandreas.sandberg@arm.com        return retError(-ret);
43012531Sandreas.sandberg@arm.com    } else {
43112531Sandreas.sandberg@arm.com        return retOK(ret ? 1 : 0);
43212531Sandreas.sandberg@arm.com    }
43312531Sandreas.sandberg@arm.com}
43412531Sandreas.sandberg@arm.com
43512531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
43612531Sandreas.sandberg@arm.comArmSemihosting::callSeek(ThreadContext *tc, bool aarch64,
43712531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
43812531Sandreas.sandberg@arm.com{
43912531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
44012531Sandreas.sandberg@arm.com        return retError(EBADF);
44112531Sandreas.sandberg@arm.com
44212531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->seek(argv[2]);
44312531Sandreas.sandberg@arm.com    if (ret < 0) {
44412531Sandreas.sandberg@arm.com        return retError(-ret);
44512531Sandreas.sandberg@arm.com    } else {
44612531Sandreas.sandberg@arm.com        return retOK(0);
44712531Sandreas.sandberg@arm.com    }
44812531Sandreas.sandberg@arm.com}
44912531Sandreas.sandberg@arm.com
45012531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
45112531Sandreas.sandberg@arm.comArmSemihosting::callFLen(ThreadContext *tc, bool aarch64,
45212531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
45312531Sandreas.sandberg@arm.com{
45412531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
45512531Sandreas.sandberg@arm.com        return retError(EBADF);
45612531Sandreas.sandberg@arm.com
45712531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->isTTY();
45812531Sandreas.sandberg@arm.com    if (ret < 0) {
45912531Sandreas.sandberg@arm.com        return retError(-ret);
46012531Sandreas.sandberg@arm.com    } else {
46112531Sandreas.sandberg@arm.com        return retOK(0);
46212531Sandreas.sandberg@arm.com    }
46312531Sandreas.sandberg@arm.com}
46412531Sandreas.sandberg@arm.com
46512531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
46612531Sandreas.sandberg@arm.comArmSemihosting::callTmpNam(ThreadContext *tc, bool aarch64,
46712531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
46812531Sandreas.sandberg@arm.com{
46912531Sandreas.sandberg@arm.com    const Addr guest_buf = argv[1];
47012531Sandreas.sandberg@arm.com    //const uint64_t id = argv[2];
47112531Sandreas.sandberg@arm.com    const uint64_t max_len = argv[3];
47212531Sandreas.sandberg@arm.com
47312531Sandreas.sandberg@arm.com    std::vector<char> buf(L_tmpnam);
47412531Sandreas.sandberg@arm.com    char *path = tmpnam(buf.data());
47512531Sandreas.sandberg@arm.com    if (!path)
47612531Sandreas.sandberg@arm.com        return retError(EINVAL);
47712531Sandreas.sandberg@arm.com
47812531Sandreas.sandberg@arm.com    const size_t path_len = strlen(path);
47912531Sandreas.sandberg@arm.com    if (path_len >= max_len)
48012531Sandreas.sandberg@arm.com        return retError(ENOSPC);
48112531Sandreas.sandberg@arm.com
48214010Sgabeblack@google.com    physProxy(tc).writeBlob(guest_buf, path, path_len + 1);
48312531Sandreas.sandberg@arm.com    return retOK(0);
48412531Sandreas.sandberg@arm.com}
48512531Sandreas.sandberg@arm.com
48612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
48712531Sandreas.sandberg@arm.comArmSemihosting::callRemove(ThreadContext *tc, bool aarch64,
48812531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
48912531Sandreas.sandberg@arm.com{
49012531Sandreas.sandberg@arm.com    std::string fname = readString(tc, argv[1], argv[2]);
49112531Sandreas.sandberg@arm.com
49212531Sandreas.sandberg@arm.com    if (remove(fname.c_str()) != 0) {
49312531Sandreas.sandberg@arm.com        return retError(errno);
49412531Sandreas.sandberg@arm.com    } else {
49512531Sandreas.sandberg@arm.com        return retOK(0);
49612531Sandreas.sandberg@arm.com    }
49712531Sandreas.sandberg@arm.com}
49812531Sandreas.sandberg@arm.com
49912531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
50012531Sandreas.sandberg@arm.comArmSemihosting::callRename(ThreadContext *tc, bool aarch64,
50112531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
50212531Sandreas.sandberg@arm.com{
50312531Sandreas.sandberg@arm.com    std::string from = readString(tc, argv[1], argv[2]);
50412531Sandreas.sandberg@arm.com    std::string to = readString(tc, argv[3], argv[4]);
50512531Sandreas.sandberg@arm.com
50612531Sandreas.sandberg@arm.com    if (rename(from.c_str(), to.c_str()) != 0) {
50712531Sandreas.sandberg@arm.com        return retError(errno);
50812531Sandreas.sandberg@arm.com    } else {
50912531Sandreas.sandberg@arm.com        return retOK(0);
51012531Sandreas.sandberg@arm.com    }
51112531Sandreas.sandberg@arm.com}
51212531Sandreas.sandberg@arm.com
51312531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
51412531Sandreas.sandberg@arm.comArmSemihosting::callClock(ThreadContext *tc, bool aarch64,
51512531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
51612531Sandreas.sandberg@arm.com{
51712531Sandreas.sandberg@arm.com    return retOK(curTick() / (SimClock::Int::s / 100));
51812531Sandreas.sandberg@arm.com}
51912531Sandreas.sandberg@arm.com
52012531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
52112531Sandreas.sandberg@arm.comArmSemihosting::callTime(ThreadContext *tc, bool aarch64,
52212531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
52312531Sandreas.sandberg@arm.com{
52412531Sandreas.sandberg@arm.com    return retOK(timeBase + round(curTick() / SimClock::Float::s));
52512531Sandreas.sandberg@arm.com}
52612531Sandreas.sandberg@arm.com
52712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
52812531Sandreas.sandberg@arm.comArmSemihosting::callSystem(ThreadContext *tc, bool aarch64,
52912531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
53012531Sandreas.sandberg@arm.com{
53112531Sandreas.sandberg@arm.com    const std::string cmd = readString(tc, argv[1], argv[2]);
53212531Sandreas.sandberg@arm.com    warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
53312531Sandreas.sandberg@arm.com         cmd);
53412531Sandreas.sandberg@arm.com    return retError(EINVAL);
53512531Sandreas.sandberg@arm.com
53612531Sandreas.sandberg@arm.com}
53712531Sandreas.sandberg@arm.com
53812531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
53912531Sandreas.sandberg@arm.comArmSemihosting::callErrno(ThreadContext *tc, bool aarch64,
54012531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
54112531Sandreas.sandberg@arm.com{
54212531Sandreas.sandberg@arm.com    // Preserve errno by returning it in errno as well.
54312531Sandreas.sandberg@arm.com    return RetErrno(semiErrno, semiErrno);
54412531Sandreas.sandberg@arm.com}
54512531Sandreas.sandberg@arm.com
54612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
54712531Sandreas.sandberg@arm.comArmSemihosting::callGetCmdLine(ThreadContext *tc, bool aarch64,
54812531Sandreas.sandberg@arm.com                               std::vector<uint64_t> &argv)
54912531Sandreas.sandberg@arm.com{
55012531Sandreas.sandberg@arm.com    if (cmdLine.size() + 1 < argv[2]) {
55112533Sandreas.sandberg@arm.com        PortProxy &proxy = physProxy(tc);
55212531Sandreas.sandberg@arm.com        ByteOrder endian = ArmISA::byteOrder(tc);
55314010Sgabeblack@google.com        proxy.writeBlob((Addr)argv[1], cmdLine.c_str(), cmdLine.size() + 1);
55412531Sandreas.sandberg@arm.com
55512531Sandreas.sandberg@arm.com        if (aarch64)
55613893Sgabeblack@google.com            proxy.write<uint64_t>(argv[0] + 1 * 8, cmdLine.size(), endian);
55712531Sandreas.sandberg@arm.com        else
55813893Sgabeblack@google.com            proxy.write<uint32_t>(argv[0] + 1 * 4, cmdLine.size(), endian);
55912531Sandreas.sandberg@arm.com        return retOK(0);
56012531Sandreas.sandberg@arm.com    } else {
56112531Sandreas.sandberg@arm.com        return retError(0);
56212531Sandreas.sandberg@arm.com    }
56312531Sandreas.sandberg@arm.com}
56412531Sandreas.sandberg@arm.com
56512531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
56612531Sandreas.sandberg@arm.comArmSemihosting::callHeapInfo(ThreadContext *tc, bool aarch64,
56712531Sandreas.sandberg@arm.com                             std::vector<uint64_t> &argv)
56812531Sandreas.sandberg@arm.com{
56912531Sandreas.sandberg@arm.com    const PhysicalMemory &phys = tc->getSystemPtr()->getPhysMem();
57012531Sandreas.sandberg@arm.com    const AddrRangeList memories = phys.getConfAddrRanges();
57112531Sandreas.sandberg@arm.com    fatal_if(memories.size() < 1, "No memories reported from System");
57212531Sandreas.sandberg@arm.com    warn_if(memories.size() > 1, "Multiple physical memory ranges available. "
57312531Sandreas.sandberg@arm.com            "Using first range heap/stack.");
57412531Sandreas.sandberg@arm.com    const AddrRange memory = *memories.begin();
57512531Sandreas.sandberg@arm.com    const Addr mem_start = memory.start() + memReserve;
57612531Sandreas.sandberg@arm.com    Addr mem_end = memory.end();
57712531Sandreas.sandberg@arm.com
57812531Sandreas.sandberg@arm.com    // Make sure that 32-bit guests can access their memory.
57912531Sandreas.sandberg@arm.com    if (!aarch64) {
58012531Sandreas.sandberg@arm.com        const Addr phys_max = (1ULL << 32) - 1;
58112531Sandreas.sandberg@arm.com        panic_if(mem_start > phys_max,
58212531Sandreas.sandberg@arm.com                 "Physical memory out of range for a 32-bit guest.");
58312531Sandreas.sandberg@arm.com        if (mem_end > phys_max) {
58412531Sandreas.sandberg@arm.com            warn("Some physical memory out of range for a 32-bit guest.");
58512531Sandreas.sandberg@arm.com            mem_end = phys_max;
58612531Sandreas.sandberg@arm.com        }
58712531Sandreas.sandberg@arm.com    }
58812531Sandreas.sandberg@arm.com
58912531Sandreas.sandberg@arm.com    fatal_if(mem_start + stackSize >= mem_end,
59012531Sandreas.sandberg@arm.com             "Physical memory too small to fit desired stack and a heap.");
59112531Sandreas.sandberg@arm.com
59212531Sandreas.sandberg@arm.com    const Addr heap_base = mem_start;
59312531Sandreas.sandberg@arm.com    const Addr heap_limit = mem_end - stackSize + 1;
59412531Sandreas.sandberg@arm.com    const Addr stack_base = (mem_end + 1) & ~0x7ULL; // 8 byte stack alignment
59512531Sandreas.sandberg@arm.com    const Addr stack_limit = heap_limit;
59612531Sandreas.sandberg@arm.com
59712531Sandreas.sandberg@arm.com
59812531Sandreas.sandberg@arm.com    inform("Reporting heap/stack info to guest:\n"
59912531Sandreas.sandberg@arm.com           "\tHeap base: 0x%x\n"
60012531Sandreas.sandberg@arm.com           "\tHeap limit: 0x%x\n"
60112531Sandreas.sandberg@arm.com           "\tStack base: 0x%x\n"
60212531Sandreas.sandberg@arm.com           "\tStack limit: 0x%x\n",
60312531Sandreas.sandberg@arm.com           heap_base, heap_limit, stack_base, stack_limit);
60412531Sandreas.sandberg@arm.com
60512531Sandreas.sandberg@arm.com    Addr base = argv[1];
60612533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
60712531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
60812531Sandreas.sandberg@arm.com    if (aarch64) {
60913893Sgabeblack@google.com        proxy.write<uint64_t>(base + 0 * 8, heap_base, endian);
61013893Sgabeblack@google.com        proxy.write<uint64_t>(base + 1 * 8, heap_limit, endian);
61113893Sgabeblack@google.com        proxy.write<uint64_t>(base + 2 * 8, stack_base, endian);
61213893Sgabeblack@google.com        proxy.write<uint64_t>(base + 3 * 8, stack_limit, endian);
61312531Sandreas.sandberg@arm.com    } else {
61413893Sgabeblack@google.com        proxy.write<uint32_t>(base + 0 * 4, heap_base, endian);
61513893Sgabeblack@google.com        proxy.write<uint32_t>(base + 1 * 4, heap_limit, endian);
61613893Sgabeblack@google.com        proxy.write<uint32_t>(base + 2 * 4, stack_base, endian);
61713893Sgabeblack@google.com        proxy.write<uint32_t>(base + 3 * 4, stack_limit, endian);
61812531Sandreas.sandberg@arm.com    }
61912531Sandreas.sandberg@arm.com
62012531Sandreas.sandberg@arm.com    return retOK(0);
62112531Sandreas.sandberg@arm.com}
62212531Sandreas.sandberg@arm.com
62312531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
62412531Sandreas.sandberg@arm.comArmSemihosting::callExit(ThreadContext *tc, bool aarch64,
62512531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
62612531Sandreas.sandberg@arm.com{
62712531Sandreas.sandberg@arm.com    if (aarch64) {
62812531Sandreas.sandberg@arm.com        semiExit(argv[1], argv[2]);
62912531Sandreas.sandberg@arm.com    } else {
63012531Sandreas.sandberg@arm.com        semiExit(argv[0], 0);
63112531Sandreas.sandberg@arm.com    }
63212531Sandreas.sandberg@arm.com
63312531Sandreas.sandberg@arm.com    return retOK(0);
63412531Sandreas.sandberg@arm.com}
63512531Sandreas.sandberg@arm.com
63612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
63712531Sandreas.sandberg@arm.comArmSemihosting::callExitExtended(ThreadContext *tc, bool aarch64,
63812531Sandreas.sandberg@arm.com                                 std::vector<uint64_t> &argv)
63912531Sandreas.sandberg@arm.com{
64012531Sandreas.sandberg@arm.com    semiExit(argv[1], argv[2]);
64112531Sandreas.sandberg@arm.com
64212531Sandreas.sandberg@arm.com    return retOK(0);
64312531Sandreas.sandberg@arm.com}
64412531Sandreas.sandberg@arm.com
64512531Sandreas.sandberg@arm.comvoid
64612531Sandreas.sandberg@arm.comArmSemihosting::semiExit(uint64_t code, uint64_t subcode)
64712531Sandreas.sandberg@arm.com{
64812531Sandreas.sandberg@arm.com    auto it = exitCodes.find(code);
64912531Sandreas.sandberg@arm.com    if (it != exitCodes.end()) {
65012531Sandreas.sandberg@arm.com        exitSimLoop(it->second, subcode);
65112531Sandreas.sandberg@arm.com    } else {
65212531Sandreas.sandberg@arm.com        exitSimLoop(csprintf("semi:0x%x", code), subcode);
65312531Sandreas.sandberg@arm.com    }
65412531Sandreas.sandberg@arm.com}
65512531Sandreas.sandberg@arm.com
65612531Sandreas.sandberg@arm.com
65712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
65812531Sandreas.sandberg@arm.comArmSemihosting::callElapsed(ThreadContext *tc, bool aarch64,
65912531Sandreas.sandberg@arm.com                            std::vector<uint64_t> &argv)
66012531Sandreas.sandberg@arm.com{
66112533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
66212531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
66312531Sandreas.sandberg@arm.com    const uint64_t tick = semiTick(curTick());
66412531Sandreas.sandberg@arm.com
66512531Sandreas.sandberg@arm.com    if (aarch64) {
66613893Sgabeblack@google.com        proxy.write<uint64_t>(argv[0], tick, endian);
66712531Sandreas.sandberg@arm.com    } else {
66813893Sgabeblack@google.com        proxy.write<uint32_t>(argv[0] + 0 * 4, tick, endian);
66913893Sgabeblack@google.com        proxy.write<uint32_t>(argv[0] + 1 * 4, tick >> 32, endian);
67012531Sandreas.sandberg@arm.com    }
67112531Sandreas.sandberg@arm.com
67212531Sandreas.sandberg@arm.com    return retOK(0);
67312531Sandreas.sandberg@arm.com}
67412531Sandreas.sandberg@arm.com
67512531Sandreas.sandberg@arm.com
67612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
67712531Sandreas.sandberg@arm.comArmSemihosting::callTickFreq(ThreadContext *tc, bool aarch64,
67812531Sandreas.sandberg@arm.com                             std::vector<uint64_t> &argv)
67912531Sandreas.sandberg@arm.com{
68012531Sandreas.sandberg@arm.com    return retOK(semiTick(SimClock::Frequency));
68112531Sandreas.sandberg@arm.com}
68212531Sandreas.sandberg@arm.com
68312531Sandreas.sandberg@arm.comconst ArmSemihosting::SemiCall *
68412531Sandreas.sandberg@arm.comArmSemihosting::getCall(uint32_t op, bool aarch64)
68512531Sandreas.sandberg@arm.com{
68612531Sandreas.sandberg@arm.com    auto it = calls.find(op);
68712531Sandreas.sandberg@arm.com    if (it == calls.end())
68812531Sandreas.sandberg@arm.com        return nullptr;
68912531Sandreas.sandberg@arm.com    else {
69012531Sandreas.sandberg@arm.com        return &it->second;
69112531Sandreas.sandberg@arm.com    }
69212531Sandreas.sandberg@arm.com}
69312531Sandreas.sandberg@arm.com
69412698Sandreas.sandberg@arm.comFILE *
69512698Sandreas.sandberg@arm.comArmSemihosting::getSTDIO(const char *stream_name,
69612698Sandreas.sandberg@arm.com                         const std::string &name, const char *mode)
69712698Sandreas.sandberg@arm.com{
69812698Sandreas.sandberg@arm.com    auto it = stdioMap.find(name);
69912698Sandreas.sandberg@arm.com    if (it == stdioMap.end()) {
70012698Sandreas.sandberg@arm.com        FILE *f = fopen(name.c_str(), mode);
70112698Sandreas.sandberg@arm.com        if (!f) {
70212698Sandreas.sandberg@arm.com            fatal("Failed to open %s (%s): %s\n",
70312698Sandreas.sandberg@arm.com                  stream_name, name, strerror(errno));
70412698Sandreas.sandberg@arm.com        }
70512698Sandreas.sandberg@arm.com        return f;
70612698Sandreas.sandberg@arm.com    } else {
70712698Sandreas.sandberg@arm.com        return it->second;
70812698Sandreas.sandberg@arm.com    }
70912698Sandreas.sandberg@arm.com}
71012698Sandreas.sandberg@arm.com
71112531Sandreas.sandberg@arm.comstd::unique_ptr<ArmSemihosting::FileBase>
71212531Sandreas.sandberg@arm.comArmSemihosting::FileBase::create(
71312531Sandreas.sandberg@arm.com    ArmSemihosting &parent, const std::string &fname, const char *mode)
71412531Sandreas.sandberg@arm.com{
71512531Sandreas.sandberg@arm.com    std::unique_ptr<FileBase> file;
71612531Sandreas.sandberg@arm.com    if (fname == ":semihosting-features") {
71712531Sandreas.sandberg@arm.com        file.reset(new FileFeatures(parent, fname.c_str(), mode));
71812531Sandreas.sandberg@arm.com    } else {
71912531Sandreas.sandberg@arm.com        file.reset(new File(parent, fname.c_str(), mode));
72012531Sandreas.sandberg@arm.com    }
72112531Sandreas.sandberg@arm.com
72212531Sandreas.sandberg@arm.com    return file;
72312531Sandreas.sandberg@arm.com}
72412531Sandreas.sandberg@arm.com
72512531Sandreas.sandberg@arm.comstd::unique_ptr<ArmSemihosting::FileBase>
72612531Sandreas.sandberg@arm.comArmSemihosting::FileBase::create(ArmSemihosting &parent,
72712531Sandreas.sandberg@arm.com                                 CheckpointIn &cp, const std::string &sec)
72812531Sandreas.sandberg@arm.com{
72912531Sandreas.sandberg@arm.com    std::unique_ptr<FileBase> file;
73012531Sandreas.sandberg@arm.com    ScopedCheckpointSection _sec(cp, sec);
73112531Sandreas.sandberg@arm.com
73212531Sandreas.sandberg@arm.com    // Was the file open when the checkpoint was created?
73312531Sandreas.sandberg@arm.com    if (!cp.sectionExists(Serializable::currentSection()))
73412531Sandreas.sandberg@arm.com        return file;
73512531Sandreas.sandberg@arm.com
73612531Sandreas.sandberg@arm.com    std::string fname, mode;
73712531Sandreas.sandberg@arm.com    paramIn(cp, "name", fname);
73812531Sandreas.sandberg@arm.com    paramIn(cp, "mode", mode);
73912531Sandreas.sandberg@arm.com    file = create(parent, fname, mode.c_str());
74012531Sandreas.sandberg@arm.com    assert(file);
74112531Sandreas.sandberg@arm.com    file->unserialize(cp);
74212531Sandreas.sandberg@arm.com
74312531Sandreas.sandberg@arm.com    return file;
74412531Sandreas.sandberg@arm.com}
74512531Sandreas.sandberg@arm.com
74612531Sandreas.sandberg@arm.comvoid
74712531Sandreas.sandberg@arm.comArmSemihosting::FileBase::serialize(CheckpointOut &cp) const
74812531Sandreas.sandberg@arm.com{
74912531Sandreas.sandberg@arm.com    paramOut(cp, "name", _name);
75012531Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(mode);
75112531Sandreas.sandberg@arm.com}
75212531Sandreas.sandberg@arm.com
75312531Sandreas.sandberg@arm.comvoid
75412531Sandreas.sandberg@arm.comArmSemihosting::FileBase::unserialize(CheckpointIn &cp)
75512531Sandreas.sandberg@arm.com{
75612531Sandreas.sandberg@arm.com    /* Unserialization of name and mode happens in
75712531Sandreas.sandberg@arm.com     * ArmSemihosting::FileBase::create() */
75812531Sandreas.sandberg@arm.com}
75912531Sandreas.sandberg@arm.com
76012531Sandreas.sandberg@arm.comint64_t
76112531Sandreas.sandberg@arm.comArmSemihosting::FileBase::read(uint8_t *buffer, uint64_t size)
76212531Sandreas.sandberg@arm.com{
76312531Sandreas.sandberg@arm.com    return -EINVAL;
76412531Sandreas.sandberg@arm.com}
76512531Sandreas.sandberg@arm.com
76612531Sandreas.sandberg@arm.comint64_t
76712531Sandreas.sandberg@arm.comArmSemihosting::FileBase::write(const uint8_t *buffer, uint64_t size)
76812531Sandreas.sandberg@arm.com{
76912531Sandreas.sandberg@arm.com    return -EINVAL;
77012531Sandreas.sandberg@arm.com}
77112531Sandreas.sandberg@arm.com
77212531Sandreas.sandberg@arm.comint64_t
77312531Sandreas.sandberg@arm.comArmSemihosting::FileBase::seek(uint64_t pos)
77412531Sandreas.sandberg@arm.com{
77512531Sandreas.sandberg@arm.com    return -EINVAL;
77612531Sandreas.sandberg@arm.com}
77712531Sandreas.sandberg@arm.com
77812531Sandreas.sandberg@arm.comint64_t
77912531Sandreas.sandberg@arm.comArmSemihosting::FileBase::flen()
78012531Sandreas.sandberg@arm.com{
78112531Sandreas.sandberg@arm.com    return -EINVAL;
78212531Sandreas.sandberg@arm.com}
78312531Sandreas.sandberg@arm.com
78412531Sandreas.sandberg@arm.com
78512531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::FileFeatures(
78612531Sandreas.sandberg@arm.com    ArmSemihosting &_parent, const char *_name, const char *_mode)
78712531Sandreas.sandberg@arm.com    : FileBase(_parent, _name, _mode)
78812531Sandreas.sandberg@arm.com{
78912531Sandreas.sandberg@arm.com}
79012531Sandreas.sandberg@arm.com
79112531Sandreas.sandberg@arm.comint64_t
79212531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::read(uint8_t *buffer, uint64_t size)
79312531Sandreas.sandberg@arm.com{
79412531Sandreas.sandberg@arm.com    int64_t len = 0;
79512531Sandreas.sandberg@arm.com
79612531Sandreas.sandberg@arm.com    for (; pos < size && pos < ArmSemihosting::features.size(); pos++)
79712531Sandreas.sandberg@arm.com        buffer[len++] = ArmSemihosting::features[pos];
79812531Sandreas.sandberg@arm.com
79912531Sandreas.sandberg@arm.com    return len;
80012531Sandreas.sandberg@arm.com}
80112531Sandreas.sandberg@arm.com
80212531Sandreas.sandberg@arm.comint64_t
80312531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::seek(uint64_t _pos)
80412531Sandreas.sandberg@arm.com{
80512531Sandreas.sandberg@arm.com    if (_pos < ArmSemihosting::features.size()) {
80612531Sandreas.sandberg@arm.com        pos = _pos;
80712531Sandreas.sandberg@arm.com        return 0;
80812531Sandreas.sandberg@arm.com    } else {
80912531Sandreas.sandberg@arm.com        return -ENXIO;
81012531Sandreas.sandberg@arm.com    }
81112531Sandreas.sandberg@arm.com}
81212531Sandreas.sandberg@arm.com
81312531Sandreas.sandberg@arm.comvoid
81412531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::serialize(CheckpointOut &cp) const
81512531Sandreas.sandberg@arm.com{
81612531Sandreas.sandberg@arm.com    FileBase::serialize(cp);
81712531Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(pos);
81812531Sandreas.sandberg@arm.com}
81912531Sandreas.sandberg@arm.com
82012531Sandreas.sandberg@arm.comvoid
82112531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::unserialize(CheckpointIn &cp)
82212531Sandreas.sandberg@arm.com{
82312531Sandreas.sandberg@arm.com    FileBase::unserialize(cp);
82412531Sandreas.sandberg@arm.com    UNSERIALIZE_SCALAR(pos);
82512531Sandreas.sandberg@arm.com}
82612531Sandreas.sandberg@arm.com
82712531Sandreas.sandberg@arm.com
82812531Sandreas.sandberg@arm.com
82912531Sandreas.sandberg@arm.comArmSemihosting::File::File(ArmSemihosting &_parent,
83012531Sandreas.sandberg@arm.com                           const char *_name, const char *_perms)
83112531Sandreas.sandberg@arm.com    : FileBase(_parent, _name, _perms),
83212531Sandreas.sandberg@arm.com      file(nullptr)
83312531Sandreas.sandberg@arm.com{
83412531Sandreas.sandberg@arm.com}
83512531Sandreas.sandberg@arm.com
83612531Sandreas.sandberg@arm.comArmSemihosting::File::~File()
83712531Sandreas.sandberg@arm.com{
83812531Sandreas.sandberg@arm.com    if (file)
83912531Sandreas.sandberg@arm.com        close();
84012531Sandreas.sandberg@arm.com}
84112531Sandreas.sandberg@arm.com
84212531Sandreas.sandberg@arm.comint64_t
84312531Sandreas.sandberg@arm.comArmSemihosting::File::openImpl(bool in_cpt)
84412531Sandreas.sandberg@arm.com{
84512531Sandreas.sandberg@arm.com    panic_if(file, "Trying to open an already open file.\n");
84612531Sandreas.sandberg@arm.com
84712531Sandreas.sandberg@arm.com    if (_name == ":tt") {
84812531Sandreas.sandberg@arm.com        if (mode[0] == 'r') {
84912698Sandreas.sandberg@arm.com            file = parent.stdin;
85012531Sandreas.sandberg@arm.com        } else if (mode[0] == 'w') {
85112698Sandreas.sandberg@arm.com            file = parent.stdout;
85212531Sandreas.sandberg@arm.com        } else if (mode[0] == 'a') {
85312698Sandreas.sandberg@arm.com            file = parent.stderr;
85412531Sandreas.sandberg@arm.com        } else {
85512531Sandreas.sandberg@arm.com            warn("Unknown file mode for the ':tt' special file");
85612531Sandreas.sandberg@arm.com            return -EINVAL;
85712531Sandreas.sandberg@arm.com        }
85812531Sandreas.sandberg@arm.com    } else {
85912531Sandreas.sandberg@arm.com        std::string real_mode(this->mode);
86012531Sandreas.sandberg@arm.com        // Avoid truncating the file if we are restoring from a
86112531Sandreas.sandberg@arm.com        // checkpoint.
86212531Sandreas.sandberg@arm.com        if (in_cpt && real_mode[0] == 'w')
86312531Sandreas.sandberg@arm.com            real_mode[0] = 'a';
86412531Sandreas.sandberg@arm.com
86512531Sandreas.sandberg@arm.com        file = fopen(_name.c_str(), real_mode.c_str());
86612531Sandreas.sandberg@arm.com    }
86712531Sandreas.sandberg@arm.com
86812531Sandreas.sandberg@arm.com    return file ? 0 : -errno;
86912531Sandreas.sandberg@arm.com}
87012531Sandreas.sandberg@arm.com
87112531Sandreas.sandberg@arm.comint64_t
87212531Sandreas.sandberg@arm.comArmSemihosting::File::close()
87312531Sandreas.sandberg@arm.com{
87412531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to close an already closed file.\n");
87512531Sandreas.sandberg@arm.com
87612531Sandreas.sandberg@arm.com    if (needClose()) {
87712531Sandreas.sandberg@arm.com        fclose(file);
87812531Sandreas.sandberg@arm.com    }
87912531Sandreas.sandberg@arm.com    file = nullptr;
88012531Sandreas.sandberg@arm.com
88112531Sandreas.sandberg@arm.com    return 0;
88212531Sandreas.sandberg@arm.com}
88312531Sandreas.sandberg@arm.com
88412531Sandreas.sandberg@arm.combool
88512531Sandreas.sandberg@arm.comArmSemihosting::File::isTTY() const
88612531Sandreas.sandberg@arm.com{
88712698Sandreas.sandberg@arm.com    return file == parent.stdout ||
88812698Sandreas.sandberg@arm.com        file == parent.stderr ||
88912698Sandreas.sandberg@arm.com        file == parent.stdin;
89012531Sandreas.sandberg@arm.com}
89112531Sandreas.sandberg@arm.com
89212531Sandreas.sandberg@arm.comint64_t
89312531Sandreas.sandberg@arm.comArmSemihosting::File::read(uint8_t *buffer, uint64_t size)
89412531Sandreas.sandberg@arm.com{
89512531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to read from a closed file");
89612531Sandreas.sandberg@arm.com
89712531Sandreas.sandberg@arm.com    size_t ret = fread(buffer, 1, size, file);
89812531Sandreas.sandberg@arm.com    if (ret == 0) {
89912531Sandreas.sandberg@arm.com        // Error or EOF. Assume errors are due to invalid file
90012531Sandreas.sandberg@arm.com        // operations (e.g., reading a write-only stream).
90112531Sandreas.sandberg@arm.com        return ferror(file) ? -EINVAL : 0;
90212531Sandreas.sandberg@arm.com    } else {
90312531Sandreas.sandberg@arm.com        return ret;
90412531Sandreas.sandberg@arm.com    }
90512531Sandreas.sandberg@arm.com}
90612531Sandreas.sandberg@arm.com
90712531Sandreas.sandberg@arm.comint64_t
90812531Sandreas.sandberg@arm.comArmSemihosting::File::write(const uint8_t *buffer, uint64_t size)
90912531Sandreas.sandberg@arm.com{
91012531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to write to a closed file");
91112531Sandreas.sandberg@arm.com
91212531Sandreas.sandberg@arm.com
91312531Sandreas.sandberg@arm.com    size_t ret = fwrite(buffer, 1, size, file);
91412531Sandreas.sandberg@arm.com    if (ret == 0) {
91512531Sandreas.sandberg@arm.com        // Assume errors are due to invalid file operations (e.g.,
91612531Sandreas.sandberg@arm.com        // writing a read-only stream).
91712531Sandreas.sandberg@arm.com        return -EINVAL;
91812531Sandreas.sandberg@arm.com    } else {
91912531Sandreas.sandberg@arm.com        return ret;
92012531Sandreas.sandberg@arm.com    }
92112531Sandreas.sandberg@arm.com}
92212531Sandreas.sandberg@arm.com
92312531Sandreas.sandberg@arm.comint64_t
92412531Sandreas.sandberg@arm.comArmSemihosting::File::seek(uint64_t _pos)
92512531Sandreas.sandberg@arm.com{
92612531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to seek in a closed file");
92712531Sandreas.sandberg@arm.com
92812531Sandreas.sandberg@arm.com    errno = 0;
92912531Sandreas.sandberg@arm.com    if (fseek(file, _pos, SEEK_SET) == 0)
93012531Sandreas.sandberg@arm.com        return 0;
93112531Sandreas.sandberg@arm.com    else
93212531Sandreas.sandberg@arm.com        return -errno;
93312531Sandreas.sandberg@arm.com}
93412531Sandreas.sandberg@arm.com
93512531Sandreas.sandberg@arm.comint64_t
93612531Sandreas.sandberg@arm.comArmSemihosting::File::flen()
93712531Sandreas.sandberg@arm.com{
93812531Sandreas.sandberg@arm.com    errno = 0;
93912531Sandreas.sandberg@arm.com    long pos = ftell(file);
94012531Sandreas.sandberg@arm.com    if (pos < 0)
94112531Sandreas.sandberg@arm.com        return -errno;
94212531Sandreas.sandberg@arm.com
94312531Sandreas.sandberg@arm.com    if (fseek(file, 0, SEEK_END) != 0)
94412531Sandreas.sandberg@arm.com        return -errno;
94512531Sandreas.sandberg@arm.com
94612531Sandreas.sandberg@arm.com    long len = ftell(file);
94712531Sandreas.sandberg@arm.com    if (len < 0)
94812531Sandreas.sandberg@arm.com        return -errno;
94912531Sandreas.sandberg@arm.com
95012531Sandreas.sandberg@arm.com    if (fseek(file, pos, SEEK_SET) != 0)
95112531Sandreas.sandberg@arm.com        return -errno;
95212531Sandreas.sandberg@arm.com
95312531Sandreas.sandberg@arm.com    return len;
95412531Sandreas.sandberg@arm.com}
95512531Sandreas.sandberg@arm.com
95612531Sandreas.sandberg@arm.com
95712531Sandreas.sandberg@arm.comvoid
95812531Sandreas.sandberg@arm.comArmSemihosting::File::serialize(CheckpointOut &cp) const
95912531Sandreas.sandberg@arm.com{
96012531Sandreas.sandberg@arm.com    FileBase::serialize(cp);
96112531Sandreas.sandberg@arm.com
96212531Sandreas.sandberg@arm.com    if (!isTTY()) {
96312531Sandreas.sandberg@arm.com        long pos = file ? ftell(file) : 0;
96412531Sandreas.sandberg@arm.com        panic_if(pos < 0, "Failed to get file position.");
96512531Sandreas.sandberg@arm.com        SERIALIZE_SCALAR(pos);
96612531Sandreas.sandberg@arm.com    }
96712531Sandreas.sandberg@arm.com}
96812531Sandreas.sandberg@arm.com
96912531Sandreas.sandberg@arm.comvoid
97012531Sandreas.sandberg@arm.comArmSemihosting::File::unserialize(CheckpointIn &cp)
97112531Sandreas.sandberg@arm.com{
97212531Sandreas.sandberg@arm.com    FileBase::unserialize(cp);
97312531Sandreas.sandberg@arm.com
97412531Sandreas.sandberg@arm.com    if (openImpl(true) < 0) {
97512531Sandreas.sandberg@arm.com        fatal("Failed to open file: %s", _name);
97612531Sandreas.sandberg@arm.com    }
97712531Sandreas.sandberg@arm.com
97812531Sandreas.sandberg@arm.com    if (!isTTY()) {
97912531Sandreas.sandberg@arm.com        long pos = 0;
98012531Sandreas.sandberg@arm.com        UNSERIALIZE_SCALAR(pos);
98112531Sandreas.sandberg@arm.com        if (fseek(file, pos, SEEK_SET) != 0) {
98212531Sandreas.sandberg@arm.com            fatal("Failed seek to current position (%i) in '%s'", pos, _name);
98312531Sandreas.sandberg@arm.com        }
98412531Sandreas.sandberg@arm.com    }
98512531Sandreas.sandberg@arm.com}
98612531Sandreas.sandberg@arm.com
98712531Sandreas.sandberg@arm.com
98812531Sandreas.sandberg@arm.comArmSemihosting *
98912531Sandreas.sandberg@arm.comArmSemihostingParams::create()
99012531Sandreas.sandberg@arm.com{
99112531Sandreas.sandberg@arm.com    return new ArmSemihosting(this);
99212531Sandreas.sandberg@arm.com}
993