qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

arm-compat-semi.c (23684B)


      1 /*
      2  *  Semihosting support for systems modeled on the Arm "Angel"
      3  *  semihosting syscalls design. This includes Arm and RISC-V processors
      4  *
      5  *  Copyright (c) 2005, 2007 CodeSourcery.
      6  *  Copyright (c) 2019 Linaro
      7  *  Written by Paul Brook.
      8  *
      9  *  Copyright © 2020 by Keith Packard <keithp@keithp.com>
     10  *  Adapted for systems other than ARM, including RISC-V, by Keith Packard
     11  *
     12  *  This program is free software; you can redistribute it and/or modify
     13  *  it under the terms of the GNU General Public License as published by
     14  *  the Free Software Foundation; either version 2 of the License, or
     15  *  (at your option) any later version.
     16  *
     17  *  This program is distributed in the hope that it will be useful,
     18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20  *  GNU General Public License for more details.
     21  *
     22  *  You should have received a copy of the GNU General Public License
     23  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
     24  *
     25  *  ARM Semihosting is documented in:
     26  *     Semihosting for AArch32 and AArch64 Release 2.0
     27  *     https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst
     28  *
     29  *  RISC-V Semihosting is documented in:
     30  *     RISC-V Semihosting
     31  *     https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
     32  */
     33 
     34 #include "qemu/osdep.h"
     35 #include "qemu/timer.h"
     36 #include "exec/gdbstub.h"
     37 #include "semihosting/semihost.h"
     38 #include "semihosting/console.h"
     39 #include "semihosting/common-semi.h"
     40 #include "semihosting/guestfd.h"
     41 #include "semihosting/syscalls.h"
     42 
     43 #ifdef CONFIG_USER_ONLY
     44 #include "qemu.h"
     45 
     46 #define COMMON_SEMI_HEAP_SIZE (128 * 1024 * 1024)
     47 #else
     48 #include "qemu/cutils.h"
     49 #include "hw/loader.h"
     50 #include "hw/boards.h"
     51 #endif
     52 
     53 #define TARGET_SYS_OPEN        0x01
     54 #define TARGET_SYS_CLOSE       0x02
     55 #define TARGET_SYS_WRITEC      0x03
     56 #define TARGET_SYS_WRITE0      0x04
     57 #define TARGET_SYS_WRITE       0x05
     58 #define TARGET_SYS_READ        0x06
     59 #define TARGET_SYS_READC       0x07
     60 #define TARGET_SYS_ISERROR     0x08
     61 #define TARGET_SYS_ISTTY       0x09
     62 #define TARGET_SYS_SEEK        0x0a
     63 #define TARGET_SYS_FLEN        0x0c
     64 #define TARGET_SYS_TMPNAM      0x0d
     65 #define TARGET_SYS_REMOVE      0x0e
     66 #define TARGET_SYS_RENAME      0x0f
     67 #define TARGET_SYS_CLOCK       0x10
     68 #define TARGET_SYS_TIME        0x11
     69 #define TARGET_SYS_SYSTEM      0x12
     70 #define TARGET_SYS_ERRNO       0x13
     71 #define TARGET_SYS_GET_CMDLINE 0x15
     72 #define TARGET_SYS_HEAPINFO    0x16
     73 #define TARGET_SYS_EXIT        0x18
     74 #define TARGET_SYS_SYNCCACHE   0x19
     75 #define TARGET_SYS_EXIT_EXTENDED 0x20
     76 #define TARGET_SYS_ELAPSED     0x30
     77 #define TARGET_SYS_TICKFREQ    0x31
     78 
     79 /* ADP_Stopped_ApplicationExit is used for exit(0),
     80  * anything else is implemented as exit(1) */
     81 #define ADP_Stopped_ApplicationExit     (0x20026)
     82 
     83 #ifndef O_BINARY
     84 #define O_BINARY 0
     85 #endif
     86 
     87 static int gdb_open_modeflags[12] = {
     88     GDB_O_RDONLY,
     89     GDB_O_RDONLY,
     90     GDB_O_RDWR,
     91     GDB_O_RDWR,
     92     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
     93     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
     94     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
     95     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
     96     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
     97     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
     98     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
     99     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
    100 };
    101 
    102 #ifndef CONFIG_USER_ONLY
    103 
    104 /**
    105  * common_semi_find_bases: find information about ram and heap base
    106  *
    107  * This function attempts to provide meaningful numbers for RAM and
    108  * HEAP base addresses. The rambase is simply the lowest addressable
    109  * RAM position. For the heapbase we ask the loader to scan the
    110  * address space and the largest available gap by querying the "ROM"
    111  * regions.
    112  *
    113  * Returns: a structure with the numbers we need.
    114  */
    115 
    116 typedef struct LayoutInfo {
    117     target_ulong rambase;
    118     size_t ramsize;
    119     hwaddr heapbase;
    120     hwaddr heaplimit;
    121 } LayoutInfo;
    122 
    123 static bool find_ram_cb(Int128 start, Int128 len, const MemoryRegion *mr,
    124                         hwaddr offset_in_region, void *opaque)
    125 {
    126     LayoutInfo *info = (LayoutInfo *) opaque;
    127     uint64_t size = int128_get64(len);
    128 
    129     if (!mr->ram || mr->readonly) {
    130         return false;
    131     }
    132 
    133     if (size > info->ramsize) {
    134         info->rambase = int128_get64(start);
    135         info->ramsize = size;
    136     }
    137 
    138     /* search exhaustively for largest RAM */
    139     return false;
    140 }
    141 
    142 static LayoutInfo common_semi_find_bases(CPUState *cs)
    143 {
    144     FlatView *fv;
    145     LayoutInfo info = { 0, 0, 0, 0 };
    146 
    147     RCU_READ_LOCK_GUARD();
    148 
    149     fv = address_space_to_flatview(cs->as);
    150     flatview_for_each_range(fv, find_ram_cb, &info);
    151 
    152     /*
    153      * If we have found the RAM lets iterate through the ROM blobs to
    154      * work out the best place for the remainder of RAM and split it
    155      * equally between stack and heap.
    156      */
    157     if (info.rambase || info.ramsize > 0) {
    158         RomGap gap = rom_find_largest_gap_between(info.rambase, info.ramsize);
    159         info.heapbase = gap.base;
    160         info.heaplimit = gap.base + gap.size;
    161     }
    162 
    163     return info;
    164 }
    165 
    166 #endif
    167 
    168 #include "common-semi-target.h"
    169 
    170 /*
    171  * Read the input value from the argument block; fail the semihosting
    172  * call if the memory read fails. Eventually we could use a generic
    173  * CPUState helper function here.
    174  * Note that GET_ARG() handles memory access errors by jumping to
    175  * do_fault, so must be used as the first thing done in handling a
    176  * semihosting call, to avoid accidentally leaking allocated resources.
    177  * SET_ARG(), since it unavoidably happens late, instead returns an
    178  * error indication (0 on success, non-0 for error) which the caller
    179  * should check.
    180  */
    181 
    182 #define GET_ARG(n) do {                                 \
    183     if (is_64bit_semihosting(env)) {                    \
    184         if (get_user_u64(arg ## n, args + (n) * 8)) {   \
    185             goto do_fault;                              \
    186         }                                               \
    187     } else {                                            \
    188         if (get_user_u32(arg ## n, args + (n) * 4)) {   \
    189             goto do_fault;                              \
    190         }                                               \
    191     }                                                   \
    192 } while (0)
    193 
    194 #define SET_ARG(n, val)                                 \
    195     (is_64bit_semihosting(env) ?                        \
    196      put_user_u64(val, args + (n) * 8) :                \
    197      put_user_u32(val, args + (n) * 4))
    198 
    199 
    200 /*
    201  * The semihosting API has no concept of its errno being thread-safe,
    202  * as the API design predates SMP CPUs and was intended as a simple
    203  * real-hardware set of debug functionality. For QEMU, we make the
    204  * errno be per-thread in linux-user mode; in softmmu it is a simple
    205  * global, and we assume that the guest takes care of avoiding any races.
    206  */
    207 #ifndef CONFIG_USER_ONLY
    208 static target_ulong syscall_err;
    209 
    210 #include "semihosting/softmmu-uaccess.h"
    211 #endif
    212 
    213 static inline uint32_t get_swi_errno(CPUState *cs)
    214 {
    215 #ifdef CONFIG_USER_ONLY
    216     TaskState *ts = cs->opaque;
    217 
    218     return ts->swi_errno;
    219 #else
    220     return syscall_err;
    221 #endif
    222 }
    223 
    224 static void common_semi_cb(CPUState *cs, uint64_t ret, int err)
    225 {
    226     if (err) {
    227 #ifdef CONFIG_USER_ONLY
    228         TaskState *ts = cs->opaque;
    229         ts->swi_errno = err;
    230 #else
    231         syscall_err = err;
    232 #endif
    233     }
    234     common_semi_set_ret(cs, ret);
    235 }
    236 
    237 /*
    238  * Use 0xdeadbeef as the return value when there isn't a defined
    239  * return value for the call.
    240  */
    241 static void common_semi_dead_cb(CPUState *cs, uint64_t ret, int err)
    242 {
    243     common_semi_set_ret(cs, 0xdeadbeef);
    244 }
    245 
    246 /*
    247  * SYS_READ and SYS_WRITE always return the number of bytes not read/written.
    248  * There is no error condition, other than returning the original length.
    249  */
    250 static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err)
    251 {
    252     /* Recover the original length from the third argument. */
    253     CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
    254     target_ulong args = common_semi_arg(cs, 1);
    255     target_ulong arg2;
    256     GET_ARG(2);
    257 
    258     if (err) {
    259  do_fault:
    260         ret = 0; /* error: no bytes transmitted */
    261     }
    262     common_semi_set_ret(cs, arg2 - ret);
    263 }
    264 
    265 /*
    266  * Convert from Posix ret+errno to Arm SYS_ISTTY return values.
    267  * With gdbstub, err is only ever set for protocol errors to EIO.
    268  */
    269 static void common_semi_istty_cb(CPUState *cs, uint64_t ret, int err)
    270 {
    271     if (err) {
    272         ret = (err == ENOTTY ? 0 : -1);
    273     }
    274     common_semi_cb(cs, ret, err);
    275 }
    276 
    277 /*
    278  * SYS_SEEK returns 0 on success, not the resulting offset.
    279  */
    280 static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err)
    281 {
    282     if (!err) {
    283         ret = 0;
    284     }
    285     common_semi_cb(cs, ret, err);
    286 }
    287 
    288 /*
    289  * Return an address in target memory of 64 bytes where the remote
    290  * gdb should write its stat struct. (The format of this structure
    291  * is defined by GDB's remote protocol and is not target-specific.)
    292  * We put this on the guest's stack just below SP.
    293  */
    294 static target_ulong common_semi_flen_buf(CPUState *cs)
    295 {
    296     target_ulong sp = common_semi_stack_bottom(cs);
    297     return sp - 64;
    298 }
    299 
    300 static void
    301 common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err)
    302 {
    303     if (!err) {
    304         /* The size is always stored in big-endian order, extract the value. */
    305         uint64_t size;
    306         if (cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) +
    307                                 offsetof(struct gdb_stat, gdb_st_size),
    308                                 &size, 8, 0)) {
    309             ret = -1, err = EFAULT;
    310         } else {
    311             size = be64_to_cpu(size);
    312             if (ret != size) {
    313                 ret = -1, err = EOVERFLOW;
    314             }
    315         }
    316     }
    317     common_semi_cb(cs, ret, err);
    318 }
    319 
    320 static void
    321 common_semi_readc_cb(CPUState *cs, uint64_t ret, int err)
    322 {
    323     if (!err) {
    324         CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
    325         uint8_t ch;
    326 
    327         if (get_user_u8(ch, common_semi_stack_bottom(cs) - 1)) {
    328             ret = -1, err = EFAULT;
    329         } else {
    330             ret = ch;
    331         }
    332     }
    333     common_semi_cb(cs, ret, err);
    334 }
    335 
    336 #define SHFB_MAGIC_0 0x53
    337 #define SHFB_MAGIC_1 0x48
    338 #define SHFB_MAGIC_2 0x46
    339 #define SHFB_MAGIC_3 0x42
    340 
    341 /* Feature bits reportable in feature byte 0 */
    342 #define SH_EXT_EXIT_EXTENDED (1 << 0)
    343 #define SH_EXT_STDOUT_STDERR (1 << 1)
    344 
    345 static const uint8_t featurefile_data[] = {
    346     SHFB_MAGIC_0,
    347     SHFB_MAGIC_1,
    348     SHFB_MAGIC_2,
    349     SHFB_MAGIC_3,
    350     SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */
    351 };
    352 
    353 /*
    354  * Do a semihosting call.
    355  *
    356  * The specification always says that the "return register" either
    357  * returns a specific value or is corrupted, so we don't need to
    358  * report to our caller whether we are returning a value or trying to
    359  * leave the register unchanged.
    360  */
    361 void do_common_semihosting(CPUState *cs)
    362 {
    363     CPUArchState *env = cs->env_ptr;
    364     target_ulong args;
    365     target_ulong arg0, arg1, arg2, arg3;
    366     target_ulong ul_ret;
    367     char * s;
    368     int nr;
    369     uint32_t ret;
    370     int64_t elapsed;
    371 
    372     nr = common_semi_arg(cs, 0) & 0xffffffffU;
    373     args = common_semi_arg(cs, 1);
    374 
    375     switch (nr) {
    376     case TARGET_SYS_OPEN:
    377     {
    378         int ret, err = 0;
    379         int hostfd;
    380 
    381         GET_ARG(0);
    382         GET_ARG(1);
    383         GET_ARG(2);
    384         s = lock_user_string(arg0);
    385         if (!s) {
    386             goto do_fault;
    387         }
    388         if (arg1 >= 12) {
    389             unlock_user(s, arg0, 0);
    390             common_semi_cb(cs, -1, EINVAL);
    391             break;
    392         }
    393 
    394         if (strcmp(s, ":tt") == 0) {
    395             /*
    396              * We implement SH_EXT_STDOUT_STDERR, so:
    397              *  open for read == stdin
    398              *  open for write == stdout
    399              *  open for append == stderr
    400              */
    401             if (arg1 < 4) {
    402                 hostfd = STDIN_FILENO;
    403             } else if (arg1 < 8) {
    404                 hostfd = STDOUT_FILENO;
    405             } else {
    406                 hostfd = STDERR_FILENO;
    407             }
    408             ret = alloc_guestfd();
    409             associate_guestfd(ret, hostfd);
    410         } else if (strcmp(s, ":semihosting-features") == 0) {
    411             /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */
    412             if (arg1 != 0 && arg1 != 1) {
    413                 ret = -1;
    414                 err = EACCES;
    415             } else {
    416                 ret = alloc_guestfd();
    417                 staticfile_guestfd(ret, featurefile_data,
    418                                    sizeof(featurefile_data));
    419             }
    420         } else {
    421             unlock_user(s, arg0, 0);
    422             semihost_sys_open(cs, common_semi_cb, arg0, arg2 + 1,
    423                               gdb_open_modeflags[arg1], 0644);
    424             break;
    425         }
    426         unlock_user(s, arg0, 0);
    427         common_semi_cb(cs, ret, err);
    428         break;
    429     }
    430 
    431     case TARGET_SYS_CLOSE:
    432         GET_ARG(0);
    433         semihost_sys_close(cs, common_semi_cb, arg0);
    434         break;
    435 
    436     case TARGET_SYS_WRITEC:
    437         /*
    438          * FIXME: the byte to be written is in a target_ulong slot,
    439          * which means this is wrong for a big-endian guest.
    440          */
    441         semihost_sys_write_gf(cs, common_semi_dead_cb,
    442                               &console_out_gf, args, 1);
    443         break;
    444 
    445     case TARGET_SYS_WRITE0:
    446         {
    447             ssize_t len = target_strlen(args);
    448             if (len < 0) {
    449                 common_semi_dead_cb(cs, -1, EFAULT);
    450             } else {
    451                 semihost_sys_write_gf(cs, common_semi_dead_cb,
    452                                       &console_out_gf, args, len);
    453             }
    454         }
    455         break;
    456 
    457     case TARGET_SYS_WRITE:
    458         GET_ARG(0);
    459         GET_ARG(1);
    460         GET_ARG(2);
    461         semihost_sys_write(cs, common_semi_rw_cb, arg0, arg1, arg2);
    462         break;
    463 
    464     case TARGET_SYS_READ:
    465         GET_ARG(0);
    466         GET_ARG(1);
    467         GET_ARG(2);
    468         semihost_sys_read(cs, common_semi_rw_cb, arg0, arg1, arg2);
    469         break;
    470 
    471     case TARGET_SYS_READC:
    472         semihost_sys_read_gf(cs, common_semi_readc_cb, &console_in_gf,
    473                              common_semi_stack_bottom(cs) - 1, 1);
    474         break;
    475 
    476     case TARGET_SYS_ISERROR:
    477         GET_ARG(0);
    478         common_semi_set_ret(cs, (target_long)arg0 < 0);
    479         break;
    480 
    481     case TARGET_SYS_ISTTY:
    482         GET_ARG(0);
    483         semihost_sys_isatty(cs, common_semi_istty_cb, arg0);
    484         break;
    485 
    486     case TARGET_SYS_SEEK:
    487         GET_ARG(0);
    488         GET_ARG(1);
    489         semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET);
    490         break;
    491 
    492     case TARGET_SYS_FLEN:
    493         GET_ARG(0);
    494         semihost_sys_flen(cs, common_semi_flen_fstat_cb, common_semi_cb,
    495                           arg0, common_semi_flen_buf(cs));
    496         break;
    497 
    498     case TARGET_SYS_TMPNAM:
    499     {
    500         int len;
    501         char *p;
    502 
    503         GET_ARG(0);
    504         GET_ARG(1);
    505         GET_ARG(2);
    506         len = asprintf(&s, "%s/qemu-%x%02x", g_get_tmp_dir(),
    507                        getpid(), (int)arg1 & 0xff);
    508         if (len < 0) {
    509             common_semi_set_ret(cs, -1);
    510             break;
    511         }
    512 
    513         /* Allow for trailing NUL */
    514         len++;
    515         /* Make sure there's enough space in the buffer */
    516         if (len > arg2) {
    517             free(s);
    518             common_semi_set_ret(cs, -1);
    519             break;
    520         }
    521         p = lock_user(VERIFY_WRITE, arg0, len, 0);
    522         if (!p) {
    523             free(s);
    524             goto do_fault;
    525         }
    526         memcpy(p, s, len);
    527         unlock_user(p, arg0, len);
    528         free(s);
    529         common_semi_set_ret(cs, 0);
    530         break;
    531     }
    532 
    533     case TARGET_SYS_REMOVE:
    534         GET_ARG(0);
    535         GET_ARG(1);
    536         semihost_sys_remove(cs, common_semi_cb, arg0, arg1 + 1);
    537         break;
    538 
    539     case TARGET_SYS_RENAME:
    540         GET_ARG(0);
    541         GET_ARG(1);
    542         GET_ARG(2);
    543         GET_ARG(3);
    544         semihost_sys_rename(cs, common_semi_cb, arg0, arg1 + 1, arg2, arg3 + 1);
    545         break;
    546 
    547     case TARGET_SYS_CLOCK:
    548         common_semi_set_ret(cs, clock() / (CLOCKS_PER_SEC / 100));
    549         break;
    550 
    551     case TARGET_SYS_TIME:
    552         ul_ret = time(NULL);
    553         common_semi_cb(cs, ul_ret, ul_ret == -1 ? errno : 0);
    554         break;
    555 
    556     case TARGET_SYS_SYSTEM:
    557         GET_ARG(0);
    558         GET_ARG(1);
    559         semihost_sys_system(cs, common_semi_cb, arg0, arg1 + 1);
    560         break;
    561 
    562     case TARGET_SYS_ERRNO:
    563         common_semi_set_ret(cs, get_swi_errno(cs));
    564         break;
    565 
    566     case TARGET_SYS_GET_CMDLINE:
    567         {
    568             /* Build a command-line from the original argv.
    569              *
    570              * The inputs are:
    571              *     * arg0, pointer to a buffer of at least the size
    572              *               specified in arg1.
    573              *     * arg1, size of the buffer pointed to by arg0 in
    574              *               bytes.
    575              *
    576              * The outputs are:
    577              *     * arg0, pointer to null-terminated string of the
    578              *               command line.
    579              *     * arg1, length of the string pointed to by arg0.
    580              */
    581 
    582             char *output_buffer;
    583             size_t input_size;
    584             size_t output_size;
    585             int status = 0;
    586 #if !defined(CONFIG_USER_ONLY)
    587             const char *cmdline;
    588 #else
    589             TaskState *ts = cs->opaque;
    590 #endif
    591             GET_ARG(0);
    592             GET_ARG(1);
    593             input_size = arg1;
    594             /* Compute the size of the output string.  */
    595 #if !defined(CONFIG_USER_ONLY)
    596             cmdline = semihosting_get_cmdline();
    597             if (cmdline == NULL) {
    598                 cmdline = ""; /* Default to an empty line. */
    599             }
    600             output_size = strlen(cmdline) + 1; /* Count terminating 0. */
    601 #else
    602             unsigned int i;
    603 
    604             output_size = ts->info->env_strings - ts->info->arg_strings;
    605             if (!output_size) {
    606                 /*
    607                  * We special-case the "empty command line" case (argc==0).
    608                  * Just provide the terminating 0.
    609                  */
    610                 output_size = 1;
    611             }
    612 #endif
    613 
    614             if (output_size > input_size) {
    615                 /* Not enough space to store command-line arguments.  */
    616                 common_semi_cb(cs, -1, E2BIG);
    617                 break;
    618             }
    619 
    620             /* Adjust the command-line length.  */
    621             if (SET_ARG(1, output_size - 1)) {
    622                 /* Couldn't write back to argument block */
    623                 goto do_fault;
    624             }
    625 
    626             /* Lock the buffer on the ARM side.  */
    627             output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
    628             if (!output_buffer) {
    629                 goto do_fault;
    630             }
    631 
    632             /* Copy the command-line arguments.  */
    633 #if !defined(CONFIG_USER_ONLY)
    634             pstrcpy(output_buffer, output_size, cmdline);
    635 #else
    636             if (output_size == 1) {
    637                 /* Empty command-line.  */
    638                 output_buffer[0] = '\0';
    639                 goto out;
    640             }
    641 
    642             if (copy_from_user(output_buffer, ts->info->arg_strings,
    643                                output_size)) {
    644                 unlock_user(output_buffer, arg0, 0);
    645                 goto do_fault;
    646             }
    647 
    648             /* Separate arguments by white spaces.  */
    649             for (i = 0; i < output_size - 1; i++) {
    650                 if (output_buffer[i] == 0) {
    651                     output_buffer[i] = ' ';
    652                 }
    653             }
    654         out:
    655 #endif
    656             /* Unlock the buffer on the ARM side.  */
    657             unlock_user(output_buffer, arg0, output_size);
    658             common_semi_cb(cs, status, 0);
    659         }
    660         break;
    661 
    662     case TARGET_SYS_HEAPINFO:
    663         {
    664             target_ulong retvals[4];
    665             int i;
    666 #ifdef CONFIG_USER_ONLY
    667             TaskState *ts = cs->opaque;
    668             target_ulong limit;
    669 #else
    670             LayoutInfo info = common_semi_find_bases(cs);
    671 #endif
    672 
    673             GET_ARG(0);
    674 
    675 #ifdef CONFIG_USER_ONLY
    676             /*
    677              * Some C libraries assume the heap immediately follows .bss, so
    678              * allocate it using sbrk.
    679              */
    680             if (!ts->heap_limit) {
    681                 abi_ulong ret;
    682 
    683                 ts->heap_base = do_brk(0);
    684                 limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE;
    685                 /* Try a big heap, and reduce the size if that fails.  */
    686                 for (;;) {
    687                     ret = do_brk(limit);
    688                     if (ret >= limit) {
    689                         break;
    690                     }
    691                     limit = (ts->heap_base >> 1) + (limit >> 1);
    692                 }
    693                 ts->heap_limit = limit;
    694             }
    695 
    696             retvals[0] = ts->heap_base;
    697             retvals[1] = ts->heap_limit;
    698             retvals[2] = ts->stack_base;
    699             retvals[3] = 0; /* Stack limit.  */
    700 #else
    701             retvals[0] = info.heapbase;  /* Heap Base */
    702             retvals[1] = info.heaplimit; /* Heap Limit */
    703             retvals[2] = info.heaplimit; /* Stack base */
    704             retvals[3] = info.heapbase;  /* Stack limit.  */
    705 #endif
    706 
    707             for (i = 0; i < ARRAY_SIZE(retvals); i++) {
    708                 bool fail;
    709 
    710                 if (is_64bit_semihosting(env)) {
    711                     fail = put_user_u64(retvals[i], arg0 + i * 8);
    712                 } else {
    713                     fail = put_user_u32(retvals[i], arg0 + i * 4);
    714                 }
    715 
    716                 if (fail) {
    717                     /* Couldn't write back to argument block */
    718                     goto do_fault;
    719                 }
    720             }
    721             common_semi_set_ret(cs, 0);
    722         }
    723         break;
    724 
    725     case TARGET_SYS_EXIT:
    726     case TARGET_SYS_EXIT_EXTENDED:
    727         if (common_semi_sys_exit_extended(cs, nr)) {
    728             /*
    729              * The A64 version of SYS_EXIT takes a parameter block,
    730              * so the application-exit type can return a subcode which
    731              * is the exit status code from the application.
    732              * SYS_EXIT_EXTENDED is an a new-in-v2.0 optional function
    733              * which allows A32/T32 guests to also provide a status code.
    734              */
    735             GET_ARG(0);
    736             GET_ARG(1);
    737 
    738             if (arg0 == ADP_Stopped_ApplicationExit) {
    739                 ret = arg1;
    740             } else {
    741                 ret = 1;
    742             }
    743         } else {
    744             /*
    745              * The A32/T32 version of SYS_EXIT specifies only
    746              * Stopped_ApplicationExit as normal exit, but does not
    747              * allow the guest to specify the exit status code.
    748              * Everything else is considered an error.
    749              */
    750             ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
    751         }
    752         gdb_exit(ret);
    753         exit(ret);
    754 
    755     case TARGET_SYS_ELAPSED:
    756         elapsed = get_clock() - clock_start;
    757         if (sizeof(target_ulong) == 8) {
    758             if (SET_ARG(0, elapsed)) {
    759                 goto do_fault;
    760             }
    761         } else {
    762             if (SET_ARG(0, (uint32_t) elapsed) ||
    763                 SET_ARG(1, (uint32_t) (elapsed >> 32))) {
    764                 goto do_fault;
    765             }
    766         }
    767         common_semi_set_ret(cs, 0);
    768         break;
    769 
    770     case TARGET_SYS_TICKFREQ:
    771         /* qemu always uses nsec */
    772         common_semi_set_ret(cs, 1000000000);
    773         break;
    774 
    775     case TARGET_SYS_SYNCCACHE:
    776         /*
    777          * Clean the D-cache and invalidate the I-cache for the specified
    778          * virtual address range. This is a nop for us since we don't
    779          * implement caches. This is only present on A64.
    780          */
    781         if (common_semi_has_synccache(env)) {
    782             common_semi_set_ret(cs, 0);
    783             break;
    784         }
    785         /* fall through */
    786     default:
    787         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
    788         cpu_dump_state(cs, stderr, 0);
    789         abort();
    790 
    791     do_fault:
    792         common_semi_cb(cs, -1, EFAULT);
    793         break;
    794     }
    795 }