]>
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 | |
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 | ||
95 | static 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 | ||
110 | static 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 |
125 | typedef 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 | */ | |
136 | typedef struct GuestFD { | |
137 | GuestFDType type; | |
c46a653c PM |
138 | union { |
139 | int hostfd; | |
140 | target_ulong featurefile_offset; | |
141 | }; | |
35e9a0a8 PM |
142 | } GuestFD; |
143 | ||
144 | static 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 | */ | |
151 | static inline hwaddr | |
152 | common_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 |
177 | static inline target_ulong | |
178 | common_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 | ||
189 | static inline void | |
190 | common_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 | ||
201 | static inline bool | |
202 | common_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" | |
209 | static inline target_ulong | |
210 | common_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 |
232 | static inline target_ulong | |
233 | common_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 | ||
240 | static inline void | |
241 | common_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 | ||
248 | static inline bool | |
249 | common_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 | ||
256 | static inline target_ulong | |
257 | common_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 | */ | |
274 | static 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 | */ | |
302 | static 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 | */ |
320 | static 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 | */ | |
333 | static 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 | */ | |
349 | static 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 |
367 | static target_ulong syscall_err; |
368 | ||
6ed68455 PM |
369 | #include "exec/softmmu-semi.h" |
370 | #endif | |
371 | ||
3c37cfe0 | 372 | static 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 | 386 | static 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 | 397 | static target_ulong common_semi_syscall_len; |
a2d1ebaf | 398 | |
3c37cfe0 | 399 | static 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 | 425 | static 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 |
453 | static void |
454 | common_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 | 467 | static int common_semi_open_guestfd; |
35e9a0a8 | 468 | |
3c37cfe0 KP |
469 | static void |
470 | common_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 |
483 | static target_ulong |
484 | common_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 |
519 | typedef uint32_t sys_closefn(CPUState *cs, GuestFD *gf); |
520 | typedef uint32_t sys_writefn(CPUState *cs, GuestFD *gf, | |
52c8a163 | 521 | target_ulong buf, uint32_t len); |
3c37cfe0 | 522 | typedef uint32_t sys_readfn(CPUState *cs, GuestFD *gf, |
2c3a09a6 | 523 | target_ulong buf, uint32_t len); |
3c37cfe0 KP |
524 | typedef uint32_t sys_isattyfn(CPUState *cs, GuestFD *gf); |
525 | typedef uint32_t sys_seekfn(CPUState *cs, GuestFD *gf, | |
45e88ffc | 526 | target_ulong offset); |
3c37cfe0 | 527 | typedef uint32_t sys_flenfn(CPUState *cs, GuestFD *gf); |
263eb621 | 528 | |
3c37cfe0 | 529 | static 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 | 543 | static 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 | 563 | static 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 | 585 | static uint32_t host_isattyfn(CPUState *cs, GuestFD *gf) |
0213fa45 PM |
586 | { |
587 | return isatty(gf->hostfd); | |
588 | } | |
589 | ||
3c37cfe0 | 590 | static 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 | 599 | static 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 | 609 | static 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 | 614 | static 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 | 622 | static 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 | 630 | static 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 | 635 | static 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 | 641 | static 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 |
656 | static 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 | ||
664 | static 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 | 673 | static uint32_t featurefile_closefn(CPUState *cs, GuestFD *gf) |
c46a653c PM |
674 | { |
675 | /* Nothing to do */ | |
676 | return 0; | |
677 | } | |
678 | ||
3c37cfe0 | 679 | static 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 | 688 | static 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 | 715 | static uint32_t featurefile_isattyfn(CPUState *cs, GuestFD *gf) |
c46a653c PM |
716 | { |
717 | return 0; | |
718 | } | |
719 | ||
3c37cfe0 | 720 | static 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 | 727 | static uint32_t featurefile_flenfn(CPUState *cs, GuestFD *gf) |
c46a653c PM |
728 | { |
729 | return sizeof(featurefile_data); | |
730 | } | |
731 | ||
263eb621 PM |
732 | typedef 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 | ||
741 | static 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 |
773 | static 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 | 814 | target_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 | } |