]> git.proxmox.com Git - mirror_qemu.git/blame - semihosting/arm-compat-semi.c
target/riscv: Add J extension state description
[mirror_qemu.git] / semihosting / arm-compat-semi.c
CommitLineData
a4f81979 1/*
0bb446d8 2 * Semihosting support for systems modeled on the Arm "Angel"
a10b9d93 3 * semihosting syscalls design. This includes Arm and RISC-V processors
5fafdf24 4 *
8e71621f 5 * Copyright (c) 2005, 2007 CodeSourcery.
4cb28db9 6 * Copyright (c) 2019 Linaro
8e71621f 7 * Written by Paul Brook.
a4f81979 8 *
0bb446d8
KP
9 * Copyright © 2020 by Keith Packard <keithp@keithp.com>
10 * Adapted for systems other than ARM, including RISC-V, by Keith Packard
11 *
a4f81979
FB
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
8167ee88 23 * along with this program; if not, see <http://www.gnu.org/licenses/>.
4cb28db9
AB
24 *
25 * ARM Semihosting is documented in:
26 * Semihosting for AArch32 and AArch64 Release 2.0
27 * https://static.docs.arm.com/100863/0200/semihosting.pdf
a10b9d93
KP
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
a4f81979
FB
32 */
33
74c21bd0 34#include "qemu/osdep.h"
a4f81979 35
6b5fe137
PMD
36#include "semihosting/semihost.h"
37#include "semihosting/console.h"
38#include "semihosting/common-semi.h"
4d834039 39#include "qemu/timer.h"
85b4fa0c 40#include "exec/gdbstub.h"
8e71621f 41#ifdef CONFIG_USER_ONLY
a4f81979
FB
42#include "qemu.h"
43
3c37cfe0 44#define COMMON_SEMI_HEAP_SIZE (128 * 1024 * 1024)
8e71621f 45#else
f348b6d1 46#include "qemu/cutils.h"
3c37cfe0 47#ifdef TARGET_ARM
69515951 48#include "hw/arm/boot.h"
3c37cfe0 49#endif
6e504a98 50#include "hw/boards.h"
8e71621f 51#endif
a4f81979 52
3881725c
SW
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
767ba049 60#define TARGET_SYS_ISERROR 0x08
3881725c
SW
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
e9ebfbfc 74#define TARGET_SYS_SYNCCACHE 0x19
22a43bb9 75#define TARGET_SYS_EXIT_EXTENDED 0x20
4d834039
KP
76#define TARGET_SYS_ELAPSED 0x30
77#define TARGET_SYS_TICKFREQ 0x31
a4f81979 78
1ecc3a2d
LI
79/* ADP_Stopped_ApplicationExit is used for exit(0),
80 * anything else is implemented as exit(1) */
81#define ADP_Stopped_ApplicationExit (0x20026)
82
a4f81979
FB
83#ifndef O_BINARY
84#define O_BINARY 0
85#endif
86
a2d1ebaf
PB
87#define GDB_O_RDONLY 0x000
88#define GDB_O_WRONLY 0x001
89#define GDB_O_RDWR 0x002
90#define GDB_O_APPEND 0x008
91#define GDB_O_CREAT 0x200
92#define GDB_O_TRUNC 0x400
93#define GDB_O_BINARY 0
94
95static int gdb_open_modeflags[12] = {
96 GDB_O_RDONLY,
97 GDB_O_RDONLY | GDB_O_BINARY,
98 GDB_O_RDWR,
99 GDB_O_RDWR | GDB_O_BINARY,
100 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
101 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
102 GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
103 GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
104 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
105 GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
106 GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
107 GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
108};
109
110static int open_modeflags[12] = {
a4f81979
FB
111 O_RDONLY,
112 O_RDONLY | O_BINARY,
113 O_RDWR,
114 O_RDWR | O_BINARY,
115 O_WRONLY | O_CREAT | O_TRUNC,
116 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
117 O_RDWR | O_CREAT | O_TRUNC,
118 O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
119 O_WRONLY | O_CREAT | O_APPEND,
120 O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
121 O_RDWR | O_CREAT | O_APPEND,
122 O_RDWR | O_CREAT | O_APPEND | O_BINARY
123};
124
35e9a0a8
PM
125typedef enum GuestFDType {
126 GuestFDUnused = 0,
127 GuestFDHost = 1,
263eb621 128 GuestFDGDB = 2,
c46a653c 129 GuestFDFeatureFile = 3,
35e9a0a8
PM
130} GuestFDType;
131
132/*
133 * Guest file descriptors are integer indexes into an array of
134 * these structures (we will dynamically resize as necessary).
135 */
136typedef struct GuestFD {
137 GuestFDType type;
c46a653c
PM
138 union {
139 int hostfd;
140 target_ulong featurefile_offset;
141 };
35e9a0a8
PM
142} GuestFD;
143
144static GArray *guestfd_array;
145
095f8c02
KP
146#ifndef CONFIG_USER_ONLY
147#include "exec/address-spaces.h"
148/*
149 * Find the base of a RAM region containing the specified address
150 */
151static inline hwaddr
152common_semi_find_region_base(hwaddr addr)
153{
154 MemoryRegion *subregion;
155
156 /*
157 * Find the chunk of R/W memory containing the address. This is
158 * used for the SYS_HEAPINFO semihosting call, which should
159 * probably be using information from the loaded application.
160 */
161 QTAILQ_FOREACH(subregion, &get_system_memory()->subregions,
162 subregions_link) {
163 if (subregion->ram && !subregion->readonly) {
164 Int128 top128 = int128_add(int128_make64(subregion->addr),
165 subregion->size);
166 Int128 addr128 = int128_make64(addr);
167 if (subregion->addr <= addr && int128_lt(addr128, top128)) {
168 return subregion->addr;
169 }
170 }
171 }
172 return 0;
173}
174#endif
175
3c37cfe0
KP
176#ifdef TARGET_ARM
177static inline target_ulong
178common_semi_arg(CPUState *cs, int argno)
179{
180 ARMCPU *cpu = ARM_CPU(cs);
181 CPUARMState *env = &cpu->env;
182 if (is_a64(env)) {
183 return env->xregs[argno];
184 } else {
185 return env->regs[argno];
186 }
187}
188
189static inline void
190common_semi_set_ret(CPUState *cs, target_ulong ret)
191{
192 ARMCPU *cpu = ARM_CPU(cs);
193 CPUARMState *env = &cpu->env;
194 if (is_a64(env)) {
195 env->xregs[0] = ret;
196 } else {
197 env->regs[0] = ret;
198 }
199}
200
201static inline bool
202common_semi_sys_exit_extended(CPUState *cs, int nr)
203{
204 return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr));
205}
206
207#ifndef CONFIG_USER_ONLY
208#include "hw/arm/boot.h"
209static inline target_ulong
210common_semi_rambase(CPUState *cs)
211{
212 CPUArchState *env = cs->env_ptr;
213 const struct arm_boot_info *info = env->boot_info;
095f8c02
KP
214 target_ulong sp;
215
216 if (info) {
217 return info->loader_start;
218 }
219
220 if (is_a64(env)) {
221 sp = env->xregs[31];
222 } else {
223 sp = env->regs[13];
224 }
225 return common_semi_find_region_base(sp);
3c37cfe0
KP
226}
227#endif
228
229#endif /* TARGET_ARM */
230
a10b9d93
KP
231#ifdef TARGET_RISCV
232static inline target_ulong
233common_semi_arg(CPUState *cs, int argno)
234{
235 RISCVCPU *cpu = RISCV_CPU(cs);
236 CPURISCVState *env = &cpu->env;
237 return env->gpr[xA0 + argno];
238}
239
240static inline void
241common_semi_set_ret(CPUState *cs, target_ulong ret)
242{
243 RISCVCPU *cpu = RISCV_CPU(cs);
244 CPURISCVState *env = &cpu->env;
245 env->gpr[xA0] = ret;
246}
247
248static inline bool
249common_semi_sys_exit_extended(CPUState *cs, int nr)
250{
251 return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8);
252}
253
254#ifndef CONFIG_USER_ONLY
255
256static inline target_ulong
257common_semi_rambase(CPUState *cs)
258{
259 RISCVCPU *cpu = RISCV_CPU(cs);
260 CPURISCVState *env = &cpu->env;
261 return common_semi_find_region_base(env->gpr[xSP]);
262}
263#endif
264
265#endif
266
35e9a0a8
PM
267/*
268 * Allocate a new guest file descriptor and return it; if we
269 * couldn't allocate a new fd then return -1.
270 * This is a fairly simplistic implementation because we don't
271 * expect that most semihosting guest programs will make very
272 * heavy use of opening and closing fds.
273 */
274static int alloc_guestfd(void)
275{
276 guint i;
277
278 if (!guestfd_array) {
279 /* New entries zero-initialized, i.e. type GuestFDUnused */
280 guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
281 }
282
21bf9b06
MY
283 /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */
284 for (i = 1; i < guestfd_array->len; i++) {
35e9a0a8
PM
285 GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i);
286
287 if (gf->type == GuestFDUnused) {
288 return i;
289 }
290 }
291
292 /* All elements already in use: expand the array */
293 g_array_set_size(guestfd_array, i + 1);
294 return i;
295}
296
297/*
298 * Look up the guestfd in the data structure; return NULL
299 * for out of bounds, but don't check whether the slot is unused.
300 * This is used internally by the other guestfd functions.
301 */
302static GuestFD *do_get_guestfd(int guestfd)
303{
304 if (!guestfd_array) {
305 return NULL;
306 }
307
21bf9b06 308 if (guestfd <= 0 || guestfd >= guestfd_array->len) {
35e9a0a8
PM
309 return NULL;
310 }
311
312 return &g_array_index(guestfd_array, GuestFD, guestfd);
313}
314
315/*
316 * Associate the specified guest fd (which must have been
317 * allocated via alloc_fd() and not previously used) with
263eb621 318 * the specified host/gdb fd.
35e9a0a8
PM
319 */
320static void associate_guestfd(int guestfd, int hostfd)
321{
322 GuestFD *gf = do_get_guestfd(guestfd);
323
324 assert(gf);
263eb621 325 gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost;
35e9a0a8
PM
326 gf->hostfd = hostfd;
327}
328
329/*
330 * Deallocate the specified guest file descriptor. This doesn't
331 * close the host fd, it merely undoes the work of alloc_fd().
332 */
333static void dealloc_guestfd(int guestfd)
334{
335 GuestFD *gf = do_get_guestfd(guestfd);
336
337 assert(gf);
338 gf->type = GuestFDUnused;
339}
340
341/*
342 * Given a guest file descriptor, get the associated struct.
343 * If the fd is not valid, return NULL. This is the function
344 * used by the various semihosting calls to validate a handle
345 * from the guest.
346 * Note: calling alloc_guestfd() or dealloc_guestfd() will
347 * invalidate any GuestFD* obtained by calling this function.
348 */
349static GuestFD *get_guestfd(int guestfd)
350{
351 GuestFD *gf = do_get_guestfd(guestfd);
352
353 if (!gf || gf->type == GuestFDUnused) {
354 return NULL;
355 }
356 return gf;
357}
358
6ed68455
PM
359/*
360 * The semihosting API has no concept of its errno being thread-safe,
361 * as the API design predates SMP CPUs and was intended as a simple
362 * real-hardware set of debug functionality. For QEMU, we make the
363 * errno be per-thread in linux-user mode; in softmmu it is a simple
364 * global, and we assume that the guest takes care of avoiding any races.
365 */
366#ifndef CONFIG_USER_ONLY
1b003821
PM
367static target_ulong syscall_err;
368
6ed68455
PM
369#include "exec/softmmu-semi.h"
370#endif
371
3c37cfe0 372static inline uint32_t set_swi_errno(CPUState *cs, uint32_t code)
8e71621f 373{
1b003821 374 if (code == (uint32_t)-1) {
6ed68455 375#ifdef CONFIG_USER_ONLY
6ed68455
PM
376 TaskState *ts = cs->opaque;
377
378 ts->swi_errno = errno;
379#else
1b003821 380 syscall_err = errno;
6ed68455 381#endif
1b003821 382 }
8e71621f
PB
383 return code;
384}
385
3c37cfe0 386static inline uint32_t get_swi_errno(CPUState *cs)
6ed68455
PM
387{
388#ifdef CONFIG_USER_ONLY
6ed68455
PM
389 TaskState *ts = cs->opaque;
390
391 return ts->swi_errno;
392#else
393 return syscall_err;
8e71621f 394#endif
6ed68455 395}
a4f81979 396
3c37cfe0 397static target_ulong common_semi_syscall_len;
a2d1ebaf 398
3c37cfe0 399static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
a2d1ebaf 400{
3c37cfe0 401 target_ulong reg0 = common_semi_arg(cs, 0);
33d9cc8a 402
a2d1ebaf 403 if (ret == (target_ulong)-1) {
939f5b43 404 errno = err;
3c37cfe0 405 set_swi_errno(cs, -1);
bb19cbc9 406 reg0 = ret;
a2d1ebaf
PB
407 } else {
408 /* Fixup syscalls that use nonstardard return conventions. */
bb19cbc9 409 switch (reg0) {
3881725c
SW
410 case TARGET_SYS_WRITE:
411 case TARGET_SYS_READ:
3c37cfe0 412 reg0 = common_semi_syscall_len - ret;
a2d1ebaf 413 break;
3881725c 414 case TARGET_SYS_SEEK:
bb19cbc9 415 reg0 = 0;
a2d1ebaf
PB
416 break;
417 default:
bb19cbc9 418 reg0 = ret;
a2d1ebaf
PB
419 break;
420 }
421 }
3c37cfe0 422 common_semi_set_ret(cs, reg0);
faacc041
PM
423}
424
3c37cfe0 425static target_ulong common_semi_flen_buf(CPUState *cs)
faacc041 426{
3c37cfe0
KP
427 target_ulong sp;
428#ifdef TARGET_ARM
faacc041
PM
429 /* Return an address in target memory of 64 bytes where the remote
430 * gdb should write its stat struct. (The format of this structure
431 * is defined by GDB's remote protocol and is not target-specific.)
432 * We put this on the guest's stack just below SP.
433 */
3c37cfe0 434 ARMCPU *cpu = ARM_CPU(cs);
faacc041 435 CPUARMState *env = &cpu->env;
faacc041
PM
436
437 if (is_a64(env)) {
438 sp = env->xregs[31];
439 } else {
440 sp = env->regs[13];
441 }
3c37cfe0 442#endif
a10b9d93
KP
443#ifdef TARGET_RISCV
444 RISCVCPU *cpu = RISCV_CPU(cs);
445 CPURISCVState *env = &cpu->env;
446
447 sp = env->gpr[xSP];
448#endif
faacc041
PM
449
450 return sp - 64;
a2d1ebaf
PB
451}
452
3c37cfe0
KP
453static void
454common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
33d9cc8a
PB
455{
456 /* The size is always stored in big-endian order, extract
457 the value. We assume the size always fit in 32 bits. */
458 uint32_t size;
3c37cfe0
KP
459 cpu_memory_rw_debug(cs, common_semi_flen_buf(cs) + 32,
460 (uint8_t *)&size, 4, 0);
faacc041 461 size = be32_to_cpu(size);
3c37cfe0 462 common_semi_set_ret(cs, size);
939f5b43 463 errno = err;
3c37cfe0 464 set_swi_errno(cs, -1);
33d9cc8a
PB
465}
466
3c37cfe0 467static int common_semi_open_guestfd;
35e9a0a8 468
3c37cfe0
KP
469static void
470common_semi_open_cb(CPUState *cs, target_ulong ret, target_ulong err)
35e9a0a8 471{
35e9a0a8 472 if (ret == (target_ulong)-1) {
939f5b43 473 errno = err;
3c37cfe0
KP
474 set_swi_errno(cs, -1);
475 dealloc_guestfd(common_semi_open_guestfd);
35e9a0a8 476 } else {
3c37cfe0
KP
477 associate_guestfd(common_semi_open_guestfd, ret);
478 ret = common_semi_open_guestfd;
35e9a0a8 479 }
3c37cfe0 480 common_semi_set_ret(cs, ret);
35e9a0a8
PM
481}
482
3c37cfe0
KP
483static target_ulong
484common_semi_gdb_syscall(CPUState *cs, gdb_syscall_complete_cb cb,
485 const char *fmt, ...)
bb19cbc9
PM
486{
487 va_list va;
bb19cbc9
PM
488
489 va_start(va, fmt);
490 gdb_do_syscallv(cb, fmt, va);
491 va_end(va);
492
f8ad2306
PM
493 /*
494 * FIXME: in softmmu mode, the gdbstub will schedule our callback
495 * to occur, but will not actually call it to complete the syscall
496 * until after this function has returned and we are back in the
497 * CPU main loop. Therefore callers to this function must not
498 * do anything with its return value, because it is not necessarily
499 * the result of the syscall, but could just be the old value of X0.
500 * The only thing safe to do with this is that the callers of
0bb446d8 501 * do_common_semihosting() will write it straight back into X0.
f8ad2306
PM
502 * (In linux-user mode, the callback will have happened before
503 * gdb_do_syscallv() returns.)
504 *
505 * We should tidy this up so neither this function nor
0bb446d8 506 * do_common_semihosting() return a value, so the mistake of
f8ad2306 507 * doing something with the return value is not possible to make.
bb19cbc9
PM
508 */
509
3c37cfe0 510 return common_semi_arg(cs, 0);
bb19cbc9
PM
511}
512
263eb621
PM
513/*
514 * Types for functions implementing various semihosting calls
515 * for specific types of guest file descriptor. These must all
516 * do the work and return the required return value for the guest,
517 * setting the guest errno if appropriate.
518 */
3c37cfe0
KP
519typedef uint32_t sys_closefn(CPUState *cs, GuestFD *gf);
520typedef uint32_t sys_writefn(CPUState *cs, GuestFD *gf,
52c8a163 521 target_ulong buf, uint32_t len);
3c37cfe0 522typedef uint32_t sys_readfn(CPUState *cs, GuestFD *gf,
2c3a09a6 523 target_ulong buf, uint32_t len);
3c37cfe0
KP
524typedef uint32_t sys_isattyfn(CPUState *cs, GuestFD *gf);
525typedef uint32_t sys_seekfn(CPUState *cs, GuestFD *gf,
45e88ffc 526 target_ulong offset);
3c37cfe0 527typedef uint32_t sys_flenfn(CPUState *cs, GuestFD *gf);
263eb621 528
3c37cfe0 529static uint32_t host_closefn(CPUState *cs, GuestFD *gf)
263eb621 530{
16ab12a9
PM
531 /*
532 * Only close the underlying host fd if it's one we opened on behalf
533 * of the guest in SYS_OPEN.
534 */
535 if (gf->hostfd == STDIN_FILENO ||
536 gf->hostfd == STDOUT_FILENO ||
537 gf->hostfd == STDERR_FILENO) {
538 return 0;
539 }
3c37cfe0 540 return set_swi_errno(cs, close(gf->hostfd));
263eb621
PM
541}
542
3c37cfe0 543static uint32_t host_writefn(CPUState *cs, GuestFD *gf,
52c8a163
PM
544 target_ulong buf, uint32_t len)
545{
3c37cfe0 546 CPUArchState *env = cs->env_ptr;
52c8a163 547 uint32_t ret;
52c8a163 548 char *s = lock_user(VERIFY_READ, buf, len, 1);
3c37cfe0 549 (void) env; /* Used in arm softmmu lock_user implicitly */
52c8a163
PM
550 if (!s) {
551 /* Return bytes not written on error */
552 return len;
553 }
3c37cfe0 554 ret = set_swi_errno(cs, write(gf->hostfd, s, len));
52c8a163
PM
555 unlock_user(s, buf, 0);
556 if (ret == (uint32_t)-1) {
557 ret = 0;
558 }
559 /* Return bytes not written */
560 return len - ret;
561}
562
3c37cfe0 563static uint32_t host_readfn(CPUState *cs, GuestFD *gf,
2c3a09a6
PM
564 target_ulong buf, uint32_t len)
565{
3c37cfe0 566 CPUArchState *env = cs->env_ptr;
2c3a09a6 567 uint32_t ret;
2c3a09a6 568 char *s = lock_user(VERIFY_WRITE, buf, len, 0);
3c37cfe0 569 (void) env; /* Used in arm softmmu lock_user implicitly */
2c3a09a6
PM
570 if (!s) {
571 /* return bytes not read */
572 return len;
573 }
574 do {
3c37cfe0 575 ret = set_swi_errno(cs, read(gf->hostfd, s, len));
2c3a09a6
PM
576 } while (ret == -1 && errno == EINTR);
577 unlock_user(s, buf, len);
578 if (ret == (uint32_t)-1) {
579 ret = 0;
580 }
581 /* Return bytes not read */
582 return len - ret;
583}
584
3c37cfe0 585static uint32_t host_isattyfn(CPUState *cs, GuestFD *gf)
0213fa45
PM
586{
587 return isatty(gf->hostfd);
588}
589
3c37cfe0 590static uint32_t host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset)
45e88ffc 591{
3c37cfe0 592 uint32_t ret = set_swi_errno(cs, lseek(gf->hostfd, offset, SEEK_SET));
45e88ffc
PM
593 if (ret == (uint32_t)-1) {
594 return -1;
595 }
596 return 0;
597}
598
3c37cfe0 599static uint32_t host_flenfn(CPUState *cs, GuestFD *gf)
1631a7be 600{
1631a7be 601 struct stat buf;
3c37cfe0 602 uint32_t ret = set_swi_errno(cs, fstat(gf->hostfd, &buf));
1631a7be
PM
603 if (ret == (uint32_t)-1) {
604 return -1;
605 }
606 return buf.st_size;
607}
608
3c37cfe0 609static uint32_t gdb_closefn(CPUState *cs, GuestFD *gf)
263eb621 610{
3c37cfe0 611 return common_semi_gdb_syscall(cs, common_semi_cb, "close,%x", gf->hostfd);
263eb621
PM
612}
613
3c37cfe0 614static uint32_t gdb_writefn(CPUState *cs, GuestFD *gf,
52c8a163
PM
615 target_ulong buf, uint32_t len)
616{
3c37cfe0
KP
617 common_semi_syscall_len = len;
618 return common_semi_gdb_syscall(cs, common_semi_cb, "write,%x,%x,%x",
619 gf->hostfd, buf, len);
52c8a163
PM
620}
621
3c37cfe0 622static uint32_t gdb_readfn(CPUState *cs, GuestFD *gf,
2c3a09a6
PM
623 target_ulong buf, uint32_t len)
624{
3c37cfe0
KP
625 common_semi_syscall_len = len;
626 return common_semi_gdb_syscall(cs, common_semi_cb, "read,%x,%x,%x",
627 gf->hostfd, buf, len);
2c3a09a6
PM
628}
629
3c37cfe0 630static uint32_t gdb_isattyfn(CPUState *cs, GuestFD *gf)
0213fa45 631{
3c37cfe0 632 return common_semi_gdb_syscall(cs, common_semi_cb, "isatty,%x", gf->hostfd);
0213fa45
PM
633}
634
3c37cfe0 635static uint32_t gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset)
45e88ffc 636{
3c37cfe0
KP
637 return common_semi_gdb_syscall(cs, common_semi_cb, "lseek,%x,%x,0",
638 gf->hostfd, offset);
45e88ffc
PM
639}
640
3c37cfe0 641static uint32_t gdb_flenfn(CPUState *cs, GuestFD *gf)
1631a7be 642{
3c37cfe0
KP
643 return common_semi_gdb_syscall(cs, common_semi_flen_cb, "fstat,%x,%x",
644 gf->hostfd, common_semi_flen_buf(cs));
1631a7be
PM
645}
646
c46a653c
PM
647#define SHFB_MAGIC_0 0x53
648#define SHFB_MAGIC_1 0x48
649#define SHFB_MAGIC_2 0x46
650#define SHFB_MAGIC_3 0x42
651
22a43bb9
PM
652/* Feature bits reportable in feature byte 0 */
653#define SH_EXT_EXIT_EXTENDED (1 << 0)
6ee18643 654#define SH_EXT_STDOUT_STDERR (1 << 1)
22a43bb9 655
c46a653c
PM
656static const uint8_t featurefile_data[] = {
657 SHFB_MAGIC_0,
658 SHFB_MAGIC_1,
659 SHFB_MAGIC_2,
660 SHFB_MAGIC_3,
6ee18643 661 SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */
c46a653c
PM
662};
663
664static void init_featurefile_guestfd(int guestfd)
665{
666 GuestFD *gf = do_get_guestfd(guestfd);
667
668 assert(gf);
669 gf->type = GuestFDFeatureFile;
670 gf->featurefile_offset = 0;
671}
672
3c37cfe0 673static uint32_t featurefile_closefn(CPUState *cs, GuestFD *gf)
c46a653c
PM
674{
675 /* Nothing to do */
676 return 0;
677}
678
3c37cfe0 679static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf,
c46a653c
PM
680 target_ulong buf, uint32_t len)
681{
682 /* This fd can never be open for writing */
c46a653c
PM
683
684 errno = EBADF;
3c37cfe0 685 return set_swi_errno(cs, -1);
c46a653c
PM
686}
687
3c37cfe0 688static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf,
c46a653c
PM
689 target_ulong buf, uint32_t len)
690{
3c37cfe0 691 CPUArchState *env = cs->env_ptr;
c46a653c 692 uint32_t i;
c46a653c
PM
693 char *s;
694
3c37cfe0 695 (void) env; /* Used in arm softmmu lock_user implicitly */
c46a653c
PM
696 s = lock_user(VERIFY_WRITE, buf, len, 0);
697 if (!s) {
698 return len;
699 }
700
701 for (i = 0; i < len; i++) {
702 if (gf->featurefile_offset >= sizeof(featurefile_data)) {
703 break;
704 }
705 s[i] = featurefile_data[gf->featurefile_offset];
706 gf->featurefile_offset++;
707 }
708
709 unlock_user(s, buf, len);
710
711 /* Return number of bytes not read */
712 return len - i;
713}
714
3c37cfe0 715static uint32_t featurefile_isattyfn(CPUState *cs, GuestFD *gf)
c46a653c
PM
716{
717 return 0;
718}
719
3c37cfe0 720static uint32_t featurefile_seekfn(CPUState *cs, GuestFD *gf,
c46a653c
PM
721 target_ulong offset)
722{
723 gf->featurefile_offset = offset;
724 return 0;
725}
726
3c37cfe0 727static uint32_t featurefile_flenfn(CPUState *cs, GuestFD *gf)
c46a653c
PM
728{
729 return sizeof(featurefile_data);
730}
731
263eb621
PM
732typedef struct GuestFDFunctions {
733 sys_closefn *closefn;
52c8a163 734 sys_writefn *writefn;
2c3a09a6 735 sys_readfn *readfn;
0213fa45 736 sys_isattyfn *isattyfn;
45e88ffc 737 sys_seekfn *seekfn;
1631a7be 738 sys_flenfn *flenfn;
263eb621
PM
739} GuestFDFunctions;
740
741static const GuestFDFunctions guestfd_fns[] = {
742 [GuestFDHost] = {
743 .closefn = host_closefn,
52c8a163 744 .writefn = host_writefn,
2c3a09a6 745 .readfn = host_readfn,
0213fa45 746 .isattyfn = host_isattyfn,
45e88ffc 747 .seekfn = host_seekfn,
1631a7be 748 .flenfn = host_flenfn,
263eb621
PM
749 },
750 [GuestFDGDB] = {
751 .closefn = gdb_closefn,
52c8a163 752 .writefn = gdb_writefn,
2c3a09a6 753 .readfn = gdb_readfn,
0213fa45 754 .isattyfn = gdb_isattyfn,
45e88ffc 755 .seekfn = gdb_seekfn,
1631a7be 756 .flenfn = gdb_flenfn,
263eb621 757 },
c46a653c
PM
758 [GuestFDFeatureFile] = {
759 .closefn = featurefile_closefn,
760 .writefn = featurefile_writefn,
761 .readfn = featurefile_readfn,
762 .isattyfn = featurefile_isattyfn,
763 .seekfn = featurefile_seekfn,
764 .flenfn = featurefile_flenfn,
765 },
263eb621
PM
766};
767
3960ca5b
AB
768/*
769 * Read the input value from the argument block; fail the semihosting
770 * call if the memory read fails. Eventually we could use a generic
771 * CPUState helper function here.
f296c0d1 772 */
3960ca5b
AB
773static inline bool is_64bit_semihosting(CPUArchState *env)
774{
775#if defined(TARGET_ARM)
776 return is_a64(env);
777#elif defined(TARGET_RISCV)
db23e5d9 778 return riscv_cpu_mxl(env) != MXL_RV32;
3960ca5b
AB
779#else
780#error un-handled architecture
781#endif
782}
783
784
f296c0d1 785#define GET_ARG(n) do { \
3960ca5b 786 if (is_64bit_semihosting(env)) { \
faacc041 787 if (get_user_u64(arg ## n, args + (n) * 8)) { \
f7d38cf2 788 errno = EFAULT; \
3960ca5b 789 return set_swi_errno(cs, -1); \
faacc041
PM
790 } \
791 } else { \
792 if (get_user_u32(arg ## n, args + (n) * 4)) { \
f7d38cf2 793 errno = EFAULT; \
3c37cfe0 794 return set_swi_errno(cs, -1); \
faacc041 795 } \
f296c0d1
PM
796 } \
797} while (0)
798
faacc041 799#define SET_ARG(n, val) \
3960ca5b 800 (is_64bit_semihosting(env) ? \
faacc041
PM
801 put_user_u64(val, args + (n) * 8) : \
802 put_user_u32(val, args + (n) * 4))
803
a10b9d93 804
4cb28db9
AB
805/*
806 * Do a semihosting call.
807 *
808 * The specification always says that the "return register" either
809 * returns a specific value or is corrupted, so we don't need to
810 * report to our caller whether we are returning a value or trying to
811 * leave the register unchanged. We use 0xdeadbeef as the return value
812 * when there isn't a defined return value for the call.
813 */
0bb446d8 814target_ulong do_common_semihosting(CPUState *cs)
a4f81979 815{
3c37cfe0 816 CPUArchState *env = cs->env_ptr;
53a5960a 817 target_ulong args;
f296c0d1 818 target_ulong arg0, arg1, arg2, arg3;
27e3b109 819 target_ulong ul_ret;
a4f81979
FB
820 char * s;
821 int nr;
822 uint32_t ret;
8e71621f 823 uint32_t len;
35e9a0a8 824 GuestFD *gf;
4d834039 825 int64_t elapsed;
a4f81979 826
3c37cfe0
KP
827 (void) env; /* Used implicitly by arm lock_user macro */
828 nr = common_semi_arg(cs, 0) & 0xffffffffU;
829 args = common_semi_arg(cs, 1);
faacc041 830
a4f81979 831 switch (nr) {
3881725c 832 case TARGET_SYS_OPEN:
35e9a0a8
PM
833 {
834 int guestfd;
835
f296c0d1
PM
836 GET_ARG(0);
837 GET_ARG(1);
838 GET_ARG(2);
839 s = lock_user_string(arg0);
840 if (!s) {
f7d38cf2 841 errno = EFAULT;
3c37cfe0 842 return set_swi_errno(cs, -1);
f296c0d1
PM
843 }
844 if (arg1 >= 12) {
845 unlock_user(s, arg0, 0);
f7d38cf2 846 errno = EINVAL;
3c37cfe0 847 return set_swi_errno(cs, -1);
396bef4b 848 }
35e9a0a8
PM
849
850 guestfd = alloc_guestfd();
851 if (guestfd < 0) {
852 unlock_user(s, arg0, 0);
853 errno = EMFILE;
3c37cfe0 854 return set_swi_errno(cs, -1);
35e9a0a8
PM
855 }
856
a4f81979 857 if (strcmp(s, ":tt") == 0) {
6ee18643
PM
858 int result_fileno;
859
860 /*
861 * We implement SH_EXT_STDOUT_STDERR, so:
862 * open for read == stdin
863 * open for write == stdout
864 * open for append == stderr
865 */
866 if (arg1 < 4) {
867 result_fileno = STDIN_FILENO;
868 } else if (arg1 < 8) {
869 result_fileno = STDOUT_FILENO;
870 } else {
871 result_fileno = STDERR_FILENO;
872 }
35e9a0a8 873 associate_guestfd(guestfd, result_fileno);
f296c0d1 874 unlock_user(s, arg0, 0);
35e9a0a8 875 return guestfd;
a4f81979 876 }
c46a653c
PM
877 if (strcmp(s, ":semihosting-features") == 0) {
878 unlock_user(s, arg0, 0);
879 /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */
880 if (arg1 != 0 && arg1 != 1) {
881 dealloc_guestfd(guestfd);
882 errno = EACCES;
3c37cfe0 883 return set_swi_errno(cs, -1);
c46a653c
PM
884 }
885 init_featurefile_guestfd(guestfd);
886 return guestfd;
887 }
888
a2d1ebaf 889 if (use_gdb_syscalls()) {
3c37cfe0
KP
890 common_semi_open_guestfd = guestfd;
891 ret = common_semi_gdb_syscall(cs, common_semi_open_cb,
892 "open,%s,%x,1a4", arg0, (int)arg2 + 1,
893 gdb_open_modeflags[arg1]);
a2d1ebaf 894 } else {
3c37cfe0 895 ret = set_swi_errno(cs, open(s, open_modeflags[arg1], 0644));
35e9a0a8
PM
896 if (ret == (uint32_t)-1) {
897 dealloc_guestfd(guestfd);
898 } else {
899 associate_guestfd(guestfd, ret);
900 ret = guestfd;
901 }
a2d1ebaf 902 }
f296c0d1 903 unlock_user(s, arg0, 0);
8e71621f 904 return ret;
35e9a0a8 905 }
3881725c 906 case TARGET_SYS_CLOSE:
f296c0d1 907 GET_ARG(0);
35e9a0a8
PM
908
909 gf = get_guestfd(arg0);
910 if (!gf) {
911 errno = EBADF;
3c37cfe0 912 return set_swi_errno(cs, -1);
35e9a0a8
PM
913 }
914
3c37cfe0 915 ret = guestfd_fns[gf->type].closefn(cs, gf);
35e9a0a8
PM
916 dealloc_guestfd(arg0);
917 return ret;
3881725c 918 case TARGET_SYS_WRITEC:
3c37cfe0 919 qemu_semihosting_console_outc(cs->env_ptr, args);
0dc07721 920 return 0xdeadbeef;
3881725c 921 case TARGET_SYS_WRITE0:
3c37cfe0 922 return qemu_semihosting_console_outs(cs->env_ptr, args);
3881725c 923 case TARGET_SYS_WRITE:
f296c0d1
PM
924 GET_ARG(0);
925 GET_ARG(1);
926 GET_ARG(2);
927 len = arg2;
35e9a0a8
PM
928
929 gf = get_guestfd(arg0);
930 if (!gf) {
931 errno = EBADF;
3c37cfe0 932 return set_swi_errno(cs, -1);
35e9a0a8
PM
933 }
934
3c37cfe0 935 return guestfd_fns[gf->type].writefn(cs, gf, arg1, len);
3881725c 936 case TARGET_SYS_READ:
f296c0d1
PM
937 GET_ARG(0);
938 GET_ARG(1);
939 GET_ARG(2);
940 len = arg2;
35e9a0a8
PM
941
942 gf = get_guestfd(arg0);
943 if (!gf) {
944 errno = EBADF;
3c37cfe0 945 return set_swi_errno(cs, -1);
35e9a0a8
PM
946 }
947
3c37cfe0 948 return guestfd_fns[gf->type].readfn(cs, gf, arg1, len);
3881725c 949 case TARGET_SYS_READC:
3c37cfe0 950 return qemu_semihosting_console_inc(cs->env_ptr);
767ba049
KP
951 case TARGET_SYS_ISERROR:
952 GET_ARG(0);
953 return (target_long) arg0 < 0 ? 1 : 0;
3881725c 954 case TARGET_SYS_ISTTY:
f296c0d1 955 GET_ARG(0);
35e9a0a8
PM
956
957 gf = get_guestfd(arg0);
958 if (!gf) {
959 errno = EBADF;
3c37cfe0 960 return set_swi_errno(cs, -1);
35e9a0a8
PM
961 }
962
3c37cfe0 963 return guestfd_fns[gf->type].isattyfn(cs, gf);
3881725c 964 case TARGET_SYS_SEEK:
f296c0d1
PM
965 GET_ARG(0);
966 GET_ARG(1);
35e9a0a8
PM
967
968 gf = get_guestfd(arg0);
969 if (!gf) {
970 errno = EBADF;
3c37cfe0 971 return set_swi_errno(cs, -1);
35e9a0a8
PM
972 }
973
3c37cfe0 974 return guestfd_fns[gf->type].seekfn(cs, gf, arg1);
3881725c 975 case TARGET_SYS_FLEN:
f296c0d1 976 GET_ARG(0);
35e9a0a8
PM
977
978 gf = get_guestfd(arg0);
979 if (!gf) {
980 errno = EBADF;
3c37cfe0 981 return set_swi_errno(cs, -1);
35e9a0a8
PM
982 }
983
3c37cfe0 984 return guestfd_fns[gf->type].flenfn(cs, gf);
3881725c 985 case TARGET_SYS_TMPNAM:
27e3b109
KP
986 GET_ARG(0);
987 GET_ARG(1);
988 GET_ARG(2);
989 if (asprintf(&s, "/tmp/qemu-%x%02x", getpid(),
990 (int) (arg1 & 0xff)) < 0) {
991 return -1;
992 }
993 ul_ret = (target_ulong) -1;
994
995 /* Make sure there's enough space in the buffer */
996 if (strlen(s) < arg2) {
997 char *output = lock_user(VERIFY_WRITE, arg0, arg2, 0);
998 strcpy(output, s);
999 unlock_user(output, arg0, arg2);
1000 ul_ret = 0;
1001 }
1002 free(s);
1003 return ul_ret;
3881725c 1004 case TARGET_SYS_REMOVE:
f296c0d1
PM
1005 GET_ARG(0);
1006 GET_ARG(1);
a2d1ebaf 1007 if (use_gdb_syscalls()) {
3c37cfe0
KP
1008 ret = common_semi_gdb_syscall(cs, common_semi_cb, "unlink,%s",
1009 arg0, (int)arg1 + 1);
a2d1ebaf 1010 } else {
f296c0d1
PM
1011 s = lock_user_string(arg0);
1012 if (!s) {
f7d38cf2 1013 errno = EFAULT;
3c37cfe0 1014 return set_swi_errno(cs, -1);
f296c0d1 1015 }
3c37cfe0 1016 ret = set_swi_errno(cs, remove(s));
f296c0d1 1017 unlock_user(s, arg0, 0);
a2d1ebaf 1018 }
8e71621f 1019 return ret;
3881725c 1020 case TARGET_SYS_RENAME:
f296c0d1
PM
1021 GET_ARG(0);
1022 GET_ARG(1);
1023 GET_ARG(2);
1024 GET_ARG(3);
a2d1ebaf 1025 if (use_gdb_syscalls()) {
3c37cfe0
KP
1026 return common_semi_gdb_syscall(cs, common_semi_cb, "rename,%s,%s",
1027 arg0, (int)arg1 + 1, arg2,
1028 (int)arg3 + 1);
a2d1ebaf 1029 } else {
8e71621f 1030 char *s2;
f296c0d1
PM
1031 s = lock_user_string(arg0);
1032 s2 = lock_user_string(arg2);
f7d38cf2
PM
1033 if (!s || !s2) {
1034 errno = EFAULT;
3c37cfe0 1035 ret = set_swi_errno(cs, -1);
f7d38cf2 1036 } else {
3c37cfe0 1037 ret = set_swi_errno(cs, rename(s, s2));
f7d38cf2 1038 }
579a97f7 1039 if (s2)
f296c0d1 1040 unlock_user(s2, arg2, 0);
579a97f7 1041 if (s)
f296c0d1 1042 unlock_user(s, arg0, 0);
8e71621f
PB
1043 return ret;
1044 }
3881725c 1045 case TARGET_SYS_CLOCK:
a4f81979 1046 return clock() / (CLOCKS_PER_SEC / 100);
3881725c 1047 case TARGET_SYS_TIME:
3c37cfe0 1048 return set_swi_errno(cs, time(NULL));
3881725c 1049 case TARGET_SYS_SYSTEM:
f296c0d1
PM
1050 GET_ARG(0);
1051 GET_ARG(1);
a2d1ebaf 1052 if (use_gdb_syscalls()) {
3c37cfe0
KP
1053 return common_semi_gdb_syscall(cs, common_semi_cb, "system,%s",
1054 arg0, (int)arg1 + 1);
a2d1ebaf 1055 } else {
f296c0d1
PM
1056 s = lock_user_string(arg0);
1057 if (!s) {
f7d38cf2 1058 errno = EFAULT;
3c37cfe0 1059 return set_swi_errno(cs, -1);
f296c0d1 1060 }
3c37cfe0 1061 ret = set_swi_errno(cs, system(s));
f296c0d1 1062 unlock_user(s, arg0, 0);
a982b531 1063 return ret;
a2d1ebaf 1064 }
3881725c 1065 case TARGET_SYS_ERRNO:
3c37cfe0 1066 return get_swi_errno(cs);
3881725c 1067 case TARGET_SYS_GET_CMDLINE:
38d0662a 1068 {
1c1b40c1
CV
1069 /* Build a command-line from the original argv.
1070 *
1071 * The inputs are:
f296c0d1
PM
1072 * * arg0, pointer to a buffer of at least the size
1073 * specified in arg1.
1074 * * arg1, size of the buffer pointed to by arg0 in
1c1b40c1
CV
1075 * bytes.
1076 *
1077 * The outputs are:
f296c0d1 1078 * * arg0, pointer to null-terminated string of the
1c1b40c1 1079 * command line.
f296c0d1 1080 * * arg1, length of the string pointed to by arg0.
1c1b40c1 1081 */
579a97f7 1082
1c1b40c1 1083 char *output_buffer;
f296c0d1 1084 size_t input_size;
1c1b40c1
CV
1085 size_t output_size;
1086 int status = 0;
f3c2bda2
LI
1087#if !defined(CONFIG_USER_ONLY)
1088 const char *cmdline;
6ed68455
PM
1089#else
1090 TaskState *ts = cs->opaque;
f3c2bda2 1091#endif
f296c0d1
PM
1092 GET_ARG(0);
1093 GET_ARG(1);
1094 input_size = arg1;
1c1b40c1
CV
1095 /* Compute the size of the output string. */
1096#if !defined(CONFIG_USER_ONLY)
f3c2bda2
LI
1097 cmdline = semihosting_get_cmdline();
1098 if (cmdline == NULL) {
1099 cmdline = ""; /* Default to an empty line. */
1100 }
1101 output_size = strlen(cmdline) + 1; /* Count terminating 0. */
1c1b40c1
CV
1102#else
1103 unsigned int i;
38d0662a 1104
1c1b40c1
CV
1105 output_size = ts->info->arg_end - ts->info->arg_start;
1106 if (!output_size) {
4cb28db9
AB
1107 /*
1108 * We special-case the "empty command line" case (argc==0).
1109 * Just provide the terminating 0.
1110 */
1c1b40c1
CV
1111 output_size = 1;
1112 }
1113#endif
38d0662a 1114
1c1b40c1 1115 if (output_size > input_size) {
4cb28db9 1116 /* Not enough space to store command-line arguments. */
f7d38cf2 1117 errno = E2BIG;
3c37cfe0 1118 return set_swi_errno(cs, -1);
38d0662a 1119 }
38d0662a 1120
1c1b40c1 1121 /* Adjust the command-line length. */
f296c0d1
PM
1122 if (SET_ARG(1, output_size - 1)) {
1123 /* Couldn't write back to argument block */
f7d38cf2 1124 errno = EFAULT;
3c37cfe0 1125 return set_swi_errno(cs, -1);
f296c0d1 1126 }
38d0662a 1127
1c1b40c1 1128 /* Lock the buffer on the ARM side. */
f296c0d1 1129 output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
1c1b40c1 1130 if (!output_buffer) {
f7d38cf2 1131 errno = EFAULT;
3c37cfe0 1132 return set_swi_errno(cs, -1);
1c1b40c1 1133 }
38d0662a 1134
1c1b40c1
CV
1135 /* Copy the command-line arguments. */
1136#if !defined(CONFIG_USER_ONLY)
f3c2bda2 1137 pstrcpy(output_buffer, output_size, cmdline);
1c1b40c1
CV
1138#else
1139 if (output_size == 1) {
1140 /* Empty command-line. */
1141 output_buffer[0] = '\0';
1142 goto out;
1143 }
2e8785ac 1144
1c1b40c1
CV
1145 if (copy_from_user(output_buffer, ts->info->arg_start,
1146 output_size)) {
f7d38cf2 1147 errno = EFAULT;
3c37cfe0 1148 status = set_swi_errno(cs, -1);
1c1b40c1 1149 goto out;
2e8785ac
WS
1150 }
1151
1c1b40c1
CV
1152 /* Separate arguments by white spaces. */
1153 for (i = 0; i < output_size - 1; i++) {
1154 if (output_buffer[i] == 0) {
1155 output_buffer[i] = ' ';
1156 }
1157 }
1158 out:
1159#endif
1160 /* Unlock the buffer on the ARM side. */
f296c0d1 1161 unlock_user(output_buffer, arg0, output_size);
2e8785ac 1162
1c1b40c1 1163 return status;
38d0662a 1164 }
3881725c 1165 case TARGET_SYS_HEAPINFO:
a4f81979 1166 {
f5666418 1167 target_ulong retvals[4];
90e26f5a 1168 target_ulong limit;
f5666418 1169 int i;
6ed68455
PM
1170#ifdef CONFIG_USER_ONLY
1171 TaskState *ts = cs->opaque;
69515951 1172#else
3c37cfe0 1173 target_ulong rambase = common_semi_rambase(cs);
6ed68455 1174#endif
f5666418 1175
f296c0d1 1176 GET_ARG(0);
a4f81979 1177
8e71621f 1178#ifdef CONFIG_USER_ONLY
4cb28db9
AB
1179 /*
1180 * Some C libraries assume the heap immediately follows .bss, so
1181 * allocate it using sbrk.
1182 */
a4f81979 1183 if (!ts->heap_limit) {
206ae74a 1184 abi_ulong ret;
a4f81979 1185
53a5960a 1186 ts->heap_base = do_brk(0);
3c37cfe0 1187 limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE;
a4f81979
FB
1188 /* Try a big heap, and reduce the size if that fails. */
1189 for (;;) {
53a5960a 1190 ret = do_brk(limit);
206ae74a 1191 if (ret >= limit) {
a4f81979 1192 break;
206ae74a 1193 }
a4f81979
FB
1194 limit = (ts->heap_base >> 1) + (limit >> 1);
1195 }
1196 ts->heap_limit = limit;
1197 }
3b46e624 1198
f5666418
PM
1199 retvals[0] = ts->heap_base;
1200 retvals[1] = ts->heap_limit;
1201 retvals[2] = ts->stack_base;
1202 retvals[3] = 0; /* Stack limit. */
8e71621f 1203#else
6e504a98 1204 limit = current_machine->ram_size;
8e71621f 1205 /* TODO: Make this use the limit of the loaded application. */
69515951
PM
1206 retvals[0] = rambase + limit / 2;
1207 retvals[1] = rambase + limit;
1208 retvals[2] = rambase + limit; /* Stack base */
1209 retvals[3] = rambase; /* Stack limit. */
8e71621f 1210#endif
f5666418
PM
1211
1212 for (i = 0; i < ARRAY_SIZE(retvals); i++) {
1213 bool fail;
1214
35e3f029
AB
1215 if (is_64bit_semihosting(env)) {
1216 fail = put_user_u64(retvals[i], arg0 + i * 8);
1217 } else {
1218 fail = put_user_u32(retvals[i], arg0 + i * 4);
1219 }
f5666418
PM
1220
1221 if (fail) {
1222 /* Couldn't write back to argument block */
f7d38cf2 1223 errno = EFAULT;
3c37cfe0 1224 return set_swi_errno(cs, -1);
f5666418
PM
1225 }
1226 }
a4f81979
FB
1227 return 0;
1228 }
3881725c 1229 case TARGET_SYS_EXIT:
22a43bb9 1230 case TARGET_SYS_EXIT_EXTENDED:
3c37cfe0 1231 if (common_semi_sys_exit_extended(cs, nr)) {
4cb28db9 1232 /*
22a43bb9 1233 * The A64 version of SYS_EXIT takes a parameter block,
7446d35e
PM
1234 * so the application-exit type can return a subcode which
1235 * is the exit status code from the application.
22a43bb9
PM
1236 * SYS_EXIT_EXTENDED is an a new-in-v2.0 optional function
1237 * which allows A32/T32 guests to also provide a status code.
7446d35e
PM
1238 */
1239 GET_ARG(0);
1240 GET_ARG(1);
1241
1242 if (arg0 == ADP_Stopped_ApplicationExit) {
1243 ret = arg1;
1244 } else {
1245 ret = 1;
1246 }
1247 } else {
4cb28db9 1248 /*
22a43bb9
PM
1249 * The A32/T32 version of SYS_EXIT specifies only
1250 * Stopped_ApplicationExit as normal exit, but does not
1251 * allow the guest to specify the exit status code.
1252 * Everything else is considered an error.
4cb28db9 1253 */
7446d35e
PM
1254 ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
1255 }
ad9dcb20 1256 gdb_exit(ret);
1ecc3a2d 1257 exit(ret);
4d834039
KP
1258 case TARGET_SYS_ELAPSED:
1259 elapsed = get_clock() - clock_start;
1260 if (sizeof(target_ulong) == 8) {
1261 SET_ARG(0, elapsed);
1262 } else {
1263 SET_ARG(0, (uint32_t) elapsed);
1264 SET_ARG(1, (uint32_t) (elapsed >> 32));
1265 }
1266 return 0;
1267 case TARGET_SYS_TICKFREQ:
1268 /* qemu always uses nsec */
1269 return 1000000000;
e9ebfbfc 1270 case TARGET_SYS_SYNCCACHE:
4cb28db9
AB
1271 /*
1272 * Clean the D-cache and invalidate the I-cache for the specified
e9ebfbfc
PM
1273 * virtual address range. This is a nop for us since we don't
1274 * implement caches. This is only present on A64.
1275 */
3c37cfe0
KP
1276#ifdef TARGET_ARM
1277 if (is_a64(cs->env_ptr)) {
e9ebfbfc
PM
1278 return 0;
1279 }
a10b9d93
KP
1280#endif
1281#ifdef TARGET_RISCV
1282 return 0;
3c37cfe0 1283#endif
e9ebfbfc 1284 /* fall through -- invalid for A32/T32 */
a4f81979
FB
1285 default:
1286 fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
90c84c56 1287 cpu_dump_state(cs, stderr, 0);
a4f81979
FB
1288 abort();
1289 }
1290}