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