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