semihosting.cc revision 12533
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"
5012531Sandreas.sandberg@arm.com#include "mem/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 } },
7912531Sandreas.sandberg@arm.com    { 0x15, { "SYS_GET_CMDLINE", &ArmSemihosting::callGetCmdLine, 1, 1} },
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
12412531Sandreas.sandberg@arm.comArmSemihosting::ArmSemihosting(const ArmSemihostingParams *p)
12512531Sandreas.sandberg@arm.com    : SimObject(p),
12612531Sandreas.sandberg@arm.com      cmdLine(p->cmd_line),
12712531Sandreas.sandberg@arm.com      memReserve(p->mem_reserve),
12812531Sandreas.sandberg@arm.com      stackSize(p->stack_size),
12912531Sandreas.sandberg@arm.com      timeBase([p]{ struct tm t = p->time; return mkutctime(&t); }()),
13012531Sandreas.sandberg@arm.com      tickShift(calcTickShift()),
13112531Sandreas.sandberg@arm.com      semiErrno(0)
13212531Sandreas.sandberg@arm.com{
13312531Sandreas.sandberg@arm.com    // Create an empty place-holder file for position 0 as semi-hosting
13412531Sandreas.sandberg@arm.com    // calls typically expect non-zero file handles.
13512531Sandreas.sandberg@arm.com    files.push_back(nullptr);
13612531Sandreas.sandberg@arm.com
13712531Sandreas.sandberg@arm.com    if (tickShift > 0)
13812531Sandreas.sandberg@arm.com        inform("Semihosting: Shifting elapsed ticks by %i bits.",
13912531Sandreas.sandberg@arm.com               tickShift);
14012531Sandreas.sandberg@arm.com}
14112531Sandreas.sandberg@arm.com
14212531Sandreas.sandberg@arm.comuint64_t
14312531Sandreas.sandberg@arm.comArmSemihosting::call64(ThreadContext *tc, uint32_t op, uint64_t param)
14412531Sandreas.sandberg@arm.com{
14512531Sandreas.sandberg@arm.com    const SemiCall *call = getCall(op, true);
14612531Sandreas.sandberg@arm.com    if (!call) {
14712531Sandreas.sandberg@arm.com        warn("Unknown aarch64 semihosting call: op = 0x%x, param = 0x%x",
14812531Sandreas.sandberg@arm.com             op, param);
14912531Sandreas.sandberg@arm.com
15012531Sandreas.sandberg@arm.com        return (uint64_t)-1;
15112531Sandreas.sandberg@arm.com    } else if (!call->implemented64()) {
15212531Sandreas.sandberg@arm.com        warn("Unimplemented aarch64 semihosting call: "
15312531Sandreas.sandberg@arm.com             "%s (op = 0x%x, param = 0x%x)",
15412531Sandreas.sandberg@arm.com             call->name, op, param);
15512531Sandreas.sandberg@arm.com
15612531Sandreas.sandberg@arm.com        return (uint64_t)-1;
15712531Sandreas.sandberg@arm.com    }
15812531Sandreas.sandberg@arm.com
15912531Sandreas.sandberg@arm.com    std::vector<uint64_t> argv(call->argc64 + 1);
16012533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
16112531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
16212531Sandreas.sandberg@arm.com
16312531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting call64: %s(0x%x)\n", call->name, param);
16412531Sandreas.sandberg@arm.com    argv[0] = param;
16512531Sandreas.sandberg@arm.com    for (int i = 0; i < call->argc64; ++i) {
16612531Sandreas.sandberg@arm.com        argv[i + 1] = proxy.readGtoH<uint64_t>(param + i * 8, endian);
16712531Sandreas.sandberg@arm.com        DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]);
16812531Sandreas.sandberg@arm.com    }
16912531Sandreas.sandberg@arm.com
17012531Sandreas.sandberg@arm.com    auto ret_errno = (this->*call->call)(tc, true, argv);
17112531Sandreas.sandberg@arm.com    semiErrno = ret_errno.second;
17212531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "\t ->: 0x%x, %i\n",
17312531Sandreas.sandberg@arm.com            ret_errno.first, ret_errno.second);
17412531Sandreas.sandberg@arm.com    return ret_errno.first;
17512531Sandreas.sandberg@arm.com}
17612531Sandreas.sandberg@arm.com
17712531Sandreas.sandberg@arm.comuint32_t
17812531Sandreas.sandberg@arm.comArmSemihosting::call32(ThreadContext *tc, uint32_t op, uint32_t param)
17912531Sandreas.sandberg@arm.com{
18012531Sandreas.sandberg@arm.com    const SemiCall *call = getCall(op, false);
18112531Sandreas.sandberg@arm.com    if (!call) {
18212531Sandreas.sandberg@arm.com        warn("Unknown aarch32 semihosting call: op = 0x%x, param = 0x%x",
18312531Sandreas.sandberg@arm.com             op, param);
18412531Sandreas.sandberg@arm.com
18512531Sandreas.sandberg@arm.com        return (uint32_t)-1;
18612531Sandreas.sandberg@arm.com    } else if (!call->implemented32()) {
18712531Sandreas.sandberg@arm.com        warn("Unimplemented aarch32 semihosting call: "
18812531Sandreas.sandberg@arm.com             "%s (op = 0x%x, param = 0x%x)",
18912531Sandreas.sandberg@arm.com             call->name, op, param);
19012531Sandreas.sandberg@arm.com
19112531Sandreas.sandberg@arm.com        return (uint32_t)-1;
19212531Sandreas.sandberg@arm.com    }
19312531Sandreas.sandberg@arm.com
19412531Sandreas.sandberg@arm.com    std::vector<uint64_t> argv(call->argc32 + 1);
19512533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
19612531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
19712531Sandreas.sandberg@arm.com
19812531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting call32: %s(0x%x)\n", call->name, param);
19912531Sandreas.sandberg@arm.com    argv[0] = param;
20012531Sandreas.sandberg@arm.com    for (int i = 0; i < call->argc32; ++i) {
20112531Sandreas.sandberg@arm.com        argv[i + 1] = proxy.readGtoH<uint32_t>(param + i * 4, endian);
20212531Sandreas.sandberg@arm.com        DPRINTF(Semihosting, "\t: 0x%x\n", argv[i + 1]);
20312531Sandreas.sandberg@arm.com    }
20412531Sandreas.sandberg@arm.com
20512531Sandreas.sandberg@arm.com    auto ret_errno = (this->*call->call)(tc, false, argv);
20612531Sandreas.sandberg@arm.com    semiErrno = ret_errno.second;
20712531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "\t ->: 0x%x, %i\n",
20812531Sandreas.sandberg@arm.com            ret_errno.first, ret_errno.second);
20912531Sandreas.sandberg@arm.com    return ret_errno.first;
21012531Sandreas.sandberg@arm.com}
21112531Sandreas.sandberg@arm.com
21212531Sandreas.sandberg@arm.comvoid
21312531Sandreas.sandberg@arm.comArmSemihosting::serialize(CheckpointOut &cp) const
21412531Sandreas.sandberg@arm.com{
21512531Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(semiErrno);
21612531Sandreas.sandberg@arm.com
21712531Sandreas.sandberg@arm.com    paramOut(cp, "num_files", files.size());
21812531Sandreas.sandberg@arm.com    for (int i = 0; i < files.size(); i++) {
21912531Sandreas.sandberg@arm.com        // File closed?
22012531Sandreas.sandberg@arm.com        if (!files[i])
22112531Sandreas.sandberg@arm.com            continue;
22212531Sandreas.sandberg@arm.com
22312531Sandreas.sandberg@arm.com        files[i]->serializeSection(cp, csprintf("file%i", i));
22412531Sandreas.sandberg@arm.com    }
22512531Sandreas.sandberg@arm.com}
22612531Sandreas.sandberg@arm.com
22712531Sandreas.sandberg@arm.comvoid
22812531Sandreas.sandberg@arm.comArmSemihosting::unserialize(CheckpointIn &cp)
22912531Sandreas.sandberg@arm.com{
23012531Sandreas.sandberg@arm.com    UNSERIALIZE_SCALAR(semiErrno);
23112531Sandreas.sandberg@arm.com
23212531Sandreas.sandberg@arm.com    size_t num_files;
23312531Sandreas.sandberg@arm.com    paramIn(cp, "num_files", num_files);
23412531Sandreas.sandberg@arm.com    files.resize(num_files);
23512531Sandreas.sandberg@arm.com    for (int i = 0; i < num_files; i++)
23612531Sandreas.sandberg@arm.com        files[i] = FileBase::create(*this, cp, csprintf("file%i", i));
23712531Sandreas.sandberg@arm.com}
23812531Sandreas.sandberg@arm.com
23912533Sandreas.sandberg@arm.comPortProxy &
24012533Sandreas.sandberg@arm.comArmSemihosting::physProxy(ThreadContext *tc)
24112533Sandreas.sandberg@arm.com{
24212533Sandreas.sandberg@arm.com    if (ArmISA::inSecureState(tc)) {
24312533Sandreas.sandberg@arm.com        if (!physProxyS) {
24412533Sandreas.sandberg@arm.com            System *sys = tc->getSystemPtr();
24512533Sandreas.sandberg@arm.com            physProxyS.reset(new SecurePortProxy(
24612533Sandreas.sandberg@arm.com                                 sys->getSystemPort(),
24712533Sandreas.sandberg@arm.com                                 sys->cacheLineSize()));
24812533Sandreas.sandberg@arm.com        }
24912533Sandreas.sandberg@arm.com        return *physProxyS;
25012533Sandreas.sandberg@arm.com    } else {
25112533Sandreas.sandberg@arm.com        return tc->getPhysProxy();
25212533Sandreas.sandberg@arm.com    }
25312533Sandreas.sandberg@arm.com}
25412533Sandreas.sandberg@arm.com
25512533Sandreas.sandberg@arm.com
25612531Sandreas.sandberg@arm.comstd::string
25712531Sandreas.sandberg@arm.comArmSemihosting::readString(ThreadContext *tc, Addr ptr, size_t len)
25812531Sandreas.sandberg@arm.com{
25912531Sandreas.sandberg@arm.com    std::vector<char> buf(len + 1);
26012531Sandreas.sandberg@arm.com
26112531Sandreas.sandberg@arm.com    buf[len] = '\0';
26212533Sandreas.sandberg@arm.com    physProxy(tc).readBlob(ptr, (uint8_t *)buf.data(), len);
26312531Sandreas.sandberg@arm.com
26412531Sandreas.sandberg@arm.com    return std::string(buf.data());
26512531Sandreas.sandberg@arm.com}
26612531Sandreas.sandberg@arm.com
26712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
26812531Sandreas.sandberg@arm.comArmSemihosting::callOpen(ThreadContext *tc, bool aarch64,
26912531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
27012531Sandreas.sandberg@arm.com{
27112531Sandreas.sandberg@arm.com    const Addr name_base = argv[1];
27212531Sandreas.sandberg@arm.com    const char *mode = argv[2] < fmodes.size() ? fmodes[argv[2]] : nullptr;
27312531Sandreas.sandberg@arm.com    const Addr name_size = argv[3];
27412531Sandreas.sandberg@arm.com
27512531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_OPEN(0x%x, %i[%s], %i)\n",
27612531Sandreas.sandberg@arm.com            name_base, argv[2], mode ? mode : "-", name_size);
27712531Sandreas.sandberg@arm.com    if (!mode || !name_base)
27812531Sandreas.sandberg@arm.com        return retError(EINVAL);
27912531Sandreas.sandberg@arm.com
28012531Sandreas.sandberg@arm.com    std::string fname = readString(tc, name_base, name_size);
28112531Sandreas.sandberg@arm.com
28212531Sandreas.sandberg@arm.com    std::unique_ptr<ArmSemihosting::FileBase> file =
28312531Sandreas.sandberg@arm.com        FileBase::create(*this, fname, mode);
28412531Sandreas.sandberg@arm.com    int64_t ret = file->open();
28512531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_OPEN(\"%s\", %i[%s]): %i\n",
28612531Sandreas.sandberg@arm.com            fname, argv[2], mode, ret);
28712531Sandreas.sandberg@arm.com    if (ret < 0) {
28812531Sandreas.sandberg@arm.com        return retError(-ret);
28912531Sandreas.sandberg@arm.com    } else {
29012531Sandreas.sandberg@arm.com        files.push_back(std::move(file));
29112531Sandreas.sandberg@arm.com        return retOK(files.size() - 1);
29212531Sandreas.sandberg@arm.com    }
29312531Sandreas.sandberg@arm.com}
29412531Sandreas.sandberg@arm.com
29512531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
29612531Sandreas.sandberg@arm.comArmSemihosting::callClose(ThreadContext *tc, bool aarch64,
29712531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
29812531Sandreas.sandberg@arm.com{
29912531Sandreas.sandberg@arm.com    if (argv[1] > files.size()) {
30012531Sandreas.sandberg@arm.com        DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i): Illegal file\n");
30112531Sandreas.sandberg@arm.com        return retError(EBADF);
30212531Sandreas.sandberg@arm.com    }
30312531Sandreas.sandberg@arm.com
30412531Sandreas.sandberg@arm.com    std::unique_ptr<FileBase> &file = files[argv[1]];
30512531Sandreas.sandberg@arm.com    int64_t error = file->close();
30612531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_CLOSE(%i[%s]): %i\n",
30712531Sandreas.sandberg@arm.com            argv[1], file->fileName(), error);
30812531Sandreas.sandberg@arm.com    if (error < 0) {
30912531Sandreas.sandberg@arm.com        return retError(-error);
31012531Sandreas.sandberg@arm.com    } else {
31112531Sandreas.sandberg@arm.com        // Zap the pointer and free the entry in the file table as
31212531Sandreas.sandberg@arm.com        // well.
31312531Sandreas.sandberg@arm.com        files[argv[1]].reset();
31412531Sandreas.sandberg@arm.com        return retOK(0);
31512531Sandreas.sandberg@arm.com    }
31612531Sandreas.sandberg@arm.com}
31712531Sandreas.sandberg@arm.com
31812531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
31912531Sandreas.sandberg@arm.comArmSemihosting::callWriteC(ThreadContext *tc, bool aarch64,
32012531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
32112531Sandreas.sandberg@arm.com{
32212533Sandreas.sandberg@arm.com    const char c = physProxy(tc).read<char>(argv[0]);
32312531Sandreas.sandberg@arm.com
32412531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_WRITEC('%c')\n", c);
32512531Sandreas.sandberg@arm.com    std::cout.put(c);
32612531Sandreas.sandberg@arm.com
32712531Sandreas.sandberg@arm.com    return retOK(0);
32812531Sandreas.sandberg@arm.com}
32912531Sandreas.sandberg@arm.com
33012531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
33112531Sandreas.sandberg@arm.comArmSemihosting::callWrite0(ThreadContext *tc, bool aarch64,
33212531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
33312531Sandreas.sandberg@arm.com{
33412531Sandreas.sandberg@arm.com    DPRINTF(Semihosting, "Semihosting SYS_WRITE0(...)\n");
33512533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
33612531Sandreas.sandberg@arm.com    for (Addr addr = (Addr)argv[0]; ; ++addr) {
33712531Sandreas.sandberg@arm.com        char data = proxy.read<char>(addr);
33812531Sandreas.sandberg@arm.com        if (data == 0)
33912531Sandreas.sandberg@arm.com            break;
34012531Sandreas.sandberg@arm.com
34112531Sandreas.sandberg@arm.com        std::cout.put(data);
34212531Sandreas.sandberg@arm.com    }
34312531Sandreas.sandberg@arm.com
34412531Sandreas.sandberg@arm.com    return retOK(0);
34512531Sandreas.sandberg@arm.com}
34612531Sandreas.sandberg@arm.com
34712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
34812531Sandreas.sandberg@arm.comArmSemihosting::callWrite(ThreadContext *tc, bool aarch64,
34912531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
35012531Sandreas.sandberg@arm.com{
35112531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
35212531Sandreas.sandberg@arm.com        return RetErrno(argv[3], EBADF);
35312531Sandreas.sandberg@arm.com
35412531Sandreas.sandberg@arm.com    std::vector<uint8_t> buffer(argv[3]);
35512533Sandreas.sandberg@arm.com    physProxy(tc).readBlob(argv[2], buffer.data(), buffer.size());
35612531Sandreas.sandberg@arm.com
35712531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->write(buffer.data(), buffer.size());
35812531Sandreas.sandberg@arm.com    if (ret < 0) {
35912531Sandreas.sandberg@arm.com        // No bytes written (we're returning the number of bytes not
36012531Sandreas.sandberg@arm.com        // written)
36112531Sandreas.sandberg@arm.com        return RetErrno(argv[3], -ret);
36212531Sandreas.sandberg@arm.com    } else {
36312531Sandreas.sandberg@arm.com        // Return the number of bytes not written
36412531Sandreas.sandberg@arm.com        return RetErrno(argv[3] - ret, 0);
36512531Sandreas.sandberg@arm.com    }
36612531Sandreas.sandberg@arm.com}
36712531Sandreas.sandberg@arm.com
36812531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
36912531Sandreas.sandberg@arm.comArmSemihosting::callRead(ThreadContext *tc, bool aarch64,
37012531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
37112531Sandreas.sandberg@arm.com{
37212531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
37312531Sandreas.sandberg@arm.com        return RetErrno(argv[3], EBADF);
37412531Sandreas.sandberg@arm.com
37512531Sandreas.sandberg@arm.com    std::vector<uint8_t> buffer(argv[3]);
37612531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->read(buffer.data(), buffer.size());
37712531Sandreas.sandberg@arm.com    if (ret < 0) {
37812531Sandreas.sandberg@arm.com        return RetErrno(argv[3], -ret);
37912531Sandreas.sandberg@arm.com    } else {
38012531Sandreas.sandberg@arm.com        panic_if(ret > buffer.size(), "Read longer than buffer size.");
38112531Sandreas.sandberg@arm.com
38212533Sandreas.sandberg@arm.com        physProxy(tc).writeBlob(argv[2], buffer.data(), ret);
38312531Sandreas.sandberg@arm.com
38412531Sandreas.sandberg@arm.com        // Return the number of bytes not written
38512531Sandreas.sandberg@arm.com        return retOK(argv[3] - ret);
38612531Sandreas.sandberg@arm.com    }
38712531Sandreas.sandberg@arm.com}
38812531Sandreas.sandberg@arm.com
38912531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
39012531Sandreas.sandberg@arm.comArmSemihosting::callReadC(ThreadContext *tc, bool aarch64,
39112531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
39212531Sandreas.sandberg@arm.com{
39312531Sandreas.sandberg@arm.com    return retOK((char)std::cin.get());
39412531Sandreas.sandberg@arm.com}
39512531Sandreas.sandberg@arm.com
39612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
39712531Sandreas.sandberg@arm.comArmSemihosting::callIsError(ThreadContext *tc, bool aarch64,
39812531Sandreas.sandberg@arm.com                            std::vector<uint64_t> &argv)
39912531Sandreas.sandberg@arm.com{
40012531Sandreas.sandberg@arm.com    // Sign extend from a 32 bit integer in aarch32 since the argument
40112531Sandreas.sandberg@arm.com    // reader zero extends to a uint64_t.
40212531Sandreas.sandberg@arm.com    const int64_t status = (int64_t)(aarch64 ? argv[1] :sext<32>(argv[1]));
40312531Sandreas.sandberg@arm.com    // Assume there was an error if the status value is negative.
40412531Sandreas.sandberg@arm.com    return retOK(status < 0 ? 1 : 0);
40512531Sandreas.sandberg@arm.com}
40612531Sandreas.sandberg@arm.com
40712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
40812531Sandreas.sandberg@arm.comArmSemihosting::callIsTTY(ThreadContext *tc, bool aarch64,
40912531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
41012531Sandreas.sandberg@arm.com{
41112531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
41212531Sandreas.sandberg@arm.com        return retError(EBADF);
41312531Sandreas.sandberg@arm.com
41412531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->isTTY();
41512531Sandreas.sandberg@arm.com    if (ret < 0) {
41612531Sandreas.sandberg@arm.com        return retError(-ret);
41712531Sandreas.sandberg@arm.com    } else {
41812531Sandreas.sandberg@arm.com        return retOK(ret ? 1 : 0);
41912531Sandreas.sandberg@arm.com    }
42012531Sandreas.sandberg@arm.com}
42112531Sandreas.sandberg@arm.com
42212531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
42312531Sandreas.sandberg@arm.comArmSemihosting::callSeek(ThreadContext *tc, bool aarch64,
42412531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
42512531Sandreas.sandberg@arm.com{
42612531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
42712531Sandreas.sandberg@arm.com        return retError(EBADF);
42812531Sandreas.sandberg@arm.com
42912531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->seek(argv[2]);
43012531Sandreas.sandberg@arm.com    if (ret < 0) {
43112531Sandreas.sandberg@arm.com        return retError(-ret);
43212531Sandreas.sandberg@arm.com    } else {
43312531Sandreas.sandberg@arm.com        return retOK(0);
43412531Sandreas.sandberg@arm.com    }
43512531Sandreas.sandberg@arm.com}
43612531Sandreas.sandberg@arm.com
43712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
43812531Sandreas.sandberg@arm.comArmSemihosting::callFLen(ThreadContext *tc, bool aarch64,
43912531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
44012531Sandreas.sandberg@arm.com{
44112531Sandreas.sandberg@arm.com    if (argv[1] > files.size() || !files[argv[1]])
44212531Sandreas.sandberg@arm.com        return retError(EBADF);
44312531Sandreas.sandberg@arm.com
44412531Sandreas.sandberg@arm.com    int64_t ret = files[argv[1]]->isTTY();
44512531Sandreas.sandberg@arm.com    if (ret < 0) {
44612531Sandreas.sandberg@arm.com        return retError(-ret);
44712531Sandreas.sandberg@arm.com    } else {
44812531Sandreas.sandberg@arm.com        return retOK(0);
44912531Sandreas.sandberg@arm.com    }
45012531Sandreas.sandberg@arm.com}
45112531Sandreas.sandberg@arm.com
45212531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
45312531Sandreas.sandberg@arm.comArmSemihosting::callTmpNam(ThreadContext *tc, bool aarch64,
45412531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
45512531Sandreas.sandberg@arm.com{
45612531Sandreas.sandberg@arm.com    const Addr guest_buf = argv[1];
45712531Sandreas.sandberg@arm.com    //const uint64_t id = argv[2];
45812531Sandreas.sandberg@arm.com    const uint64_t max_len = argv[3];
45912531Sandreas.sandberg@arm.com
46012531Sandreas.sandberg@arm.com    std::vector<char> buf(L_tmpnam);
46112531Sandreas.sandberg@arm.com    char *path = tmpnam(buf.data());
46212531Sandreas.sandberg@arm.com    if (!path)
46312531Sandreas.sandberg@arm.com        return retError(EINVAL);
46412531Sandreas.sandberg@arm.com
46512531Sandreas.sandberg@arm.com    const size_t path_len = strlen(path);
46612531Sandreas.sandberg@arm.com    if (path_len >= max_len)
46712531Sandreas.sandberg@arm.com        return retError(ENOSPC);
46812531Sandreas.sandberg@arm.com
46912533Sandreas.sandberg@arm.com    physProxy(tc).writeBlob(
47012531Sandreas.sandberg@arm.com        guest_buf, (const uint8_t *)path, path_len + 1);
47112531Sandreas.sandberg@arm.com    return retOK(0);
47212531Sandreas.sandberg@arm.com}
47312531Sandreas.sandberg@arm.com
47412531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
47512531Sandreas.sandberg@arm.comArmSemihosting::callRemove(ThreadContext *tc, bool aarch64,
47612531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
47712531Sandreas.sandberg@arm.com{
47812531Sandreas.sandberg@arm.com    std::string fname = readString(tc, argv[1], argv[2]);
47912531Sandreas.sandberg@arm.com
48012531Sandreas.sandberg@arm.com    if (remove(fname.c_str()) != 0) {
48112531Sandreas.sandberg@arm.com        return retError(errno);
48212531Sandreas.sandberg@arm.com    } else {
48312531Sandreas.sandberg@arm.com        return retOK(0);
48412531Sandreas.sandberg@arm.com    }
48512531Sandreas.sandberg@arm.com}
48612531Sandreas.sandberg@arm.com
48712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
48812531Sandreas.sandberg@arm.comArmSemihosting::callRename(ThreadContext *tc, bool aarch64,
48912531Sandreas.sandberg@arm.com                           std::vector<uint64_t> &argv)
49012531Sandreas.sandberg@arm.com{
49112531Sandreas.sandberg@arm.com    std::string from = readString(tc, argv[1], argv[2]);
49212531Sandreas.sandberg@arm.com    std::string to = readString(tc, argv[3], argv[4]);
49312531Sandreas.sandberg@arm.com
49412531Sandreas.sandberg@arm.com    if (rename(from.c_str(), to.c_str()) != 0) {
49512531Sandreas.sandberg@arm.com        return retError(errno);
49612531Sandreas.sandberg@arm.com    } else {
49712531Sandreas.sandberg@arm.com        return retOK(0);
49812531Sandreas.sandberg@arm.com    }
49912531Sandreas.sandberg@arm.com}
50012531Sandreas.sandberg@arm.com
50112531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
50212531Sandreas.sandberg@arm.comArmSemihosting::callClock(ThreadContext *tc, bool aarch64,
50312531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
50412531Sandreas.sandberg@arm.com{
50512531Sandreas.sandberg@arm.com    return retOK(curTick() / (SimClock::Int::s / 100));
50612531Sandreas.sandberg@arm.com}
50712531Sandreas.sandberg@arm.com
50812531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
50912531Sandreas.sandberg@arm.comArmSemihosting::callTime(ThreadContext *tc, bool aarch64,
51012531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
51112531Sandreas.sandberg@arm.com{
51212531Sandreas.sandberg@arm.com    return retOK(timeBase + round(curTick() / SimClock::Float::s));
51312531Sandreas.sandberg@arm.com}
51412531Sandreas.sandberg@arm.com
51512531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
51612531Sandreas.sandberg@arm.comArmSemihosting::callSystem(ThreadContext *tc, bool aarch64,
51712531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
51812531Sandreas.sandberg@arm.com{
51912531Sandreas.sandberg@arm.com    const std::string cmd = readString(tc, argv[1], argv[2]);
52012531Sandreas.sandberg@arm.com    warn("Semihosting: SYS_SYSTEM not implemented. Guest tried to run: %s\n",
52112531Sandreas.sandberg@arm.com         cmd);
52212531Sandreas.sandberg@arm.com    return retError(EINVAL);
52312531Sandreas.sandberg@arm.com
52412531Sandreas.sandberg@arm.com}
52512531Sandreas.sandberg@arm.com
52612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
52712531Sandreas.sandberg@arm.comArmSemihosting::callErrno(ThreadContext *tc, bool aarch64,
52812531Sandreas.sandberg@arm.com                          std::vector<uint64_t> &argv)
52912531Sandreas.sandberg@arm.com{
53012531Sandreas.sandberg@arm.com    // Preserve errno by returning it in errno as well.
53112531Sandreas.sandberg@arm.com    return RetErrno(semiErrno, semiErrno);
53212531Sandreas.sandberg@arm.com}
53312531Sandreas.sandberg@arm.com
53412531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
53512531Sandreas.sandberg@arm.comArmSemihosting::callGetCmdLine(ThreadContext *tc, bool aarch64,
53612531Sandreas.sandberg@arm.com                               std::vector<uint64_t> &argv)
53712531Sandreas.sandberg@arm.com{
53812531Sandreas.sandberg@arm.com    if (cmdLine.size() + 1 < argv[2]) {
53912533Sandreas.sandberg@arm.com        PortProxy &proxy = physProxy(tc);
54012531Sandreas.sandberg@arm.com        ByteOrder endian = ArmISA::byteOrder(tc);
54112531Sandreas.sandberg@arm.com        proxy.writeBlob(
54212531Sandreas.sandberg@arm.com            (Addr)argv[1],
54312531Sandreas.sandberg@arm.com            (const uint8_t *)cmdLine.c_str(), cmdLine.size() + 1);
54412531Sandreas.sandberg@arm.com
54512531Sandreas.sandberg@arm.com        if (aarch64)
54612531Sandreas.sandberg@arm.com            proxy.writeHtoG<uint64_t>(argv[0] + 1 * 8, cmdLine.size(), endian);
54712531Sandreas.sandberg@arm.com        else
54812531Sandreas.sandberg@arm.com            proxy.writeHtoG<uint32_t>(argv[0] + 1 * 4, cmdLine.size(), endian);
54912531Sandreas.sandberg@arm.com        return retOK(0);
55012531Sandreas.sandberg@arm.com    } else {
55112531Sandreas.sandberg@arm.com        return retError(0);
55212531Sandreas.sandberg@arm.com    }
55312531Sandreas.sandberg@arm.com}
55412531Sandreas.sandberg@arm.com
55512531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
55612531Sandreas.sandberg@arm.comArmSemihosting::callHeapInfo(ThreadContext *tc, bool aarch64,
55712531Sandreas.sandberg@arm.com                             std::vector<uint64_t> &argv)
55812531Sandreas.sandberg@arm.com{
55912531Sandreas.sandberg@arm.com    const PhysicalMemory &phys = tc->getSystemPtr()->getPhysMem();
56012531Sandreas.sandberg@arm.com    const AddrRangeList memories = phys.getConfAddrRanges();
56112531Sandreas.sandberg@arm.com    fatal_if(memories.size() < 1, "No memories reported from System");
56212531Sandreas.sandberg@arm.com    warn_if(memories.size() > 1, "Multiple physical memory ranges available. "
56312531Sandreas.sandberg@arm.com            "Using first range heap/stack.");
56412531Sandreas.sandberg@arm.com    const AddrRange memory = *memories.begin();
56512531Sandreas.sandberg@arm.com    const Addr mem_start = memory.start() + memReserve;
56612531Sandreas.sandberg@arm.com    Addr mem_end = memory.end();
56712531Sandreas.sandberg@arm.com
56812531Sandreas.sandberg@arm.com    // Make sure that 32-bit guests can access their memory.
56912531Sandreas.sandberg@arm.com    if (!aarch64) {
57012531Sandreas.sandberg@arm.com        const Addr phys_max = (1ULL << 32) - 1;
57112531Sandreas.sandberg@arm.com        panic_if(mem_start > phys_max,
57212531Sandreas.sandberg@arm.com                 "Physical memory out of range for a 32-bit guest.");
57312531Sandreas.sandberg@arm.com        if (mem_end > phys_max) {
57412531Sandreas.sandberg@arm.com            warn("Some physical memory out of range for a 32-bit guest.");
57512531Sandreas.sandberg@arm.com            mem_end = phys_max;
57612531Sandreas.sandberg@arm.com        }
57712531Sandreas.sandberg@arm.com    }
57812531Sandreas.sandberg@arm.com
57912531Sandreas.sandberg@arm.com    fatal_if(mem_start + stackSize >= mem_end,
58012531Sandreas.sandberg@arm.com             "Physical memory too small to fit desired stack and a heap.");
58112531Sandreas.sandberg@arm.com
58212531Sandreas.sandberg@arm.com    const Addr heap_base = mem_start;
58312531Sandreas.sandberg@arm.com    const Addr heap_limit = mem_end - stackSize + 1;
58412531Sandreas.sandberg@arm.com    const Addr stack_base = (mem_end + 1) & ~0x7ULL; // 8 byte stack alignment
58512531Sandreas.sandberg@arm.com    const Addr stack_limit = heap_limit;
58612531Sandreas.sandberg@arm.com
58712531Sandreas.sandberg@arm.com
58812531Sandreas.sandberg@arm.com    inform("Reporting heap/stack info to guest:\n"
58912531Sandreas.sandberg@arm.com           "\tHeap base: 0x%x\n"
59012531Sandreas.sandberg@arm.com           "\tHeap limit: 0x%x\n"
59112531Sandreas.sandberg@arm.com           "\tStack base: 0x%x\n"
59212531Sandreas.sandberg@arm.com           "\tStack limit: 0x%x\n",
59312531Sandreas.sandberg@arm.com           heap_base, heap_limit, stack_base, stack_limit);
59412531Sandreas.sandberg@arm.com
59512531Sandreas.sandberg@arm.com    Addr base = argv[1];
59612533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
59712531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
59812531Sandreas.sandberg@arm.com    if (aarch64) {
59912531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint64_t>(base + 0 * 8, heap_base, endian);
60012531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint64_t>(base + 1 * 8, heap_limit, endian);
60112531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint64_t>(base + 2 * 8, stack_base, endian);
60212531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint64_t>(base + 3 * 8, stack_limit, endian);
60312531Sandreas.sandberg@arm.com    } else {
60412531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint32_t>(base + 0 * 4, heap_base, endian);
60512531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint32_t>(base + 1 * 4, heap_limit, endian);
60612531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint32_t>(base + 2 * 4, stack_base, endian);
60712531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint32_t>(base + 3 * 4, stack_limit, endian);
60812531Sandreas.sandberg@arm.com    }
60912531Sandreas.sandberg@arm.com
61012531Sandreas.sandberg@arm.com    return retOK(0);
61112531Sandreas.sandberg@arm.com}
61212531Sandreas.sandberg@arm.com
61312531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
61412531Sandreas.sandberg@arm.comArmSemihosting::callExit(ThreadContext *tc, bool aarch64,
61512531Sandreas.sandberg@arm.com                         std::vector<uint64_t> &argv)
61612531Sandreas.sandberg@arm.com{
61712531Sandreas.sandberg@arm.com    if (aarch64) {
61812531Sandreas.sandberg@arm.com        semiExit(argv[1], argv[2]);
61912531Sandreas.sandberg@arm.com    } else {
62012531Sandreas.sandberg@arm.com        semiExit(argv[0], 0);
62112531Sandreas.sandberg@arm.com    }
62212531Sandreas.sandberg@arm.com
62312531Sandreas.sandberg@arm.com    return retOK(0);
62412531Sandreas.sandberg@arm.com}
62512531Sandreas.sandberg@arm.com
62612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
62712531Sandreas.sandberg@arm.comArmSemihosting::callExitExtended(ThreadContext *tc, bool aarch64,
62812531Sandreas.sandberg@arm.com                                 std::vector<uint64_t> &argv)
62912531Sandreas.sandberg@arm.com{
63012531Sandreas.sandberg@arm.com    semiExit(argv[1], argv[2]);
63112531Sandreas.sandberg@arm.com
63212531Sandreas.sandberg@arm.com    return retOK(0);
63312531Sandreas.sandberg@arm.com}
63412531Sandreas.sandberg@arm.com
63512531Sandreas.sandberg@arm.comvoid
63612531Sandreas.sandberg@arm.comArmSemihosting::semiExit(uint64_t code, uint64_t subcode)
63712531Sandreas.sandberg@arm.com{
63812531Sandreas.sandberg@arm.com    auto it = exitCodes.find(code);
63912531Sandreas.sandberg@arm.com    if (it != exitCodes.end()) {
64012531Sandreas.sandberg@arm.com        exitSimLoop(it->second, subcode);
64112531Sandreas.sandberg@arm.com    } else {
64212531Sandreas.sandberg@arm.com        exitSimLoop(csprintf("semi:0x%x", code), subcode);
64312531Sandreas.sandberg@arm.com    }
64412531Sandreas.sandberg@arm.com}
64512531Sandreas.sandberg@arm.com
64612531Sandreas.sandberg@arm.com
64712531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
64812531Sandreas.sandberg@arm.comArmSemihosting::callElapsed(ThreadContext *tc, bool aarch64,
64912531Sandreas.sandberg@arm.com                            std::vector<uint64_t> &argv)
65012531Sandreas.sandberg@arm.com{
65112533Sandreas.sandberg@arm.com    PortProxy &proxy = physProxy(tc);
65212531Sandreas.sandberg@arm.com    ByteOrder endian = ArmISA::byteOrder(tc);
65312531Sandreas.sandberg@arm.com    const uint64_t tick = semiTick(curTick());
65412531Sandreas.sandberg@arm.com
65512531Sandreas.sandberg@arm.com    if (aarch64) {
65612531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint64_t>(argv[0], tick, endian);
65712531Sandreas.sandberg@arm.com    } else {
65812531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint32_t>(argv[0] + 0 * 4, tick, endian);
65912531Sandreas.sandberg@arm.com        proxy.writeHtoG<uint32_t>(argv[0] + 1 * 4, tick >> 32, endian);
66012531Sandreas.sandberg@arm.com    }
66112531Sandreas.sandberg@arm.com
66212531Sandreas.sandberg@arm.com    return retOK(0);
66312531Sandreas.sandberg@arm.com}
66412531Sandreas.sandberg@arm.com
66512531Sandreas.sandberg@arm.com
66612531Sandreas.sandberg@arm.comArmSemihosting::RetErrno
66712531Sandreas.sandberg@arm.comArmSemihosting::callTickFreq(ThreadContext *tc, bool aarch64,
66812531Sandreas.sandberg@arm.com                             std::vector<uint64_t> &argv)
66912531Sandreas.sandberg@arm.com{
67012531Sandreas.sandberg@arm.com    return retOK(semiTick(SimClock::Frequency));
67112531Sandreas.sandberg@arm.com}
67212531Sandreas.sandberg@arm.com
67312531Sandreas.sandberg@arm.comconst ArmSemihosting::SemiCall *
67412531Sandreas.sandberg@arm.comArmSemihosting::getCall(uint32_t op, bool aarch64)
67512531Sandreas.sandberg@arm.com{
67612531Sandreas.sandberg@arm.com    auto it = calls.find(op);
67712531Sandreas.sandberg@arm.com    if (it == calls.end())
67812531Sandreas.sandberg@arm.com        return nullptr;
67912531Sandreas.sandberg@arm.com    else {
68012531Sandreas.sandberg@arm.com        return &it->second;
68112531Sandreas.sandberg@arm.com    }
68212531Sandreas.sandberg@arm.com}
68312531Sandreas.sandberg@arm.com
68412531Sandreas.sandberg@arm.comstd::unique_ptr<ArmSemihosting::FileBase>
68512531Sandreas.sandberg@arm.comArmSemihosting::FileBase::create(
68612531Sandreas.sandberg@arm.com    ArmSemihosting &parent, const std::string &fname, const char *mode)
68712531Sandreas.sandberg@arm.com{
68812531Sandreas.sandberg@arm.com    std::unique_ptr<FileBase> file;
68912531Sandreas.sandberg@arm.com    if (fname == ":semihosting-features") {
69012531Sandreas.sandberg@arm.com        file.reset(new FileFeatures(parent, fname.c_str(), mode));
69112531Sandreas.sandberg@arm.com    } else {
69212531Sandreas.sandberg@arm.com        file.reset(new File(parent, fname.c_str(), mode));
69312531Sandreas.sandberg@arm.com    }
69412531Sandreas.sandberg@arm.com
69512531Sandreas.sandberg@arm.com    return file;
69612531Sandreas.sandberg@arm.com}
69712531Sandreas.sandberg@arm.com
69812531Sandreas.sandberg@arm.comstd::unique_ptr<ArmSemihosting::FileBase>
69912531Sandreas.sandberg@arm.comArmSemihosting::FileBase::create(ArmSemihosting &parent,
70012531Sandreas.sandberg@arm.com                                 CheckpointIn &cp, const std::string &sec)
70112531Sandreas.sandberg@arm.com{
70212531Sandreas.sandberg@arm.com    std::unique_ptr<FileBase> file;
70312531Sandreas.sandberg@arm.com    ScopedCheckpointSection _sec(cp, sec);
70412531Sandreas.sandberg@arm.com
70512531Sandreas.sandberg@arm.com    // Was the file open when the checkpoint was created?
70612531Sandreas.sandberg@arm.com    if (!cp.sectionExists(Serializable::currentSection()))
70712531Sandreas.sandberg@arm.com        return file;
70812531Sandreas.sandberg@arm.com
70912531Sandreas.sandberg@arm.com    std::string fname, mode;
71012531Sandreas.sandberg@arm.com    paramIn(cp, "name", fname);
71112531Sandreas.sandberg@arm.com    paramIn(cp, "mode", mode);
71212531Sandreas.sandberg@arm.com    file = create(parent, fname, mode.c_str());
71312531Sandreas.sandberg@arm.com    assert(file);
71412531Sandreas.sandberg@arm.com    file->unserialize(cp);
71512531Sandreas.sandberg@arm.com
71612531Sandreas.sandberg@arm.com    return file;
71712531Sandreas.sandberg@arm.com}
71812531Sandreas.sandberg@arm.com
71912531Sandreas.sandberg@arm.comvoid
72012531Sandreas.sandberg@arm.comArmSemihosting::FileBase::serialize(CheckpointOut &cp) const
72112531Sandreas.sandberg@arm.com{
72212531Sandreas.sandberg@arm.com    paramOut(cp, "name", _name);
72312531Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(mode);
72412531Sandreas.sandberg@arm.com}
72512531Sandreas.sandberg@arm.com
72612531Sandreas.sandberg@arm.comvoid
72712531Sandreas.sandberg@arm.comArmSemihosting::FileBase::unserialize(CheckpointIn &cp)
72812531Sandreas.sandberg@arm.com{
72912531Sandreas.sandberg@arm.com    /* Unserialization of name and mode happens in
73012531Sandreas.sandberg@arm.com     * ArmSemihosting::FileBase::create() */
73112531Sandreas.sandberg@arm.com}
73212531Sandreas.sandberg@arm.com
73312531Sandreas.sandberg@arm.comint64_t
73412531Sandreas.sandberg@arm.comArmSemihosting::FileBase::read(uint8_t *buffer, uint64_t size)
73512531Sandreas.sandberg@arm.com{
73612531Sandreas.sandberg@arm.com    return -EINVAL;
73712531Sandreas.sandberg@arm.com}
73812531Sandreas.sandberg@arm.com
73912531Sandreas.sandberg@arm.comint64_t
74012531Sandreas.sandberg@arm.comArmSemihosting::FileBase::write(const uint8_t *buffer, uint64_t size)
74112531Sandreas.sandberg@arm.com{
74212531Sandreas.sandberg@arm.com    return -EINVAL;
74312531Sandreas.sandberg@arm.com}
74412531Sandreas.sandberg@arm.com
74512531Sandreas.sandberg@arm.comint64_t
74612531Sandreas.sandberg@arm.comArmSemihosting::FileBase::seek(uint64_t pos)
74712531Sandreas.sandberg@arm.com{
74812531Sandreas.sandberg@arm.com    return -EINVAL;
74912531Sandreas.sandberg@arm.com}
75012531Sandreas.sandberg@arm.com
75112531Sandreas.sandberg@arm.comint64_t
75212531Sandreas.sandberg@arm.comArmSemihosting::FileBase::flen()
75312531Sandreas.sandberg@arm.com{
75412531Sandreas.sandberg@arm.com    return -EINVAL;
75512531Sandreas.sandberg@arm.com}
75612531Sandreas.sandberg@arm.com
75712531Sandreas.sandberg@arm.com
75812531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::FileFeatures(
75912531Sandreas.sandberg@arm.com    ArmSemihosting &_parent, const char *_name, const char *_mode)
76012531Sandreas.sandberg@arm.com    : FileBase(_parent, _name, _mode)
76112531Sandreas.sandberg@arm.com{
76212531Sandreas.sandberg@arm.com}
76312531Sandreas.sandberg@arm.com
76412531Sandreas.sandberg@arm.comint64_t
76512531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::read(uint8_t *buffer, uint64_t size)
76612531Sandreas.sandberg@arm.com{
76712531Sandreas.sandberg@arm.com    int64_t len = 0;
76812531Sandreas.sandberg@arm.com
76912531Sandreas.sandberg@arm.com    for (; pos < size && pos < ArmSemihosting::features.size(); pos++)
77012531Sandreas.sandberg@arm.com        buffer[len++] = ArmSemihosting::features[pos];
77112531Sandreas.sandberg@arm.com
77212531Sandreas.sandberg@arm.com    return len;
77312531Sandreas.sandberg@arm.com}
77412531Sandreas.sandberg@arm.com
77512531Sandreas.sandberg@arm.comint64_t
77612531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::seek(uint64_t _pos)
77712531Sandreas.sandberg@arm.com{
77812531Sandreas.sandberg@arm.com    if (_pos < ArmSemihosting::features.size()) {
77912531Sandreas.sandberg@arm.com        pos = _pos;
78012531Sandreas.sandberg@arm.com        return 0;
78112531Sandreas.sandberg@arm.com    } else {
78212531Sandreas.sandberg@arm.com        return -ENXIO;
78312531Sandreas.sandberg@arm.com    }
78412531Sandreas.sandberg@arm.com}
78512531Sandreas.sandberg@arm.com
78612531Sandreas.sandberg@arm.comvoid
78712531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::serialize(CheckpointOut &cp) const
78812531Sandreas.sandberg@arm.com{
78912531Sandreas.sandberg@arm.com    FileBase::serialize(cp);
79012531Sandreas.sandberg@arm.com    SERIALIZE_SCALAR(pos);
79112531Sandreas.sandberg@arm.com}
79212531Sandreas.sandberg@arm.com
79312531Sandreas.sandberg@arm.comvoid
79412531Sandreas.sandberg@arm.comArmSemihosting::FileFeatures::unserialize(CheckpointIn &cp)
79512531Sandreas.sandberg@arm.com{
79612531Sandreas.sandberg@arm.com    FileBase::unserialize(cp);
79712531Sandreas.sandberg@arm.com    UNSERIALIZE_SCALAR(pos);
79812531Sandreas.sandberg@arm.com}
79912531Sandreas.sandberg@arm.com
80012531Sandreas.sandberg@arm.com
80112531Sandreas.sandberg@arm.com
80212531Sandreas.sandberg@arm.comArmSemihosting::File::File(ArmSemihosting &_parent,
80312531Sandreas.sandberg@arm.com                           const char *_name, const char *_perms)
80412531Sandreas.sandberg@arm.com    : FileBase(_parent, _name, _perms),
80512531Sandreas.sandberg@arm.com      file(nullptr)
80612531Sandreas.sandberg@arm.com{
80712531Sandreas.sandberg@arm.com}
80812531Sandreas.sandberg@arm.com
80912531Sandreas.sandberg@arm.comArmSemihosting::File::~File()
81012531Sandreas.sandberg@arm.com{
81112531Sandreas.sandberg@arm.com    if (file)
81212531Sandreas.sandberg@arm.com        close();
81312531Sandreas.sandberg@arm.com}
81412531Sandreas.sandberg@arm.com
81512531Sandreas.sandberg@arm.comint64_t
81612531Sandreas.sandberg@arm.comArmSemihosting::File::openImpl(bool in_cpt)
81712531Sandreas.sandberg@arm.com{
81812531Sandreas.sandberg@arm.com    panic_if(file, "Trying to open an already open file.\n");
81912531Sandreas.sandberg@arm.com
82012531Sandreas.sandberg@arm.com    if (_name == ":tt") {
82112531Sandreas.sandberg@arm.com        if (mode[0] == 'r') {
82212531Sandreas.sandberg@arm.com            file = stdin;
82312531Sandreas.sandberg@arm.com        } else if (mode[0] == 'w') {
82412531Sandreas.sandberg@arm.com            file = stdout;
82512531Sandreas.sandberg@arm.com        } else if (mode[0] == 'a') {
82612531Sandreas.sandberg@arm.com            file = stderr;
82712531Sandreas.sandberg@arm.com        } else {
82812531Sandreas.sandberg@arm.com            warn("Unknown file mode for the ':tt' special file");
82912531Sandreas.sandberg@arm.com            return -EINVAL;
83012531Sandreas.sandberg@arm.com        }
83112531Sandreas.sandberg@arm.com    } else {
83212531Sandreas.sandberg@arm.com        std::string real_mode(this->mode);
83312531Sandreas.sandberg@arm.com        // Avoid truncating the file if we are restoring from a
83412531Sandreas.sandberg@arm.com        // checkpoint.
83512531Sandreas.sandberg@arm.com        if (in_cpt && real_mode[0] == 'w')
83612531Sandreas.sandberg@arm.com            real_mode[0] = 'a';
83712531Sandreas.sandberg@arm.com
83812531Sandreas.sandberg@arm.com        file = fopen(_name.c_str(), real_mode.c_str());
83912531Sandreas.sandberg@arm.com    }
84012531Sandreas.sandberg@arm.com
84112531Sandreas.sandberg@arm.com    return file ? 0 : -errno;
84212531Sandreas.sandberg@arm.com}
84312531Sandreas.sandberg@arm.com
84412531Sandreas.sandberg@arm.comint64_t
84512531Sandreas.sandberg@arm.comArmSemihosting::File::close()
84612531Sandreas.sandberg@arm.com{
84712531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to close an already closed file.\n");
84812531Sandreas.sandberg@arm.com
84912531Sandreas.sandberg@arm.com    if (needClose()) {
85012531Sandreas.sandberg@arm.com        fclose(file);
85112531Sandreas.sandberg@arm.com    }
85212531Sandreas.sandberg@arm.com    file = nullptr;
85312531Sandreas.sandberg@arm.com
85412531Sandreas.sandberg@arm.com    return 0;
85512531Sandreas.sandberg@arm.com}
85612531Sandreas.sandberg@arm.com
85712531Sandreas.sandberg@arm.combool
85812531Sandreas.sandberg@arm.comArmSemihosting::File::isTTY() const
85912531Sandreas.sandberg@arm.com{
86012531Sandreas.sandberg@arm.com    return file == stdout || file == stderr || file == stdin;
86112531Sandreas.sandberg@arm.com}
86212531Sandreas.sandberg@arm.com
86312531Sandreas.sandberg@arm.comint64_t
86412531Sandreas.sandberg@arm.comArmSemihosting::File::read(uint8_t *buffer, uint64_t size)
86512531Sandreas.sandberg@arm.com{
86612531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to read from a closed file");
86712531Sandreas.sandberg@arm.com
86812531Sandreas.sandberg@arm.com    size_t ret = fread(buffer, 1, size, file);
86912531Sandreas.sandberg@arm.com    if (ret == 0) {
87012531Sandreas.sandberg@arm.com        // Error or EOF. Assume errors are due to invalid file
87112531Sandreas.sandberg@arm.com        // operations (e.g., reading a write-only stream).
87212531Sandreas.sandberg@arm.com        return ferror(file) ? -EINVAL : 0;
87312531Sandreas.sandberg@arm.com    } else {
87412531Sandreas.sandberg@arm.com        return ret;
87512531Sandreas.sandberg@arm.com    }
87612531Sandreas.sandberg@arm.com}
87712531Sandreas.sandberg@arm.com
87812531Sandreas.sandberg@arm.comint64_t
87912531Sandreas.sandberg@arm.comArmSemihosting::File::write(const uint8_t *buffer, uint64_t size)
88012531Sandreas.sandberg@arm.com{
88112531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to write to a closed file");
88212531Sandreas.sandberg@arm.com
88312531Sandreas.sandberg@arm.com
88412531Sandreas.sandberg@arm.com    size_t ret = fwrite(buffer, 1, size, file);
88512531Sandreas.sandberg@arm.com    if (ret == 0) {
88612531Sandreas.sandberg@arm.com        // Assume errors are due to invalid file operations (e.g.,
88712531Sandreas.sandberg@arm.com        // writing a read-only stream).
88812531Sandreas.sandberg@arm.com        return -EINVAL;
88912531Sandreas.sandberg@arm.com    } else {
89012531Sandreas.sandberg@arm.com        return ret;
89112531Sandreas.sandberg@arm.com    }
89212531Sandreas.sandberg@arm.com}
89312531Sandreas.sandberg@arm.com
89412531Sandreas.sandberg@arm.comint64_t
89512531Sandreas.sandberg@arm.comArmSemihosting::File::seek(uint64_t _pos)
89612531Sandreas.sandberg@arm.com{
89712531Sandreas.sandberg@arm.com    panic_if(!file, "Trying to seek in a closed file");
89812531Sandreas.sandberg@arm.com
89912531Sandreas.sandberg@arm.com    errno = 0;
90012531Sandreas.sandberg@arm.com    if (fseek(file, _pos, SEEK_SET) == 0)
90112531Sandreas.sandberg@arm.com        return 0;
90212531Sandreas.sandberg@arm.com    else
90312531Sandreas.sandberg@arm.com        return -errno;
90412531Sandreas.sandberg@arm.com}
90512531Sandreas.sandberg@arm.com
90612531Sandreas.sandberg@arm.comint64_t
90712531Sandreas.sandberg@arm.comArmSemihosting::File::flen()
90812531Sandreas.sandberg@arm.com{
90912531Sandreas.sandberg@arm.com    errno = 0;
91012531Sandreas.sandberg@arm.com    long pos = ftell(file);
91112531Sandreas.sandberg@arm.com    if (pos < 0)
91212531Sandreas.sandberg@arm.com        return -errno;
91312531Sandreas.sandberg@arm.com
91412531Sandreas.sandberg@arm.com    if (fseek(file, 0, SEEK_END) != 0)
91512531Sandreas.sandberg@arm.com        return -errno;
91612531Sandreas.sandberg@arm.com
91712531Sandreas.sandberg@arm.com    long len = ftell(file);
91812531Sandreas.sandberg@arm.com    if (len < 0)
91912531Sandreas.sandberg@arm.com        return -errno;
92012531Sandreas.sandberg@arm.com
92112531Sandreas.sandberg@arm.com    if (fseek(file, pos, SEEK_SET) != 0)
92212531Sandreas.sandberg@arm.com        return -errno;
92312531Sandreas.sandberg@arm.com
92412531Sandreas.sandberg@arm.com    return len;
92512531Sandreas.sandberg@arm.com}
92612531Sandreas.sandberg@arm.com
92712531Sandreas.sandberg@arm.com
92812531Sandreas.sandberg@arm.comvoid
92912531Sandreas.sandberg@arm.comArmSemihosting::File::serialize(CheckpointOut &cp) const
93012531Sandreas.sandberg@arm.com{
93112531Sandreas.sandberg@arm.com    FileBase::serialize(cp);
93212531Sandreas.sandberg@arm.com
93312531Sandreas.sandberg@arm.com    if (!isTTY()) {
93412531Sandreas.sandberg@arm.com        long pos = file ? ftell(file) : 0;
93512531Sandreas.sandberg@arm.com        panic_if(pos < 0, "Failed to get file position.");
93612531Sandreas.sandberg@arm.com        SERIALIZE_SCALAR(pos);
93712531Sandreas.sandberg@arm.com    }
93812531Sandreas.sandberg@arm.com}
93912531Sandreas.sandberg@arm.com
94012531Sandreas.sandberg@arm.comvoid
94112531Sandreas.sandberg@arm.comArmSemihosting::File::unserialize(CheckpointIn &cp)
94212531Sandreas.sandberg@arm.com{
94312531Sandreas.sandberg@arm.com    FileBase::unserialize(cp);
94412531Sandreas.sandberg@arm.com
94512531Sandreas.sandberg@arm.com    if (openImpl(true) < 0) {
94612531Sandreas.sandberg@arm.com        fatal("Failed to open file: %s", _name);
94712531Sandreas.sandberg@arm.com    }
94812531Sandreas.sandberg@arm.com
94912531Sandreas.sandberg@arm.com    if (!isTTY()) {
95012531Sandreas.sandberg@arm.com        long pos = 0;
95112531Sandreas.sandberg@arm.com        UNSERIALIZE_SCALAR(pos);
95212531Sandreas.sandberg@arm.com        if (fseek(file, pos, SEEK_SET) != 0) {
95312531Sandreas.sandberg@arm.com            fatal("Failed seek to current position (%i) in '%s'", pos, _name);
95412531Sandreas.sandberg@arm.com        }
95512531Sandreas.sandberg@arm.com    }
95612531Sandreas.sandberg@arm.com}
95712531Sandreas.sandberg@arm.com
95812531Sandreas.sandberg@arm.com
95912531Sandreas.sandberg@arm.comArmSemihosting *
96012531Sandreas.sandberg@arm.comArmSemihostingParams::create()
96112531Sandreas.sandberg@arm.com{
96212531Sandreas.sandberg@arm.com    return new ArmSemihosting(this);
96312531Sandreas.sandberg@arm.com}
964