]>
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" | |
6b5fe137 PMD |
37 | #include "semihosting/semihost.h" |
38 | #include "semihosting/console.h" | |
39 | #include "semihosting/common-semi.h" | |
1c6ff720 | 40 | #include "semihosting/guestfd.h" |
5b3f39cb | 41 | #include "semihosting/syscalls.h" |
1c6ff720 | 42 | |
8e71621f | 43 | #ifdef CONFIG_USER_ONLY |
a4f81979 FB |
44 | #include "qemu.h" |
45 | ||
3c37cfe0 | 46 | #define COMMON_SEMI_HEAP_SIZE (128 * 1024 * 1024) |
8e71621f | 47 | #else |
f348b6d1 | 48 | #include "qemu/cutils.h" |
5fc983af | 49 | #include "hw/loader.h" |
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 | static int gdb_open_modeflags[12] = { |
88 | GDB_O_RDONLY, | |
a1a2a3e6 RH |
89 | GDB_O_RDONLY, |
90 | GDB_O_RDWR, | |
a2d1ebaf | 91 | GDB_O_RDWR, |
a2d1ebaf | 92 | GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, |
a1a2a3e6 | 93 | GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC, |
a2d1ebaf | 94 | GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, |
a1a2a3e6 RH |
95 | GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC, |
96 | GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, | |
a2d1ebaf | 97 | GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND, |
a2d1ebaf | 98 | GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, |
a1a2a3e6 | 99 | GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND, |
a2d1ebaf PB |
100 | }; |
101 | ||
095f8c02 | 102 | #ifndef CONFIG_USER_ONLY |
5fc983af AB |
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. | |
095f8c02 | 114 | */ |
5fc983af AB |
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) | |
095f8c02 | 125 | { |
5fc983af AB |
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); | |
095f8c02 KP |
151 | |
152 | /* | |
5fc983af AB |
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. | |
095f8c02 | 156 | */ |
5fc983af AB |
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; | |
095f8c02 | 161 | } |
5fc983af AB |
162 | |
163 | return info; | |
095f8c02 | 164 | } |
5fc983af | 165 | |
095f8c02 KP |
166 | #endif |
167 | ||
1b3b7693 | 168 | #include "common-semi-target.h" |
a10b9d93 | 169 | |
3753b00e RH |
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. | |
fed49cdf PM |
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. | |
3753b00e RH |
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 | ||
6ed68455 PM |
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 | |
1b003821 PM |
208 | static target_ulong syscall_err; |
209 | ||
c89a14ad | 210 | #include "semihosting/softmmu-uaccess.h" |
6ed68455 PM |
211 | #endif |
212 | ||
3c37cfe0 | 213 | static inline uint32_t get_swi_errno(CPUState *cs) |
6ed68455 PM |
214 | { |
215 | #ifdef CONFIG_USER_ONLY | |
6ed68455 PM |
216 | TaskState *ts = cs->opaque; |
217 | ||
218 | return ts->swi_errno; | |
219 | #else | |
220 | return syscall_err; | |
8e71621f | 221 | #endif |
6ed68455 | 222 | } |
a4f81979 | 223 | |
64c8c6a9 | 224 | static void common_semi_cb(CPUState *cs, uint64_t ret, int err) |
a2d1ebaf | 225 | { |
709fe27b | 226 | if (err) { |
5aadd182 RH |
227 | #ifdef CONFIG_USER_ONLY |
228 | TaskState *ts = cs->opaque; | |
229 | ts->swi_errno = err; | |
230 | #else | |
231 | syscall_err = err; | |
232 | #endif | |
a2d1ebaf | 233 | } |
5aadd182 | 234 | common_semi_set_ret(cs, ret); |
faacc041 PM |
235 | } |
236 | ||
5d77289d RH |
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 | ||
af0484b5 RH |
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 | */ | |
64c8c6a9 | 250 | static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err) |
af0484b5 RH |
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 | ||
a2212474 RH |
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 | */ | |
64c8c6a9 | 269 | static void common_semi_istty_cb(CPUState *cs, uint64_t ret, int err) |
a2212474 RH |
270 | { |
271 | if (err) { | |
272 | ret = (err == ENOTTY ? 0 : -1); | |
273 | } | |
274 | common_semi_cb(cs, ret, err); | |
275 | } | |
276 | ||
9a894704 RH |
277 | /* |
278 | * SYS_SEEK returns 0 on success, not the resulting offset. | |
279 | */ | |
64c8c6a9 | 280 | static void common_semi_seek_cb(CPUState *cs, uint64_t ret, int err) |
9a894704 RH |
281 | { |
282 | if (!err) { | |
283 | ret = 0; | |
284 | } | |
285 | common_semi_cb(cs, ret, err); | |
286 | } | |
287 | ||
3c820ddc RH |
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 | */ | |
3c37cfe0 | 294 | static target_ulong common_semi_flen_buf(CPUState *cs) |
faacc041 | 295 | { |
3c820ddc | 296 | target_ulong sp = common_semi_stack_bottom(cs); |
faacc041 | 297 | return sp - 64; |
a2d1ebaf PB |
298 | } |
299 | ||
3c37cfe0 | 300 | static void |
64c8c6a9 | 301 | common_semi_flen_fstat_cb(CPUState *cs, uint64_t ret, int err) |
33d9cc8a | 302 | { |
84ca0dfd | 303 | if (!err) { |
cd7f29e3 RH |
304 | /* The size is always stored in big-endian order, extract the value. */ |
305 | uint64_t size; | |
a6300ed6 RH |
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 | } | |
84ca0dfd RH |
316 | } |
317 | common_semi_cb(cs, ret, err); | |
33d9cc8a PB |
318 | } |
319 | ||
1577eec0 RH |
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 | ||
c46a653c PM |
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 | ||
22a43bb9 PM |
341 | /* Feature bits reportable in feature byte 0 */ |
342 | #define SH_EXT_EXIT_EXTENDED (1 << 0) | |
6ee18643 | 343 | #define SH_EXT_STDOUT_STDERR (1 << 1) |
22a43bb9 | 344 | |
c46a653c PM |
345 | static const uint8_t featurefile_data[] = { |
346 | SHFB_MAGIC_0, | |
347 | SHFB_MAGIC_1, | |
348 | SHFB_MAGIC_2, | |
349 | SHFB_MAGIC_3, | |
6ee18643 | 350 | SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ |
c46a653c PM |
351 | }; |
352 | ||
4cb28db9 AB |
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 | |
5d77289d | 359 | * leave the register unchanged. |
4cb28db9 | 360 | */ |
ed3a06b1 | 361 | void do_common_semihosting(CPUState *cs) |
a4f81979 | 362 | { |
3c37cfe0 | 363 | CPUArchState *env = cs->env_ptr; |
53a5960a | 364 | target_ulong args; |
f296c0d1 | 365 | target_ulong arg0, arg1, arg2, arg3; |
27e3b109 | 366 | target_ulong ul_ret; |
a4f81979 FB |
367 | char * s; |
368 | int nr; | |
369 | uint32_t ret; | |
4d834039 | 370 | int64_t elapsed; |
a4f81979 | 371 | |
3c37cfe0 KP |
372 | nr = common_semi_arg(cs, 0) & 0xffffffffU; |
373 | args = common_semi_arg(cs, 1); | |
faacc041 | 374 | |
a4f81979 | 375 | switch (nr) { |
3881725c | 376 | case TARGET_SYS_OPEN: |
35e9a0a8 | 377 | { |
ed3a06b1 RH |
378 | int ret, err = 0; |
379 | int hostfd; | |
35e9a0a8 | 380 | |
f296c0d1 PM |
381 | GET_ARG(0); |
382 | GET_ARG(1); | |
383 | GET_ARG(2); | |
384 | s = lock_user_string(arg0); | |
385 | if (!s) { | |
ed3a06b1 | 386 | goto do_fault; |
f296c0d1 PM |
387 | } |
388 | if (arg1 >= 12) { | |
389 | unlock_user(s, arg0, 0); | |
ed3a06b1 RH |
390 | common_semi_cb(cs, -1, EINVAL); |
391 | break; | |
35e9a0a8 PM |
392 | } |
393 | ||
a4f81979 | 394 | if (strcmp(s, ":tt") == 0) { |
6ee18643 PM |
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) { | |
ed3a06b1 | 402 | hostfd = STDIN_FILENO; |
6ee18643 | 403 | } else if (arg1 < 8) { |
ed3a06b1 | 404 | hostfd = STDOUT_FILENO; |
6ee18643 | 405 | } else { |
ed3a06b1 | 406 | hostfd = STDERR_FILENO; |
6ee18643 | 407 | } |
ed3a06b1 RH |
408 | ret = alloc_guestfd(); |
409 | associate_guestfd(ret, hostfd); | |
410 | } else if (strcmp(s, ":semihosting-features") == 0) { | |
c46a653c PM |
411 | /* We must fail opens for modes other than 0 ('r') or 1 ('rb') */ |
412 | if (arg1 != 0 && arg1 != 1) { | |
ed3a06b1 RH |
413 | ret = -1; |
414 | err = EACCES; | |
415 | } else { | |
416 | ret = alloc_guestfd(); | |
417 | staticfile_guestfd(ret, featurefile_data, | |
418 | sizeof(featurefile_data)); | |
c46a653c | 419 | } |
5b3f39cb | 420 | } else { |
ed3a06b1 | 421 | unlock_user(s, arg0, 0); |
5b3f39cb RH |
422 | semihost_sys_open(cs, common_semi_cb, arg0, arg2 + 1, |
423 | gdb_open_modeflags[arg1], 0644); | |
ed3a06b1 | 424 | break; |
a2d1ebaf | 425 | } |
f296c0d1 | 426 | unlock_user(s, arg0, 0); |
ed3a06b1 RH |
427 | common_semi_cb(cs, ret, err); |
428 | break; | |
35e9a0a8 | 429 | } |
ed3a06b1 | 430 | |
3881725c | 431 | case TARGET_SYS_CLOSE: |
f296c0d1 | 432 | GET_ARG(0); |
5eadbbfc | 433 | semihost_sys_close(cs, common_semi_cb, arg0); |
ed3a06b1 RH |
434 | break; |
435 | ||
3881725c | 436 | case TARGET_SYS_WRITEC: |
5d77289d RH |
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); | |
ed3a06b1 RH |
443 | break; |
444 | ||
3881725c | 445 | case TARGET_SYS_WRITE0: |
7281550c RH |
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 | } | |
ed3a06b1 RH |
455 | break; |
456 | ||
3881725c | 457 | case TARGET_SYS_WRITE: |
f296c0d1 PM |
458 | GET_ARG(0); |
459 | GET_ARG(1); | |
460 | GET_ARG(2); | |
aa915bd0 | 461 | semihost_sys_write(cs, common_semi_rw_cb, arg0, arg1, arg2); |
ed3a06b1 | 462 | break; |
35e9a0a8 | 463 | |
3881725c | 464 | case TARGET_SYS_READ: |
f296c0d1 PM |
465 | GET_ARG(0); |
466 | GET_ARG(1); | |
467 | GET_ARG(2); | |
af0484b5 | 468 | semihost_sys_read(cs, common_semi_rw_cb, arg0, arg1, arg2); |
ed3a06b1 | 469 | break; |
35e9a0a8 | 470 | |
3881725c | 471 | case TARGET_SYS_READC: |
1577eec0 RH |
472 | semihost_sys_read_gf(cs, common_semi_readc_cb, &console_in_gf, |
473 | common_semi_stack_bottom(cs) - 1, 1); | |
ed3a06b1 RH |
474 | break; |
475 | ||
767ba049 KP |
476 | case TARGET_SYS_ISERROR: |
477 | GET_ARG(0); | |
ed3a06b1 RH |
478 | common_semi_set_ret(cs, (target_long)arg0 < 0); |
479 | break; | |
480 | ||
3881725c | 481 | case TARGET_SYS_ISTTY: |
f296c0d1 | 482 | GET_ARG(0); |
a2212474 | 483 | semihost_sys_isatty(cs, common_semi_istty_cb, arg0); |
ed3a06b1 | 484 | break; |
35e9a0a8 | 485 | |
3881725c | 486 | case TARGET_SYS_SEEK: |
f296c0d1 PM |
487 | GET_ARG(0); |
488 | GET_ARG(1); | |
9a894704 | 489 | semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET); |
ed3a06b1 | 490 | break; |
35e9a0a8 | 491 | |
3881725c | 492 | case TARGET_SYS_FLEN: |
f296c0d1 | 493 | GET_ARG(0); |
a6300ed6 RH |
494 | semihost_sys_flen(cs, common_semi_flen_fstat_cb, common_semi_cb, |
495 | arg0, common_semi_flen_buf(cs)); | |
ed3a06b1 | 496 | break; |
35e9a0a8 | 497 | |
3881725c | 498 | case TARGET_SYS_TMPNAM: |
ed3a06b1 RH |
499 | { |
500 | int len; | |
501 | char *p; | |
502 | ||
27e3b109 KP |
503 | GET_ARG(0); |
504 | GET_ARG(1); | |
505 | GET_ARG(2); | |
3878d0c7 BM |
506 | len = asprintf(&s, "%s/qemu-%x%02x", g_get_tmp_dir(), |
507 | getpid(), (int)arg1 & 0xff); | |
9b1268f5 PM |
508 | if (len < 0) { |
509 | common_semi_set_ret(cs, -1); | |
510 | break; | |
511 | } | |
512 | ||
513 | /* Allow for trailing NUL */ | |
514 | len++; | |
27e3b109 | 515 | /* Make sure there's enough space in the buffer */ |
9b1268f5 PM |
516 | if (len > arg2) { |
517 | free(s); | |
ed3a06b1 RH |
518 | common_semi_set_ret(cs, -1); |
519 | break; | |
27e3b109 | 520 | } |
ed3a06b1 RH |
521 | p = lock_user(VERIFY_WRITE, arg0, len, 0); |
522 | if (!p) { | |
9b1268f5 | 523 | free(s); |
ed3a06b1 RH |
524 | goto do_fault; |
525 | } | |
9b1268f5 | 526 | memcpy(p, s, len); |
ed3a06b1 | 527 | unlock_user(p, arg0, len); |
27e3b109 | 528 | free(s); |
ed3a06b1 RH |
529 | common_semi_set_ret(cs, 0); |
530 | break; | |
531 | } | |
532 | ||
3881725c | 533 | case TARGET_SYS_REMOVE: |
f296c0d1 PM |
534 | GET_ARG(0); |
535 | GET_ARG(1); | |
d49e79b8 | 536 | semihost_sys_remove(cs, common_semi_cb, arg0, arg1 + 1); |
ed3a06b1 RH |
537 | break; |
538 | ||
3881725c | 539 | case TARGET_SYS_RENAME: |
f296c0d1 PM |
540 | GET_ARG(0); |
541 | GET_ARG(1); | |
542 | GET_ARG(2); | |
543 | GET_ARG(3); | |
25a95da0 | 544 | semihost_sys_rename(cs, common_semi_cb, arg0, arg1 + 1, arg2, arg3 + 1); |
ed3a06b1 RH |
545 | break; |
546 | ||
3881725c | 547 | case TARGET_SYS_CLOCK: |
ed3a06b1 RH |
548 | common_semi_set_ret(cs, clock() / (CLOCKS_PER_SEC / 100)); |
549 | break; | |
550 | ||
3881725c | 551 | case TARGET_SYS_TIME: |
ed3a06b1 RH |
552 | ul_ret = time(NULL); |
553 | common_semi_cb(cs, ul_ret, ul_ret == -1 ? errno : 0); | |
554 | break; | |
555 | ||
3881725c | 556 | case TARGET_SYS_SYSTEM: |
f296c0d1 PM |
557 | GET_ARG(0); |
558 | GET_ARG(1); | |
90d8e0b0 | 559 | semihost_sys_system(cs, common_semi_cb, arg0, arg1 + 1); |
ed3a06b1 RH |
560 | break; |
561 | ||
3881725c | 562 | case TARGET_SYS_ERRNO: |
ed3a06b1 RH |
563 | common_semi_set_ret(cs, get_swi_errno(cs)); |
564 | break; | |
565 | ||
3881725c | 566 | case TARGET_SYS_GET_CMDLINE: |
38d0662a | 567 | { |
1c1b40c1 CV |
568 | /* Build a command-line from the original argv. |
569 | * | |
570 | * The inputs are: | |
f296c0d1 PM |
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 | |
1c1b40c1 CV |
574 | * bytes. |
575 | * | |
576 | * The outputs are: | |
f296c0d1 | 577 | * * arg0, pointer to null-terminated string of the |
1c1b40c1 | 578 | * command line. |
f296c0d1 | 579 | * * arg1, length of the string pointed to by arg0. |
1c1b40c1 | 580 | */ |
579a97f7 | 581 | |
1c1b40c1 | 582 | char *output_buffer; |
f296c0d1 | 583 | size_t input_size; |
1c1b40c1 CV |
584 | size_t output_size; |
585 | int status = 0; | |
f3c2bda2 LI |
586 | #if !defined(CONFIG_USER_ONLY) |
587 | const char *cmdline; | |
6ed68455 PM |
588 | #else |
589 | TaskState *ts = cs->opaque; | |
f3c2bda2 | 590 | #endif |
f296c0d1 PM |
591 | GET_ARG(0); |
592 | GET_ARG(1); | |
593 | input_size = arg1; | |
1c1b40c1 CV |
594 | /* Compute the size of the output string. */ |
595 | #if !defined(CONFIG_USER_ONLY) | |
f3c2bda2 LI |
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. */ | |
1c1b40c1 CV |
601 | #else |
602 | unsigned int i; | |
38d0662a | 603 | |
60f1c801 | 604 | output_size = ts->info->env_strings - ts->info->arg_strings; |
1c1b40c1 | 605 | if (!output_size) { |
4cb28db9 AB |
606 | /* |
607 | * We special-case the "empty command line" case (argc==0). | |
608 | * Just provide the terminating 0. | |
609 | */ | |
1c1b40c1 CV |
610 | output_size = 1; |
611 | } | |
612 | #endif | |
38d0662a | 613 | |
1c1b40c1 | 614 | if (output_size > input_size) { |
4cb28db9 | 615 | /* Not enough space to store command-line arguments. */ |
ed3a06b1 RH |
616 | common_semi_cb(cs, -1, E2BIG); |
617 | break; | |
38d0662a | 618 | } |
38d0662a | 619 | |
1c1b40c1 | 620 | /* Adjust the command-line length. */ |
f296c0d1 PM |
621 | if (SET_ARG(1, output_size - 1)) { |
622 | /* Couldn't write back to argument block */ | |
ed3a06b1 | 623 | goto do_fault; |
f296c0d1 | 624 | } |
38d0662a | 625 | |
1c1b40c1 | 626 | /* Lock the buffer on the ARM side. */ |
f296c0d1 | 627 | output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0); |
1c1b40c1 | 628 | if (!output_buffer) { |
ed3a06b1 | 629 | goto do_fault; |
1c1b40c1 | 630 | } |
38d0662a | 631 | |
1c1b40c1 CV |
632 | /* Copy the command-line arguments. */ |
633 | #if !defined(CONFIG_USER_ONLY) | |
f3c2bda2 | 634 | pstrcpy(output_buffer, output_size, cmdline); |
1c1b40c1 CV |
635 | #else |
636 | if (output_size == 1) { | |
637 | /* Empty command-line. */ | |
638 | output_buffer[0] = '\0'; | |
639 | goto out; | |
640 | } | |
2e8785ac | 641 | |
60f1c801 | 642 | if (copy_from_user(output_buffer, ts->info->arg_strings, |
1c1b40c1 | 643 | output_size)) { |
ed3a06b1 RH |
644 | unlock_user(output_buffer, arg0, 0); |
645 | goto do_fault; | |
2e8785ac WS |
646 | } |
647 | ||
1c1b40c1 CV |
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. */ | |
f296c0d1 | 657 | unlock_user(output_buffer, arg0, output_size); |
ed3a06b1 | 658 | common_semi_cb(cs, status, 0); |
38d0662a | 659 | } |
ed3a06b1 RH |
660 | break; |
661 | ||
3881725c | 662 | case TARGET_SYS_HEAPINFO: |
a4f81979 | 663 | { |
f5666418 | 664 | target_ulong retvals[4]; |
f5666418 | 665 | int i; |
6ed68455 PM |
666 | #ifdef CONFIG_USER_ONLY |
667 | TaskState *ts = cs->opaque; | |
5fc983af | 668 | target_ulong limit; |
69515951 | 669 | #else |
5fc983af | 670 | LayoutInfo info = common_semi_find_bases(cs); |
6ed68455 | 671 | #endif |
f5666418 | 672 | |
f296c0d1 | 673 | GET_ARG(0); |
a4f81979 | 674 | |
8e71621f | 675 | #ifdef CONFIG_USER_ONLY |
4cb28db9 AB |
676 | /* |
677 | * Some C libraries assume the heap immediately follows .bss, so | |
678 | * allocate it using sbrk. | |
679 | */ | |
a4f81979 | 680 | if (!ts->heap_limit) { |
206ae74a | 681 | abi_ulong ret; |
a4f81979 | 682 | |
53a5960a | 683 | ts->heap_base = do_brk(0); |
3c37cfe0 | 684 | limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE; |
a4f81979 FB |
685 | /* Try a big heap, and reduce the size if that fails. */ |
686 | for (;;) { | |
53a5960a | 687 | ret = do_brk(limit); |
206ae74a | 688 | if (ret >= limit) { |
a4f81979 | 689 | break; |
206ae74a | 690 | } |
a4f81979 FB |
691 | limit = (ts->heap_base >> 1) + (limit >> 1); |
692 | } | |
693 | ts->heap_limit = limit; | |
694 | } | |
3b46e624 | 695 | |
f5666418 PM |
696 | retvals[0] = ts->heap_base; |
697 | retvals[1] = ts->heap_limit; | |
698 | retvals[2] = ts->stack_base; | |
699 | retvals[3] = 0; /* Stack limit. */ | |
8e71621f | 700 | #else |
5fc983af AB |
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. */ | |
8e71621f | 705 | #endif |
f5666418 PM |
706 | |
707 | for (i = 0; i < ARRAY_SIZE(retvals); i++) { | |
708 | bool fail; | |
709 | ||
35e3f029 AB |
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 | } | |
f5666418 PM |
715 | |
716 | if (fail) { | |
717 | /* Couldn't write back to argument block */ | |
ed3a06b1 | 718 | goto do_fault; |
f5666418 PM |
719 | } |
720 | } | |
ed3a06b1 | 721 | common_semi_set_ret(cs, 0); |
a4f81979 | 722 | } |
ed3a06b1 RH |
723 | break; |
724 | ||
3881725c | 725 | case TARGET_SYS_EXIT: |
22a43bb9 | 726 | case TARGET_SYS_EXIT_EXTENDED: |
3c37cfe0 | 727 | if (common_semi_sys_exit_extended(cs, nr)) { |
4cb28db9 | 728 | /* |
22a43bb9 | 729 | * The A64 version of SYS_EXIT takes a parameter block, |
7446d35e PM |
730 | * so the application-exit type can return a subcode which |
731 | * is the exit status code from the application. | |
22a43bb9 PM |
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. | |
7446d35e PM |
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 { | |
4cb28db9 | 744 | /* |
22a43bb9 PM |
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. | |
4cb28db9 | 749 | */ |
7446d35e PM |
750 | ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1; |
751 | } | |
ad9dcb20 | 752 | gdb_exit(ret); |
1ecc3a2d | 753 | exit(ret); |
ed3a06b1 | 754 | |
4d834039 KP |
755 | case TARGET_SYS_ELAPSED: |
756 | elapsed = get_clock() - clock_start; | |
757 | if (sizeof(target_ulong) == 8) { | |
fed49cdf PM |
758 | if (SET_ARG(0, elapsed)) { |
759 | goto do_fault; | |
760 | } | |
4d834039 | 761 | } else { |
fed49cdf PM |
762 | if (SET_ARG(0, (uint32_t) elapsed) || |
763 | SET_ARG(1, (uint32_t) (elapsed >> 32))) { | |
764 | goto do_fault; | |
765 | } | |
4d834039 | 766 | } |
ed3a06b1 RH |
767 | common_semi_set_ret(cs, 0); |
768 | break; | |
769 | ||
4d834039 KP |
770 | case TARGET_SYS_TICKFREQ: |
771 | /* qemu always uses nsec */ | |
ed3a06b1 RH |
772 | common_semi_set_ret(cs, 1000000000); |
773 | break; | |
774 | ||
e9ebfbfc | 775 | case TARGET_SYS_SYNCCACHE: |
4cb28db9 AB |
776 | /* |
777 | * Clean the D-cache and invalidate the I-cache for the specified | |
e9ebfbfc PM |
778 | * virtual address range. This is a nop for us since we don't |
779 | * implement caches. This is only present on A64. | |
780 | */ | |
a1df4bab | 781 | if (common_semi_has_synccache(env)) { |
ed3a06b1 RH |
782 | common_semi_set_ret(cs, 0); |
783 | break; | |
e9ebfbfc | 784 | } |
a1df4bab | 785 | /* fall through */ |
a4f81979 FB |
786 | default: |
787 | fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); | |
90c84c56 | 788 | cpu_dump_state(cs, stderr, 0); |
a4f81979 | 789 | abort(); |
ed3a06b1 | 790 | |
ed3a06b1 RH |
791 | do_fault: |
792 | common_semi_cb(cs, -1, EFAULT); | |
793 | break; | |
a4f81979 FB |
794 | } |
795 | } |