18df9f0c3SPhilippe Mathieu-Daudé /*
28df9f0c3SPhilippe Mathieu-Daudé  *  Semihosting support for systems modeled on the Arm "Angel"
38df9f0c3SPhilippe Mathieu-Daudé  *  semihosting syscalls design. This includes Arm and RISC-V processors
48df9f0c3SPhilippe Mathieu-Daudé  *
58df9f0c3SPhilippe Mathieu-Daudé  *  Copyright (c) 2005, 2007 CodeSourcery.
68df9f0c3SPhilippe Mathieu-Daudé  *  Copyright (c) 2019 Linaro
78df9f0c3SPhilippe Mathieu-Daudé  *  Written by Paul Brook.
88df9f0c3SPhilippe Mathieu-Daudé  *
98df9f0c3SPhilippe Mathieu-Daudé  *  Copyright © 2020 by Keith Packard <keithp@keithp.com>
108df9f0c3SPhilippe Mathieu-Daudé  *  Adapted for systems other than ARM, including RISC-V, by Keith Packard
118df9f0c3SPhilippe Mathieu-Daudé  *
128df9f0c3SPhilippe Mathieu-Daudé  *  This program is free software; you can redistribute it and/or modify
138df9f0c3SPhilippe Mathieu-Daudé  *  it under the terms of the GNU General Public License as published by
148df9f0c3SPhilippe Mathieu-Daudé  *  the Free Software Foundation; either version 2 of the License, or
158df9f0c3SPhilippe Mathieu-Daudé  *  (at your option) any later version.
168df9f0c3SPhilippe Mathieu-Daudé  *
178df9f0c3SPhilippe Mathieu-Daudé  *  This program is distributed in the hope that it will be useful,
188df9f0c3SPhilippe Mathieu-Daudé  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
198df9f0c3SPhilippe Mathieu-Daudé  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
208df9f0c3SPhilippe Mathieu-Daudé  *  GNU General Public License for more details.
218df9f0c3SPhilippe Mathieu-Daudé  *
228df9f0c3SPhilippe Mathieu-Daudé  *  You should have received a copy of the GNU General Public License
238df9f0c3SPhilippe Mathieu-Daudé  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
248df9f0c3SPhilippe Mathieu-Daudé  *
258df9f0c3SPhilippe Mathieu-Daudé  *  ARM Semihosting is documented in:
268df9f0c3SPhilippe Mathieu-Daudé  *     Semihosting for AArch32 and AArch64 Release 2.0
27424d5ecfSAlex Bennée  *     https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst
288df9f0c3SPhilippe Mathieu-Daudé  *
298df9f0c3SPhilippe Mathieu-Daudé  *  RISC-V Semihosting is documented in:
308df9f0c3SPhilippe Mathieu-Daudé  *     RISC-V Semihosting
318df9f0c3SPhilippe Mathieu-Daudé  *     https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
328df9f0c3SPhilippe Mathieu-Daudé  */
338df9f0c3SPhilippe Mathieu-Daudé 
348df9f0c3SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
355b3f39cbSRichard Henderson #include "qemu/timer.h"
365b3f39cbSRichard Henderson #include "exec/gdbstub.h"
37c566080cSAlex Bennée #include "gdbstub/syscalls.h"
388df9f0c3SPhilippe Mathieu-Daudé #include "semihosting/semihost.h"
398df9f0c3SPhilippe Mathieu-Daudé #include "semihosting/console.h"
408df9f0c3SPhilippe Mathieu-Daudé #include "semihosting/common-semi.h"
411c6ff720SRichard Henderson #include "semihosting/guestfd.h"
425b3f39cbSRichard Henderson #include "semihosting/syscalls.h"
431c6ff720SRichard Henderson 
448df9f0c3SPhilippe Mathieu-Daudé #ifdef CONFIG_USER_ONLY
458df9f0c3SPhilippe Mathieu-Daudé #include "qemu.h"
468df9f0c3SPhilippe Mathieu-Daudé 
478df9f0c3SPhilippe Mathieu-Daudé #define COMMON_SEMI_HEAP_SIZE (128 * 1024 * 1024)
488df9f0c3SPhilippe Mathieu-Daudé #else
498df9f0c3SPhilippe Mathieu-Daudé #include "qemu/cutils.h"
505fc983afSAlex Bennée #include "hw/loader.h"
518df9f0c3SPhilippe Mathieu-Daudé #include "hw/boards.h"
528df9f0c3SPhilippe Mathieu-Daudé #endif
538df9f0c3SPhilippe Mathieu-Daudé 
548df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_OPEN        0x01
558df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_CLOSE       0x02
568df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_WRITEC      0x03
578df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_WRITE0      0x04
588df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_WRITE       0x05
598df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_READ        0x06
608df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_READC       0x07
618df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_ISERROR     0x08
628df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_ISTTY       0x09
638df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_SEEK        0x0a
648df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_FLEN        0x0c
658df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_TMPNAM      0x0d
668df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_REMOVE      0x0e
678df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_RENAME      0x0f
688df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_CLOCK       0x10
698df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_TIME        0x11
708df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_SYSTEM      0x12
718df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_ERRNO       0x13
728df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_GET_CMDLINE 0x15
738df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_HEAPINFO    0x16
748df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_EXIT        0x18
758df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_SYNCCACHE   0x19
768df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_EXIT_EXTENDED 0x20
778df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_ELAPSED     0x30
788df9f0c3SPhilippe Mathieu-Daudé #define TARGET_SYS_TICKFREQ    0x31
798df9f0c3SPhilippe Mathieu-Daudé 
808df9f0c3SPhilippe Mathieu-Daudé /* ADP_Stopped_ApplicationExit is used for exit(0),
818df9f0c3SPhilippe Mathieu-Daudé  * anything else is implemented as exit(1) */
828df9f0c3SPhilippe Mathieu-Daudé #define ADP_Stopped_ApplicationExit     (0x20026)
838df9f0c3SPhilippe Mathieu-Daudé 
848df9f0c3SPhilippe Mathieu-Daudé #ifndef O_BINARY
858df9f0c3SPhilippe Mathieu-Daudé #define O_BINARY 0
868df9f0c3SPhilippe Mathieu-Daudé #endif
878df9f0c3SPhilippe Mathieu-Daudé 
888df9f0c3SPhilippe Mathieu-Daudé static int gdb_open_modeflags[12] = {
898df9f0c3SPhilippe Mathieu-Daudé     GDB_O_RDONLY,
90a1a2a3e6SRichard Henderson     GDB_O_RDONLY,
918df9f0c3SPhilippe Mathieu-Daudé     GDB_O_RDWR,
92a1a2a3e6SRichard Henderson     GDB_O_RDWR,
938df9f0c3SPhilippe Mathieu-Daudé     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
94a1a2a3e6SRichard Henderson     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
958df9f0c3SPhilippe Mathieu-Daudé     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
96a1a2a3e6SRichard Henderson     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
978df9f0c3SPhilippe Mathieu-Daudé     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
98a1a2a3e6SRichard Henderson     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
998df9f0c3SPhilippe Mathieu-Daudé     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
100a1a2a3e6SRichard Henderson     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
1018df9f0c3SPhilippe Mathieu-Daudé };
1028df9f0c3SPhilippe Mathieu-Daudé 
1038df9f0c3SPhilippe Mathieu-Daudé #ifndef CONFIG_USER_ONLY
1045fc983afSAlex Bennée 
1055fc983afSAlex Bennée /**
1065fc983afSAlex Bennée  * common_semi_find_bases: find information about ram and heap base
1075fc983afSAlex Bennée  *
1085fc983afSAlex Bennée  * This function attempts to provide meaningful numbers for RAM and
1095fc983afSAlex Bennée  * HEAP base addresses. The rambase is simply the lowest addressable
1105fc983afSAlex Bennée  * RAM position. For the heapbase we ask the loader to scan the
1115fc983afSAlex Bennée  * address space and the largest available gap by querying the "ROM"
1125fc983afSAlex Bennée  * regions.
1135fc983afSAlex Bennée  *
1145fc983afSAlex Bennée  * Returns: a structure with the numbers we need.
1158df9f0c3SPhilippe Mathieu-Daudé  */
1165fc983afSAlex Bennée 
1175fc983afSAlex Bennée typedef struct LayoutInfo {
1185fc983afSAlex Bennée     target_ulong rambase;
1195fc983afSAlex Bennée     size_t ramsize;
1205fc983afSAlex Bennée     hwaddr heapbase;
1215fc983afSAlex Bennée     hwaddr heaplimit;
1225fc983afSAlex Bennée } LayoutInfo;
1235fc983afSAlex Bennée 
find_ram_cb(Int128 start,Int128 len,const MemoryRegion * mr,hwaddr offset_in_region,void * opaque)1245fc983afSAlex Bennée static bool find_ram_cb(Int128 start, Int128 len, const MemoryRegion *mr,
1255fc983afSAlex Bennée                         hwaddr offset_in_region, void *opaque)
1268df9f0c3SPhilippe Mathieu-Daudé {
1275fc983afSAlex Bennée     LayoutInfo *info = (LayoutInfo *) opaque;
1285fc983afSAlex Bennée     uint64_t size = int128_get64(len);
1295fc983afSAlex Bennée 
1305fc983afSAlex Bennée     if (!mr->ram || mr->readonly) {
1315fc983afSAlex Bennée         return false;
1325fc983afSAlex Bennée     }
1335fc983afSAlex Bennée 
1345fc983afSAlex Bennée     if (size > info->ramsize) {
1355fc983afSAlex Bennée         info->rambase = int128_get64(start);
1365fc983afSAlex Bennée         info->ramsize = size;
1375fc983afSAlex Bennée     }
1385fc983afSAlex Bennée 
1395fc983afSAlex Bennée     /* search exhaustively for largest RAM */
1405fc983afSAlex Bennée     return false;
1415fc983afSAlex Bennée }
1425fc983afSAlex Bennée 
common_semi_find_bases(CPUState * cs)1435fc983afSAlex Bennée static LayoutInfo common_semi_find_bases(CPUState *cs)
1445fc983afSAlex Bennée {
1455fc983afSAlex Bennée     FlatView *fv;
1465fc983afSAlex Bennée     LayoutInfo info = { 0, 0, 0, 0 };
1475fc983afSAlex Bennée 
1485fc983afSAlex Bennée     RCU_READ_LOCK_GUARD();
1495fc983afSAlex Bennée 
1505fc983afSAlex Bennée     fv = address_space_to_flatview(cs->as);
1515fc983afSAlex Bennée     flatview_for_each_range(fv, find_ram_cb, &info);
1528df9f0c3SPhilippe Mathieu-Daudé 
1538df9f0c3SPhilippe Mathieu-Daudé     /*
1545fc983afSAlex Bennée      * If we have found the RAM lets iterate through the ROM blobs to
1555fc983afSAlex Bennée      * work out the best place for the remainder of RAM and split it
1565fc983afSAlex Bennée      * equally between stack and heap.
1578df9f0c3SPhilippe Mathieu-Daudé      */
1585fc983afSAlex Bennée     if (info.rambase || info.ramsize > 0) {
1595fc983afSAlex Bennée         RomGap gap = rom_find_largest_gap_between(info.rambase, info.ramsize);
1605fc983afSAlex Bennée         info.heapbase = gap.base;
1615fc983afSAlex Bennée         info.heaplimit = gap.base + gap.size;
1628df9f0c3SPhilippe Mathieu-Daudé     }
1635fc983afSAlex Bennée 
1645fc983afSAlex Bennée     return info;
1658df9f0c3SPhilippe Mathieu-Daudé }
1665fc983afSAlex Bennée 
1678df9f0c3SPhilippe Mathieu-Daudé #endif
1688df9f0c3SPhilippe Mathieu-Daudé 
1691b3b7693SRichard Henderson #include "common-semi-target.h"
1708df9f0c3SPhilippe Mathieu-Daudé 
1718df9f0c3SPhilippe Mathieu-Daudé /*
1723753b00eSRichard Henderson  * Read the input value from the argument block; fail the semihosting
1733753b00eSRichard Henderson  * call if the memory read fails. Eventually we could use a generic
1743753b00eSRichard Henderson  * CPUState helper function here.
175fed49cdfSPeter Maydell  * Note that GET_ARG() handles memory access errors by jumping to
176fed49cdfSPeter Maydell  * do_fault, so must be used as the first thing done in handling a
177fed49cdfSPeter Maydell  * semihosting call, to avoid accidentally leaking allocated resources.
178fed49cdfSPeter Maydell  * SET_ARG(), since it unavoidably happens late, instead returns an
179fed49cdfSPeter Maydell  * error indication (0 on success, non-0 for error) which the caller
180fed49cdfSPeter Maydell  * should check.
1813753b00eSRichard Henderson  */
1823753b00eSRichard Henderson 
1833753b00eSRichard Henderson #define GET_ARG(n) do {                                 \
1843753b00eSRichard Henderson     if (is_64bit_semihosting(env)) {                    \
1853753b00eSRichard Henderson         if (get_user_u64(arg ## n, args + (n) * 8)) {   \
1863753b00eSRichard Henderson             goto do_fault;                              \
1873753b00eSRichard Henderson         }                                               \
1883753b00eSRichard Henderson     } else {                                            \
1893753b00eSRichard Henderson         if (get_user_u32(arg ## n, args + (n) * 4)) {   \
1903753b00eSRichard Henderson             goto do_fault;                              \
1913753b00eSRichard Henderson         }                                               \
1923753b00eSRichard Henderson     }                                                   \
1933753b00eSRichard Henderson } while (0)
1943753b00eSRichard Henderson 
1953753b00eSRichard Henderson #define SET_ARG(n, val)                                 \
1963753b00eSRichard Henderson     (is_64bit_semihosting(env) ?                        \
1973753b00eSRichard Henderson      put_user_u64(val, args + (n) * 8) :                \
1983753b00eSRichard Henderson      put_user_u32(val, args + (n) * 4))
1993753b00eSRichard Henderson 
2003753b00eSRichard Henderson 
2013753b00eSRichard Henderson /*
2028df9f0c3SPhilippe Mathieu-Daudé  * The semihosting API has no concept of its errno being thread-safe,
2038df9f0c3SPhilippe Mathieu-Daudé  * as the API design predates SMP CPUs and was intended as a simple
2048df9f0c3SPhilippe Mathieu-Daudé  * real-hardware set of debug functionality. For QEMU, we make the
205*f14eced5SPhilippe Mathieu-Daudé  * errno be per-thread in linux-user mode; in system-mode it is a simple
2068df9f0c3SPhilippe Mathieu-Daudé  * global, and we assume that the guest takes care of avoiding any races.
2078df9f0c3SPhilippe Mathieu-Daudé  */
2088df9f0c3SPhilippe Mathieu-Daudé #ifndef CONFIG_USER_ONLY
2098df9f0c3SPhilippe Mathieu-Daudé static target_ulong syscall_err;
2108df9f0c3SPhilippe Mathieu-Daudé 
211*f14eced5SPhilippe Mathieu-Daudé #include "semihosting/uaccess.h"
2128df9f0c3SPhilippe Mathieu-Daudé #endif
2138df9f0c3SPhilippe Mathieu-Daudé 
get_swi_errno(CPUState * cs)2148df9f0c3SPhilippe Mathieu-Daudé static inline uint32_t get_swi_errno(CPUState *cs)
2158df9f0c3SPhilippe Mathieu-Daudé {
2168df9f0c3SPhilippe Mathieu-Daudé #ifdef CONFIG_USER_ONLY
2178df9f0c3SPhilippe Mathieu-Daudé     TaskState *ts = cs->opaque;
2188df9f0c3SPhilippe Mathieu-Daudé 
2198df9f0c3SPhilippe Mathieu-Daudé     return ts->swi_errno;
2208df9f0c3SPhilippe Mathieu-Daudé #else
2218df9f0c3SPhilippe Mathieu-Daudé     return syscall_err;
2228df9f0c3SPhilippe Mathieu-Daudé #endif
2238df9f0c3SPhilippe Mathieu-Daudé }
2248df9f0c3SPhilippe Mathieu-Daudé 
common_semi_cb(CPUState * cs,uint64_t ret,int err)22564c8c6a9SRichard Henderson static void common_semi_cb(CPUState *cs, uint64_t ret, int err)
2268df9f0c3SPhilippe Mathieu-Daudé {
227709fe27bSRichard Henderson     if (err) {
2285aadd182SRichard Henderson #ifdef CONFIG_USER_ONLY
2295aadd182SRichard Henderson         TaskState *ts = cs->opaque;
2305aadd182SRichard Henderson         ts->swi_errno = err;
2315aadd182SRichard Henderson #else
2325aadd182SRichard Henderson         syscall_err = err;
2335aadd182SRichard Henderson #endif
2348df9f0c3SPhilippe Mathieu-Daudé     }
2355aadd182SRichard Henderson     common_semi_set_ret(cs, ret);
2368df9f0c3SPhilippe Mathieu-Daudé }
2378df9f0c3SPhilippe Mathieu-Daudé 
2383c820ddcSRichard Henderson /*
2395d77289dSRichard Henderson  * Use 0xdeadbeef as the return value when there isn't a defined
2405d77289dSRichard Henderson  * return value for the call.
2415d77289dSRichard Henderson  */
common_semi_dead_cb(CPUState * cs,uint64_t ret,int err)2425d77289dSRichard Henderson static void common_semi_dead_cb(CPUState *cs, uint64_t ret, int err)
2435d77289dSRichard Henderson {
2445d77289dSRichard Henderson     common_semi_set_ret(cs, 0xdeadbeef);
2455d77289dSRichard Henderson }
2465d77289dSRichard Henderson 
2475d77289dSRichard Henderson /*
248af0484b5SRichard Henderson  * SYS_READ and SYS_WRITE always return the number of bytes not read/written.
249af0484b5SRichard Henderson  * There is no error condition, other than returning the original length.
250af0484b5SRichard Henderson  */
common_semi_rw_cb(CPUState * cs,uint64_t ret,int err)25164c8c6a9SRichard Henderson static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err)
252af0484b5SRichard Henderson {
253af0484b5SRichard Henderson     /* Recover the original length from the third argument. */
254b77af26eSRichard Henderson     CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
255af0484b5SRichard Henderson     target_ulong args = common_semi_arg(cs, 1);
256af0484b5SRichard Henderson     target_ulong arg2;
257af0484b5SRichard Henderson     GET_ARG(2);
258af0484b5SRichard Henderson 
259af0484b5SRichard Henderson     if (err) {
260af0484b5SRichard Henderson  do_fault:
261af0484b5SRichard Henderson         ret = 0; /* error: no bytes transmitted */
262af0484b5SRichard Henderson     }
263af0484b5SRichard Henderson     common_semi_set_ret(cs, arg2 - ret);
264af0484b5SRichard Henderson }
265af0484b5SRichard Henderson 
266af0484b5SRichard Henderson /*
267a2212474SRichard Henderson  * Convert from Posix ret+errno to Arm SYS_ISTTY return values.
268a2212474SRichard Henderson  * With gdbstub, err is only ever set for protocol errors to EIO.
269a2212474SRichard Henderson  */
common_semi_istty_cb(CPUState * cs,uint64_t ret,int err)27064c8c6a9SRichard Henderson static void common_semi_istty_cb(CPUState *cs, uint64_t ret, int err)
271a2212474SRichard Henderson {
272a2212474SRichard Henderson     if (err) {
273a2212474SRichard Henderson         ret = (err == ENOTTY ? 0 : -1);
274a2212474SRichard Henderson     }
275a2212474SRichard Henderson     common_semi_cb(cs, ret, err);
276a2212474SRichard Henderson }
277a2212474SRichard Henderson 
278a2212474SRichard Henderson /*
2799a894704SRichard Henderson  * SYS_SEEK returns 0 on success, not the resulting offset.
2809a894704SRichard Henderson  */
common_semi_seek_cb(CPUState * cs,uint64_t ret,int err)28164c8c6a9SRichard Henderson static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err)
2829a894704SRichard Henderson {
2839a894704SRichard Henderson     if (!err) {
2849a894704SRichard Henderson         ret = 0;
2859a894704SRichard Henderson     }
2869a894704SRichard Henderson     common_semi_cb(cs, ret, err);
2879a894704SRichard Henderson }
2889a894704SRichard Henderson 
2899a894704SRichard Henderson /*
2903c820ddcSRichard Henderson  * Return an address in target memory of 64 bytes where the remote
2918df9f0c3SPhilippe Mathieu-Daudé  * gdb should write its stat struct. (The format of this structure
2928df9f0c3SPhilippe Mathieu-Daudé  * is defined by GDB's remote protocol and is not target-specific.)
2938df9f0c3SPhilippe Mathieu-Daudé  * We put this on the guest's stack just below SP.
2948df9f0c3SPhilippe Mathieu-Daudé  */
common_semi_flen_buf(CPUState * cs)2953c820ddcSRichard Henderson static target_ulong common_semi_flen_buf(CPUState *cs)
2963c820ddcSRichard Henderson {
2973c820ddcSRichard Henderson     target_ulong sp = common_semi_stack_bottom(cs);
2988df9f0c3SPhilippe Mathieu-Daudé     return sp - 64;
2998df9f0c3SPhilippe Mathieu-Daudé }
3008df9f0c3SPhilippe Mathieu-Daudé 
3018df9f0c3SPhilippe Mathieu-Daudé static void
common_semi_flen_fstat_cb(CPUState * cs,uint64_t ret,int err)30264c8c6a9SRichard Henderson common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err)
3038df9f0c3SPhilippe Mathieu-Daudé {
30484ca0dfdSRichard Henderson     if (!err) {
305cd7f29e3SRichard Henderson         /* The size is always stored in big-endian order, extract the value. */
306cd7f29e3SRichard Henderson         uint64_t size;
307a6300ed6SRichard Henderson         if (cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) +
308cd7f29e3SRichard Henderson                                 offsetof(struct gdb_stat, gdb_st_size),
309a6300ed6SRichard Henderson                                 &size, 8, 0)) {
310a6300ed6SRichard Henderson             ret = -1, err = EFAULT;
311a6300ed6SRichard Henderson         } else {
312a6300ed6SRichard Henderson             size = be64_to_cpu(size);
313a6300ed6SRichard Henderson             if (ret != size) {
314a6300ed6SRichard Henderson                 ret = -1, err = EOVERFLOW;
315a6300ed6SRichard Henderson             }
316a6300ed6SRichard Henderson         }
31784ca0dfdSRichard Henderson     }
31884ca0dfdSRichard Henderson     common_semi_cb(cs, ret, err);
3198df9f0c3SPhilippe Mathieu-Daudé }
3208df9f0c3SPhilippe Mathieu-Daudé 
3211577eec0SRichard Henderson static void
common_semi_readc_cb(CPUState * cs,uint64_t ret,int err)3221577eec0SRichard Henderson common_semi_readc_cb(CPUState *cs, uint64_t ret, int err)
3231577eec0SRichard Henderson {
3241577eec0SRichard Henderson     if (!err) {
325b77af26eSRichard Henderson         CPUArchState *env G_GNUC_UNUSED = cpu_env(cs);
3261577eec0SRichard Henderson         uint8_t ch;
3271577eec0SRichard Henderson 
3281577eec0SRichard Henderson         if (get_user_u8(ch, common_semi_stack_bottom(cs) - 1)) {
3291577eec0SRichard Henderson             ret = -1, err = EFAULT;
3301577eec0SRichard Henderson         } else {
3311577eec0SRichard Henderson             ret = ch;
3321577eec0SRichard Henderson         }
3331577eec0SRichard Henderson     }
3341577eec0SRichard Henderson     common_semi_cb(cs, ret, err);
3351577eec0SRichard Henderson }
3361577eec0SRichard Henderson 
3378df9f0c3SPhilippe Mathieu-Daudé #define SHFB_MAGIC_0 0x53
3388df9f0c3SPhilippe Mathieu-Daudé #define SHFB_MAGIC_1 0x48
3398df9f0c3SPhilippe Mathieu-Daudé #define SHFB_MAGIC_2 0x46
3408df9f0c3SPhilippe Mathieu-Daudé #define SHFB_MAGIC_3 0x42
3418df9f0c3SPhilippe Mathieu-Daudé 
3428df9f0c3SPhilippe Mathieu-Daudé /* Feature bits reportable in feature byte 0 */
3438df9f0c3SPhilippe Mathieu-Daudé #define SH_EXT_EXIT_EXTENDED (1 << 0)
3448df9f0c3SPhilippe Mathieu-Daudé #define SH_EXT_STDOUT_STDERR (1 << 1)
3458df9f0c3SPhilippe Mathieu-Daudé 
3468df9f0c3SPhilippe Mathieu-Daudé static const uint8_t featurefile_data[] = {
3478df9f0c3SPhilippe Mathieu-Daudé     SHFB_MAGIC_0,
3488df9f0c3SPhilippe Mathieu-Daudé     SHFB_MAGIC_1,
3498df9f0c3SPhilippe Mathieu-Daudé     SHFB_MAGIC_2,
3508df9f0c3SPhilippe Mathieu-Daudé     SHFB_MAGIC_3,
3518df9f0c3SPhilippe Mathieu-Daudé     SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */
3528df9f0c3SPhilippe Mathieu-Daudé };
3538df9f0c3SPhilippe Mathieu-Daudé 
3543960ca5bSAlex Bennée /*
3558df9f0c3SPhilippe Mathieu-Daudé  * Do a semihosting call.
3568df9f0c3SPhilippe Mathieu-Daudé  *
3578df9f0c3SPhilippe Mathieu-Daudé  * The specification always says that the "return register" either
3588df9f0c3SPhilippe Mathieu-Daudé  * returns a specific value or is corrupted, so we don't need to
3598df9f0c3SPhilippe Mathieu-Daudé  * report to our caller whether we are returning a value or trying to
3605d77289dSRichard Henderson  * leave the register unchanged.
3618df9f0c3SPhilippe Mathieu-Daudé  */
do_common_semihosting(CPUState * cs)362ed3a06b1SRichard Henderson void do_common_semihosting(CPUState *cs)
3638df9f0c3SPhilippe Mathieu-Daudé {
364b77af26eSRichard Henderson     CPUArchState *env = cpu_env(cs);
3658df9f0c3SPhilippe Mathieu-Daudé     target_ulong args;
3668df9f0c3SPhilippe Mathieu-Daudé     target_ulong arg0, arg1, arg2, arg3;
3678df9f0c3SPhilippe Mathieu-Daudé     target_ulong ul_ret;
3688df9f0c3SPhilippe Mathieu-Daudé     char * s;
3698df9f0c3SPhilippe Mathieu-Daudé     int nr;
3708df9f0c3SPhilippe Mathieu-Daudé     int64_t elapsed;
3718df9f0c3SPhilippe Mathieu-Daudé 
3728df9f0c3SPhilippe Mathieu-Daudé     nr = common_semi_arg(cs, 0) & 0xffffffffU;
3738df9f0c3SPhilippe Mathieu-Daudé     args = common_semi_arg(cs, 1);
3748df9f0c3SPhilippe Mathieu-Daudé 
3758df9f0c3SPhilippe Mathieu-Daudé     switch (nr) {
3768df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_OPEN:
3778df9f0c3SPhilippe Mathieu-Daudé     {
3788df9f0c3SPhilippe Mathieu-Daudé         int ret, err = 0;
379ed3a06b1SRichard Henderson         int hostfd;
380ed3a06b1SRichard Henderson 
3818df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
3828df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
3838df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(2);
3848df9f0c3SPhilippe Mathieu-Daudé         s = lock_user_string(arg0);
3858df9f0c3SPhilippe Mathieu-Daudé         if (!s) {
3868df9f0c3SPhilippe Mathieu-Daudé             goto do_fault;
387ed3a06b1SRichard Henderson         }
3888df9f0c3SPhilippe Mathieu-Daudé         if (arg1 >= 12) {
3898df9f0c3SPhilippe Mathieu-Daudé             unlock_user(s, arg0, 0);
3908df9f0c3SPhilippe Mathieu-Daudé             common_semi_cb(cs, -1, EINVAL);
391ed3a06b1SRichard Henderson             break;
392ed3a06b1SRichard Henderson         }
3938df9f0c3SPhilippe Mathieu-Daudé 
3948df9f0c3SPhilippe Mathieu-Daudé         if (strcmp(s, ":tt") == 0) {
3958df9f0c3SPhilippe Mathieu-Daudé             /*
3968df9f0c3SPhilippe Mathieu-Daudé              * We implement SH_EXT_STDOUT_STDERR, so:
3978df9f0c3SPhilippe Mathieu-Daudé              *  open for read == stdin
3988df9f0c3SPhilippe Mathieu-Daudé              *  open for write == stdout
3998df9f0c3SPhilippe Mathieu-Daudé              *  open for append == stderr
4008df9f0c3SPhilippe Mathieu-Daudé              */
4018df9f0c3SPhilippe Mathieu-Daudé             if (arg1 < 4) {
4028df9f0c3SPhilippe Mathieu-Daudé                 hostfd = STDIN_FILENO;
403ed3a06b1SRichard Henderson             } else if (arg1 < 8) {
4048df9f0c3SPhilippe Mathieu-Daudé                 hostfd = STDOUT_FILENO;
405ed3a06b1SRichard Henderson             } else {
4068df9f0c3SPhilippe Mathieu-Daudé                 hostfd = STDERR_FILENO;
407ed3a06b1SRichard Henderson             }
4088df9f0c3SPhilippe Mathieu-Daudé             ret = alloc_guestfd();
409ed3a06b1SRichard Henderson             associate_guestfd(ret, hostfd);
410ed3a06b1SRichard Henderson         } else if (strcmp(s, ":semihosting-features") == 0) {
411ed3a06b1SRichard Henderson             /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */
4128df9f0c3SPhilippe Mathieu-Daudé             if (arg1 != 0 && arg1 != 1) {
4138df9f0c3SPhilippe Mathieu-Daudé                 ret = -1;
414ed3a06b1SRichard Henderson                 err = EACCES;
415ed3a06b1SRichard Henderson             } else {
416ed3a06b1SRichard Henderson                 ret = alloc_guestfd();
417ed3a06b1SRichard Henderson                 staticfile_guestfd(ret, featurefile_data,
418ed3a06b1SRichard Henderson                                    sizeof(featurefile_data));
4191c6ff720SRichard Henderson             }
4208df9f0c3SPhilippe Mathieu-Daudé         } else {
4215b3f39cbSRichard Henderson             unlock_user(s, arg0, 0);
422ed3a06b1SRichard Henderson             semihost_sys_open(cs, common_semi_cb, arg0, arg2 + 1,
4235b3f39cbSRichard Henderson                               gdb_open_modeflags[arg1], 0644);
4245b3f39cbSRichard Henderson             break;
425ed3a06b1SRichard Henderson         }
4268df9f0c3SPhilippe Mathieu-Daudé         unlock_user(s, arg0, 0);
4278df9f0c3SPhilippe Mathieu-Daudé         common_semi_cb(cs, ret, err);
428ed3a06b1SRichard Henderson         break;
429ed3a06b1SRichard Henderson     }
4308df9f0c3SPhilippe Mathieu-Daudé 
431ed3a06b1SRichard Henderson     case TARGET_SYS_CLOSE:
4328df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4338df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_close(cs, common_semi_cb, arg0);
4345eadbbfcSRichard Henderson         break;
435ed3a06b1SRichard Henderson 
436ed3a06b1SRichard Henderson     case TARGET_SYS_WRITEC:
4378df9f0c3SPhilippe Mathieu-Daudé         /*
4385d77289dSRichard Henderson          * FIXME: the byte to be written is in a target_ulong slot,
4395d77289dSRichard Henderson          * which means this is wrong for a big-endian guest.
4405d77289dSRichard Henderson          */
4415d77289dSRichard Henderson         semihost_sys_write_gf(cs, common_semi_dead_cb,
4425d77289dSRichard Henderson                               &console_out_gf, args, 1);
4435d77289dSRichard Henderson         break;
444ed3a06b1SRichard Henderson 
445ed3a06b1SRichard Henderson     case TARGET_SYS_WRITE0:
4468df9f0c3SPhilippe Mathieu-Daudé         {
4477281550cSRichard Henderson             ssize_t len = target_strlen(args);
4487281550cSRichard Henderson             if (len < 0) {
4497281550cSRichard Henderson                 common_semi_dead_cb(cs, -1, EFAULT);
4507281550cSRichard Henderson             } else {
4517281550cSRichard Henderson                 semihost_sys_write_gf(cs, common_semi_dead_cb,
4527281550cSRichard Henderson                                       &console_out_gf, args, len);
4537281550cSRichard Henderson             }
4547281550cSRichard Henderson         }
4557281550cSRichard Henderson         break;
456ed3a06b1SRichard Henderson 
457ed3a06b1SRichard Henderson     case TARGET_SYS_WRITE:
4588df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4598df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
4608df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(2);
4618df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_write(cs, common_semi_rw_cb, arg0, arg1, arg2);
462aa915bd0SRichard Henderson         break;
463ed3a06b1SRichard Henderson 
4648df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_READ:
4658df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4668df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
4678df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(2);
4688df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_read(cs, common_semi_rw_cb, arg0, arg1, arg2);
469af0484b5SRichard Henderson         break;
470ed3a06b1SRichard Henderson 
4718df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_READC:
4728df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_read_gf(cs, common_semi_readc_cb, &console_in_gf,
4731577eec0SRichard Henderson                              common_semi_stack_bottom(cs) - 1, 1);
4741577eec0SRichard Henderson         break;
475ed3a06b1SRichard Henderson 
476ed3a06b1SRichard Henderson     case TARGET_SYS_ISERROR:
4778df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4788df9f0c3SPhilippe Mathieu-Daudé         common_semi_set_ret(cs, (target_long)arg0 < 0);
479ed3a06b1SRichard Henderson         break;
480ed3a06b1SRichard Henderson 
481ed3a06b1SRichard Henderson     case TARGET_SYS_ISTTY:
4828df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4838df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_isatty(cs, common_semi_istty_cb, arg0);
484a2212474SRichard Henderson         break;
485ed3a06b1SRichard Henderson 
4868df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_SEEK:
4878df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4888df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
4898df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET);
4909a894704SRichard Henderson         break;
491ed3a06b1SRichard Henderson 
4928df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_FLEN:
4938df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
4948df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_flen(cs, common_semi_flen_fstat_cb, common_semi_cb,
495a6300ed6SRichard Henderson                           arg0, common_semi_flen_buf(cs));
496a6300ed6SRichard Henderson         break;
497ed3a06b1SRichard Henderson 
4988df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_TMPNAM:
4998df9f0c3SPhilippe Mathieu-Daudé     {
500ed3a06b1SRichard Henderson         int len;
501ed3a06b1SRichard Henderson         char *p;
502ed3a06b1SRichard Henderson 
503ed3a06b1SRichard Henderson         GET_ARG(0);
5048df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
5058df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(2);
5068df9f0c3SPhilippe Mathieu-Daudé         len = asprintf(&s, "%s/qemu-%x%02x", g_get_tmp_dir(),
5073878d0c7SBin Meng                        getpid(), (int)arg1 & 0xff);
5083878d0c7SBin Meng         if (len < 0) {
5099b1268f5SPeter Maydell             common_semi_set_ret(cs, -1);
5109b1268f5SPeter Maydell             break;
5119b1268f5SPeter Maydell         }
5129b1268f5SPeter Maydell 
5139b1268f5SPeter Maydell         /* Allow for trailing NUL */
5149b1268f5SPeter Maydell         len++;
5159b1268f5SPeter Maydell         /* Make sure there's enough space in the buffer */
5168df9f0c3SPhilippe Mathieu-Daudé         if (len > arg2) {
5179b1268f5SPeter Maydell             free(s);
5189b1268f5SPeter Maydell             common_semi_set_ret(cs, -1);
519ed3a06b1SRichard Henderson             break;
520ed3a06b1SRichard Henderson         }
5218df9f0c3SPhilippe Mathieu-Daudé         p = lock_user(VERIFY_WRITE, arg0, len, 0);
522ed3a06b1SRichard Henderson         if (!p) {
523ed3a06b1SRichard Henderson             free(s);
5249b1268f5SPeter Maydell             goto do_fault;
525ed3a06b1SRichard Henderson         }
526ed3a06b1SRichard Henderson         memcpy(p, s, len);
5279b1268f5SPeter Maydell         unlock_user(p, arg0, len);
528ed3a06b1SRichard Henderson         free(s);
5298df9f0c3SPhilippe Mathieu-Daudé         common_semi_set_ret(cs, 0);
530ed3a06b1SRichard Henderson         break;
531ed3a06b1SRichard Henderson     }
532ed3a06b1SRichard Henderson 
533ed3a06b1SRichard Henderson     case TARGET_SYS_REMOVE:
5348df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
5358df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
5368df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_remove(cs, common_semi_cb, arg0, arg1 + 1);
537d49e79b8SRichard Henderson         break;
538ed3a06b1SRichard Henderson 
539ed3a06b1SRichard Henderson     case TARGET_SYS_RENAME:
5408df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
5418df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
5428df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(2);
5438df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(3);
5448df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_rename(cs, common_semi_cb, arg0, arg1 + 1, arg2, arg3 + 1);
54525a95da0SRichard Henderson         break;
546ed3a06b1SRichard Henderson 
547ed3a06b1SRichard Henderson     case TARGET_SYS_CLOCK:
5488df9f0c3SPhilippe Mathieu-Daudé         common_semi_set_ret(cs, clock() / (CLOCKS_PER_SEC / 100));
549ed3a06b1SRichard Henderson         break;
550ed3a06b1SRichard Henderson 
551ed3a06b1SRichard Henderson     case TARGET_SYS_TIME:
5528df9f0c3SPhilippe Mathieu-Daudé         ul_ret = time(NULL);
553ed3a06b1SRichard Henderson         common_semi_cb(cs, ul_ret, ul_ret == -1 ? errno : 0);
554ed3a06b1SRichard Henderson         break;
555ed3a06b1SRichard Henderson 
556ed3a06b1SRichard Henderson     case TARGET_SYS_SYSTEM:
5578df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(0);
5588df9f0c3SPhilippe Mathieu-Daudé         GET_ARG(1);
5598df9f0c3SPhilippe Mathieu-Daudé         semihost_sys_system(cs, common_semi_cb, arg0, arg1 + 1);
56090d8e0b0SRichard Henderson         break;
561ed3a06b1SRichard Henderson 
562ed3a06b1SRichard Henderson     case TARGET_SYS_ERRNO:
5638df9f0c3SPhilippe Mathieu-Daudé         common_semi_set_ret(cs, get_swi_errno(cs));
564ed3a06b1SRichard Henderson         break;
565ed3a06b1SRichard Henderson 
566ed3a06b1SRichard Henderson     case TARGET_SYS_GET_CMDLINE:
5678df9f0c3SPhilippe Mathieu-Daudé         {
5688df9f0c3SPhilippe Mathieu-Daudé             /* Build a command-line from the original argv.
5698df9f0c3SPhilippe Mathieu-Daudé              *
5708df9f0c3SPhilippe Mathieu-Daudé              * The inputs are:
5718df9f0c3SPhilippe Mathieu-Daudé              *     * arg0, pointer to a buffer of at least the size
5728df9f0c3SPhilippe Mathieu-Daudé              *               specified in arg1.
5738df9f0c3SPhilippe Mathieu-Daudé              *     * arg1, size of the buffer pointed to by arg0 in
5748df9f0c3SPhilippe Mathieu-Daudé              *               bytes.
5758df9f0c3SPhilippe Mathieu-Daudé              *
5768df9f0c3SPhilippe Mathieu-Daudé              * The outputs are:
5778df9f0c3SPhilippe Mathieu-Daudé              *     * arg0, pointer to null-terminated string of the
5788df9f0c3SPhilippe Mathieu-Daudé              *               command line.
5798df9f0c3SPhilippe Mathieu-Daudé              *     * arg1, length of the string pointed to by arg0.
5808df9f0c3SPhilippe Mathieu-Daudé              */
5818df9f0c3SPhilippe Mathieu-Daudé 
5828df9f0c3SPhilippe Mathieu-Daudé             char *output_buffer;
5838df9f0c3SPhilippe Mathieu-Daudé             size_t input_size;
5848df9f0c3SPhilippe Mathieu-Daudé             size_t output_size;
5858df9f0c3SPhilippe Mathieu-Daudé             int status = 0;
5868df9f0c3SPhilippe Mathieu-Daudé #if !defined(CONFIG_USER_ONLY)
5878df9f0c3SPhilippe Mathieu-Daudé             const char *cmdline;
5888df9f0c3SPhilippe Mathieu-Daudé #else
5898df9f0c3SPhilippe Mathieu-Daudé             TaskState *ts = cs->opaque;
5908df9f0c3SPhilippe Mathieu-Daudé #endif
5918df9f0c3SPhilippe Mathieu-Daudé             GET_ARG(0);
5928df9f0c3SPhilippe Mathieu-Daudé             GET_ARG(1);
5938df9f0c3SPhilippe Mathieu-Daudé             input_size = arg1;
5948df9f0c3SPhilippe Mathieu-Daudé             /* Compute the size of the output string.  */
5958df9f0c3SPhilippe Mathieu-Daudé #if !defined(CONFIG_USER_ONLY)
5968df9f0c3SPhilippe Mathieu-Daudé             cmdline = semihosting_get_cmdline();
5978df9f0c3SPhilippe Mathieu-Daudé             if (cmdline == NULL) {
5988df9f0c3SPhilippe Mathieu-Daudé                 cmdline = ""; /* Default to an empty line. */
5998df9f0c3SPhilippe Mathieu-Daudé             }
6008df9f0c3SPhilippe Mathieu-Daudé             output_size = strlen(cmdline) + 1; /* Count terminating 0. */
6018df9f0c3SPhilippe Mathieu-Daudé #else
6028df9f0c3SPhilippe Mathieu-Daudé             unsigned int i;
6038df9f0c3SPhilippe Mathieu-Daudé 
6048df9f0c3SPhilippe Mathieu-Daudé             output_size = ts->info->env_strings - ts->info->arg_strings;
60560f1c801SRichard Henderson             if (!output_size) {
6068df9f0c3SPhilippe Mathieu-Daudé                 /*
6078df9f0c3SPhilippe Mathieu-Daudé                  * We special-case the "empty command line" case (argc==0).
6088df9f0c3SPhilippe Mathieu-Daudé                  * Just provide the terminating 0.
6098df9f0c3SPhilippe Mathieu-Daudé                  */
6108df9f0c3SPhilippe Mathieu-Daudé                 output_size = 1;
6118df9f0c3SPhilippe Mathieu-Daudé             }
6128df9f0c3SPhilippe Mathieu-Daudé #endif
6138df9f0c3SPhilippe Mathieu-Daudé 
6148df9f0c3SPhilippe Mathieu-Daudé             if (output_size > input_size) {
6158df9f0c3SPhilippe Mathieu-Daudé                 /* Not enough space to store command-line arguments.  */
6168df9f0c3SPhilippe Mathieu-Daudé                 common_semi_cb(cs, -1, E2BIG);
617ed3a06b1SRichard Henderson                 break;
618ed3a06b1SRichard Henderson             }
6198df9f0c3SPhilippe Mathieu-Daudé 
6208df9f0c3SPhilippe Mathieu-Daudé             /* Adjust the command-line length.  */
6218df9f0c3SPhilippe Mathieu-Daudé             if (SET_ARG(1, output_size - 1)) {
6228df9f0c3SPhilippe Mathieu-Daudé                 /* Couldn't write back to argument block */
6238df9f0c3SPhilippe Mathieu-Daudé                 goto do_fault;
624ed3a06b1SRichard Henderson             }
6258df9f0c3SPhilippe Mathieu-Daudé 
6268df9f0c3SPhilippe Mathieu-Daudé             /* Lock the buffer on the ARM side.  */
6278df9f0c3SPhilippe Mathieu-Daudé             output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
6288df9f0c3SPhilippe Mathieu-Daudé             if (!output_buffer) {
6298df9f0c3SPhilippe Mathieu-Daudé                 goto do_fault;
630ed3a06b1SRichard Henderson             }
6318df9f0c3SPhilippe Mathieu-Daudé 
6328df9f0c3SPhilippe Mathieu-Daudé             /* Copy the command-line arguments.  */
6338df9f0c3SPhilippe Mathieu-Daudé #if !defined(CONFIG_USER_ONLY)
6348df9f0c3SPhilippe Mathieu-Daudé             pstrcpy(output_buffer, output_size, cmdline);
6358df9f0c3SPhilippe Mathieu-Daudé #else
6368df9f0c3SPhilippe Mathieu-Daudé             if (output_size == 1) {
6378df9f0c3SPhilippe Mathieu-Daudé                 /* Empty command-line.  */
6388df9f0c3SPhilippe Mathieu-Daudé                 output_buffer[0] = '\0';
6398df9f0c3SPhilippe Mathieu-Daudé                 goto out;
6408df9f0c3SPhilippe Mathieu-Daudé             }
6418df9f0c3SPhilippe Mathieu-Daudé 
6428df9f0c3SPhilippe Mathieu-Daudé             if (copy_from_user(output_buffer, ts->info->arg_strings,
64360f1c801SRichard Henderson                                output_size)) {
6448df9f0c3SPhilippe Mathieu-Daudé                 unlock_user(output_buffer, arg0, 0);
645ed3a06b1SRichard Henderson                 goto do_fault;
646ed3a06b1SRichard Henderson             }
6478df9f0c3SPhilippe Mathieu-Daudé 
6488df9f0c3SPhilippe Mathieu-Daudé             /* Separate arguments by white spaces.  */
6498df9f0c3SPhilippe Mathieu-Daudé             for (i = 0; i < output_size - 1; i++) {
6508df9f0c3SPhilippe Mathieu-Daudé                 if (output_buffer[i] == 0) {
6518df9f0c3SPhilippe Mathieu-Daudé                     output_buffer[i] = ' ';
6528df9f0c3SPhilippe Mathieu-Daudé                 }
6538df9f0c3SPhilippe Mathieu-Daudé             }
6548df9f0c3SPhilippe Mathieu-Daudé         out:
6558df9f0c3SPhilippe Mathieu-Daudé #endif
6568df9f0c3SPhilippe Mathieu-Daudé             /* Unlock the buffer on the ARM side.  */
6578df9f0c3SPhilippe Mathieu-Daudé             unlock_user(output_buffer, arg0, output_size);
6588df9f0c3SPhilippe Mathieu-Daudé             common_semi_cb(cs, status, 0);
659ed3a06b1SRichard Henderson         }
6608df9f0c3SPhilippe Mathieu-Daudé         break;
661ed3a06b1SRichard Henderson 
662ed3a06b1SRichard Henderson     case TARGET_SYS_HEAPINFO:
6638df9f0c3SPhilippe Mathieu-Daudé         {
6648df9f0c3SPhilippe Mathieu-Daudé             target_ulong retvals[4];
6658df9f0c3SPhilippe Mathieu-Daudé             int i;
6668df9f0c3SPhilippe Mathieu-Daudé #ifdef CONFIG_USER_ONLY
6678df9f0c3SPhilippe Mathieu-Daudé             TaskState *ts = cs->opaque;
6688df9f0c3SPhilippe Mathieu-Daudé             target_ulong limit;
6695fc983afSAlex Bennée #else
6708df9f0c3SPhilippe Mathieu-Daudé             LayoutInfo info = common_semi_find_bases(cs);
6715fc983afSAlex Bennée #endif
6728df9f0c3SPhilippe Mathieu-Daudé 
6738df9f0c3SPhilippe Mathieu-Daudé             GET_ARG(0);
6748df9f0c3SPhilippe Mathieu-Daudé 
6758df9f0c3SPhilippe Mathieu-Daudé #ifdef CONFIG_USER_ONLY
6768df9f0c3SPhilippe Mathieu-Daudé             /*
6778df9f0c3SPhilippe Mathieu-Daudé              * Some C libraries assume the heap immediately follows .bss, so
6788df9f0c3SPhilippe Mathieu-Daudé              * allocate it using sbrk.
6798df9f0c3SPhilippe Mathieu-Daudé              */
6808df9f0c3SPhilippe Mathieu-Daudé             if (!ts->heap_limit) {
6818df9f0c3SPhilippe Mathieu-Daudé                 abi_ulong ret;
6828df9f0c3SPhilippe Mathieu-Daudé 
6838df9f0c3SPhilippe Mathieu-Daudé                 ts->heap_base = do_brk(0);
6848df9f0c3SPhilippe Mathieu-Daudé                 limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE;
6858df9f0c3SPhilippe Mathieu-Daudé                 /* Try a big heap, and reduce the size if that fails.  */
6868df9f0c3SPhilippe Mathieu-Daudé                 for (;;) {
6878df9f0c3SPhilippe Mathieu-Daudé                     ret = do_brk(limit);
6888df9f0c3SPhilippe Mathieu-Daudé                     if (ret >= limit) {
6898df9f0c3SPhilippe Mathieu-Daudé                         break;
6908df9f0c3SPhilippe Mathieu-Daudé                     }
6918df9f0c3SPhilippe Mathieu-Daudé                     limit = (ts->heap_base >> 1) + (limit >> 1);
6928df9f0c3SPhilippe Mathieu-Daudé                 }
6938df9f0c3SPhilippe Mathieu-Daudé                 ts->heap_limit = limit;
6948df9f0c3SPhilippe Mathieu-Daudé             }
6958df9f0c3SPhilippe Mathieu-Daudé 
6968df9f0c3SPhilippe Mathieu-Daudé             retvals[0] = ts->heap_base;
6978df9f0c3SPhilippe Mathieu-Daudé             retvals[1] = ts->heap_limit;
6988df9f0c3SPhilippe Mathieu-Daudé             retvals[2] = ts->stack_base;
6998df9f0c3SPhilippe Mathieu-Daudé             retvals[3] = 0; /* Stack limit.  */
7008df9f0c3SPhilippe Mathieu-Daudé #else
7018df9f0c3SPhilippe Mathieu-Daudé             retvals[0] = info.heapbase;  /* Heap Base */
7025fc983afSAlex Bennée             retvals[1] = info.heaplimit; /* Heap Limit */
7035fc983afSAlex Bennée             retvals[2] = info.heaplimit; /* Stack base */
7045fc983afSAlex Bennée             retvals[3] = info.heapbase;  /* Stack limit.  */
7055fc983afSAlex Bennée #endif
7068df9f0c3SPhilippe Mathieu-Daudé 
7078df9f0c3SPhilippe Mathieu-Daudé             for (i = 0; i < ARRAY_SIZE(retvals); i++) {
7088df9f0c3SPhilippe Mathieu-Daudé                 bool fail;
7098df9f0c3SPhilippe Mathieu-Daudé 
7108df9f0c3SPhilippe Mathieu-Daudé                 if (is_64bit_semihosting(env)) {
71135e3f029SAlex Bennée                     fail = put_user_u64(retvals[i], arg0 + i * 8);
71235e3f029SAlex Bennée                 } else {
71335e3f029SAlex Bennée                     fail = put_user_u32(retvals[i], arg0 + i * 4);
71435e3f029SAlex Bennée                 }
71535e3f029SAlex Bennée 
7168df9f0c3SPhilippe Mathieu-Daudé                 if (fail) {
7178df9f0c3SPhilippe Mathieu-Daudé                     /* Couldn't write back to argument block */
7188df9f0c3SPhilippe Mathieu-Daudé                     goto do_fault;
719ed3a06b1SRichard Henderson                 }
7208df9f0c3SPhilippe Mathieu-Daudé             }
7218df9f0c3SPhilippe Mathieu-Daudé             common_semi_set_ret(cs, 0);
722ed3a06b1SRichard Henderson         }
7238df9f0c3SPhilippe Mathieu-Daudé         break;
724ed3a06b1SRichard Henderson 
725ed3a06b1SRichard Henderson     case TARGET_SYS_EXIT:
7268df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_EXIT_EXTENDED:
7278df9f0c3SPhilippe Mathieu-Daudé     {
7288df9f0c3SPhilippe Mathieu-Daudé         uint32_t ret;
7298df9f0c3SPhilippe Mathieu-Daudé 
7308df9f0c3SPhilippe Mathieu-Daudé         if (common_semi_sys_exit_extended(cs, nr)) {
7318df9f0c3SPhilippe Mathieu-Daudé             /*
7328df9f0c3SPhilippe Mathieu-Daudé              * The A64 version of SYS_EXIT takes a parameter block,
7338df9f0c3SPhilippe Mathieu-Daudé              * so the application-exit type can return a subcode which
7348df9f0c3SPhilippe Mathieu-Daudé              * is the exit status code from the application.
7358df9f0c3SPhilippe Mathieu-Daudé              * SYS_EXIT_EXTENDED is an a new-in-v2.0 optional function
7368df9f0c3SPhilippe Mathieu-Daudé              * which allows A32/T32 guests to also provide a status code.
7378df9f0c3SPhilippe Mathieu-Daudé              */
7388df9f0c3SPhilippe Mathieu-Daudé             GET_ARG(0);
7398df9f0c3SPhilippe Mathieu-Daudé             GET_ARG(1);
7408df9f0c3SPhilippe Mathieu-Daudé 
7418df9f0c3SPhilippe Mathieu-Daudé             if (arg0 == ADP_Stopped_ApplicationExit) {
7428df9f0c3SPhilippe Mathieu-Daudé                 ret = arg1;
7438df9f0c3SPhilippe Mathieu-Daudé             } else {
7448df9f0c3SPhilippe Mathieu-Daudé                 ret = 1;
7458df9f0c3SPhilippe Mathieu-Daudé             }
7468df9f0c3SPhilippe Mathieu-Daudé         } else {
7478df9f0c3SPhilippe Mathieu-Daudé             /*
7488df9f0c3SPhilippe Mathieu-Daudé              * The A32/T32 version of SYS_EXIT specifies only
7498df9f0c3SPhilippe Mathieu-Daudé              * Stopped_ApplicationExit as normal exit, but does not
7508df9f0c3SPhilippe Mathieu-Daudé              * allow the guest to specify the exit status code.
7518df9f0c3SPhilippe Mathieu-Daudé              * Everything else is considered an error.
7528df9f0c3SPhilippe Mathieu-Daudé              */
7538df9f0c3SPhilippe Mathieu-Daudé             ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
7548df9f0c3SPhilippe Mathieu-Daudé         }
755ed3a06b1SRichard Henderson         gdb_exit(ret);
7568df9f0c3SPhilippe Mathieu-Daudé         exit(ret);
7578df9f0c3SPhilippe Mathieu-Daudé     }
7588df9f0c3SPhilippe Mathieu-Daudé 
759fed49cdfSPeter Maydell     case TARGET_SYS_ELAPSED:
760fed49cdfSPeter Maydell         elapsed = get_clock() - clock_start;
761fed49cdfSPeter Maydell         if (sizeof(target_ulong) == 8) {
7628df9f0c3SPhilippe Mathieu-Daudé             if (SET_ARG(0, elapsed)) {
763fed49cdfSPeter Maydell                 goto do_fault;
764fed49cdfSPeter Maydell             }
765fed49cdfSPeter Maydell         } else {
766fed49cdfSPeter Maydell             if (SET_ARG(0, (uint32_t) elapsed) ||
7678df9f0c3SPhilippe Mathieu-Daudé                 SET_ARG(1, (uint32_t) (elapsed >> 32))) {
768ed3a06b1SRichard Henderson                 goto do_fault;
769ed3a06b1SRichard Henderson             }
770ed3a06b1SRichard Henderson         }
7718df9f0c3SPhilippe Mathieu-Daudé         common_semi_set_ret(cs, 0);
7728df9f0c3SPhilippe Mathieu-Daudé         break;
773ed3a06b1SRichard Henderson 
774ed3a06b1SRichard Henderson     case TARGET_SYS_TICKFREQ:
775ed3a06b1SRichard Henderson         /* qemu always uses nsec */
7768df9f0c3SPhilippe Mathieu-Daudé         common_semi_set_ret(cs, 1000000000);
7778df9f0c3SPhilippe Mathieu-Daudé         break;
7788df9f0c3SPhilippe Mathieu-Daudé 
7798df9f0c3SPhilippe Mathieu-Daudé     case TARGET_SYS_SYNCCACHE:
7808df9f0c3SPhilippe Mathieu-Daudé         /*
7818df9f0c3SPhilippe Mathieu-Daudé          * Clean the D-cache and invalidate the I-cache for the specified
782a1df4babSRichard Henderson          * virtual address range. This is a nop for us since we don't
783ed3a06b1SRichard Henderson          * implement caches. This is only present on A64.
784ed3a06b1SRichard Henderson          */
7858df9f0c3SPhilippe Mathieu-Daudé         if (common_semi_has_synccache(env)) {
786a1df4babSRichard Henderson             common_semi_set_ret(cs, 0);
7878df9f0c3SPhilippe Mathieu-Daudé             break;
7888df9f0c3SPhilippe Mathieu-Daudé         }
7898df9f0c3SPhilippe Mathieu-Daudé         /* fall through */
7908df9f0c3SPhilippe Mathieu-Daudé     default:
791ed3a06b1SRichard Henderson         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
792ed3a06b1SRichard Henderson         cpu_dump_state(cs, stderr, 0);
793ed3a06b1SRichard Henderson         abort();
794ed3a06b1SRichard Henderson 
7958df9f0c3SPhilippe Mathieu-Daudé     do_fault:
7968df9f0c3SPhilippe Mathieu-Daudé         common_semi_cb(cs, -1, EFAULT);
797         break;
798     }
799 }
800