2
* Arm "Angel" semihosting syscalls
4
* Copyright (c) 2005, 2007 CodeSourcery.
5
* Written by Paul Brook.
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, see <http://www.gnu.org/licenses/>.
21
#include <sys/types.h>
30
#ifdef CONFIG_USER_ONLY
33
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
35
#include "qemu-common.h"
37
#include "hw/arm-misc.h"
40
#define TARGET_SYS_OPEN 0x01
41
#define TARGET_SYS_CLOSE 0x02
42
#define TARGET_SYS_WRITEC 0x03
43
#define TARGET_SYS_WRITE0 0x04
44
#define TARGET_SYS_WRITE 0x05
45
#define TARGET_SYS_READ 0x06
46
#define TARGET_SYS_READC 0x07
47
#define TARGET_SYS_ISTTY 0x09
48
#define TARGET_SYS_SEEK 0x0a
49
#define TARGET_SYS_FLEN 0x0c
50
#define TARGET_SYS_TMPNAM 0x0d
51
#define TARGET_SYS_REMOVE 0x0e
52
#define TARGET_SYS_RENAME 0x0f
53
#define TARGET_SYS_CLOCK 0x10
54
#define TARGET_SYS_TIME 0x11
55
#define TARGET_SYS_SYSTEM 0x12
56
#define TARGET_SYS_ERRNO 0x13
57
#define TARGET_SYS_GET_CMDLINE 0x15
58
#define TARGET_SYS_HEAPINFO 0x16
59
#define TARGET_SYS_EXIT 0x18
65
#define GDB_O_RDONLY 0x000
66
#define GDB_O_WRONLY 0x001
67
#define GDB_O_RDWR 0x002
68
#define GDB_O_APPEND 0x008
69
#define GDB_O_CREAT 0x200
70
#define GDB_O_TRUNC 0x400
71
#define GDB_O_BINARY 0
73
static int gdb_open_modeflags[12] = {
75
GDB_O_RDONLY | GDB_O_BINARY,
77
GDB_O_RDWR | GDB_O_BINARY,
78
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
79
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
80
GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
81
GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
82
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
83
GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
84
GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
85
GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
88
static int open_modeflags[12] = {
93
O_WRONLY | O_CREAT | O_TRUNC,
94
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
95
O_RDWR | O_CREAT | O_TRUNC,
96
O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
97
O_WRONLY | O_CREAT | O_APPEND,
98
O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
99
O_RDWR | O_CREAT | O_APPEND,
100
O_RDWR | O_CREAT | O_APPEND | O_BINARY
103
#ifdef CONFIG_USER_ONLY
104
static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
106
if (code == (uint32_t)-1)
107
ts->swi_errno = errno;
111
static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
116
#include "softmmu-semi.h"
119
static target_ulong arm_semi_syscall_len;
121
#if !defined(CONFIG_USER_ONLY)
122
static target_ulong syscall_err;
125
static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
127
#ifdef CONFIG_USER_ONLY
128
TaskState *ts = env->opaque;
131
if (ret == (target_ulong)-1) {
132
#ifdef CONFIG_USER_ONLY
139
/* Fixup syscalls that use nonstardard return conventions. */
140
switch (env->regs[0]) {
141
case TARGET_SYS_WRITE:
142
case TARGET_SYS_READ:
143
env->regs[0] = arm_semi_syscall_len - ret;
145
case TARGET_SYS_SEEK:
155
static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong err)
157
/* The size is always stored in big-endian order, extract
158
the value. We assume the size always fit in 32 bits. */
160
cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
161
env->regs[0] = be32_to_cpu(size);
162
#ifdef CONFIG_USER_ONLY
163
((TaskState *)env->opaque)->swi_errno = err;
171
target_ulong __arg; \
172
/* FIXME - handle get_user() failure */ \
173
get_user_ual(__arg, args + (n) * 4); \
176
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
177
uint32_t do_arm_semihosting(CPUARMState *env)
184
#ifdef CONFIG_USER_ONLY
185
TaskState *ts = env->opaque;
187
CPUARMState *ts = env;
193
case TARGET_SYS_OPEN:
194
if (!(s = lock_user_string(ARG(0))))
195
/* FIXME - should this error code be -TARGET_EFAULT ? */
199
if (strcmp(s, ":tt") == 0) {
203
return STDOUT_FILENO;
205
if (use_gdb_syscalls()) {
206
gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
207
(int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
210
ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
212
unlock_user(s, ARG(0), 0);
214
case TARGET_SYS_CLOSE:
215
if (use_gdb_syscalls()) {
216
gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
219
return set_swi_errno(ts, close(ARG(0)));
221
case TARGET_SYS_WRITEC:
225
if (get_user_u8(c, args))
226
/* FIXME - should this error code be -TARGET_EFAULT ? */
228
/* Write to debug console. stderr is near enough. */
229
if (use_gdb_syscalls()) {
230
gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
233
return write(STDERR_FILENO, &c, 1);
236
case TARGET_SYS_WRITE0:
237
if (!(s = lock_user_string(args)))
238
/* FIXME - should this error code be -TARGET_EFAULT ? */
241
if (use_gdb_syscalls()) {
242
gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
245
ret = write(STDERR_FILENO, s, len);
247
unlock_user(s, args, 0);
249
case TARGET_SYS_WRITE:
251
if (use_gdb_syscalls()) {
252
arm_semi_syscall_len = len;
253
gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
256
if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
257
/* FIXME - should this error code be -TARGET_EFAULT ? */
259
ret = set_swi_errno(ts, write(ARG(0), s, len));
260
unlock_user(s, ARG(1), 0);
261
if (ret == (uint32_t)-1)
265
case TARGET_SYS_READ:
267
if (use_gdb_syscalls()) {
268
arm_semi_syscall_len = len;
269
gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
272
if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
273
/* FIXME - should this error code be -TARGET_EFAULT ? */
276
ret = set_swi_errno(ts, read(ARG(0), s, len));
277
while (ret == -1 && errno == EINTR);
278
unlock_user(s, ARG(1), len);
279
if (ret == (uint32_t)-1)
283
case TARGET_SYS_READC:
284
/* XXX: Read from debug cosole. Not implemented. */
286
case TARGET_SYS_ISTTY:
287
if (use_gdb_syscalls()) {
288
gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
291
return isatty(ARG(0));
293
case TARGET_SYS_SEEK:
294
if (use_gdb_syscalls()) {
295
gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
298
ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
299
if (ret == (uint32_t)-1)
303
case TARGET_SYS_FLEN:
304
if (use_gdb_syscalls()) {
305
gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
306
ARG(0), env->regs[13]-64);
310
ret = set_swi_errno(ts, fstat(ARG(0), &buf));
311
if (ret == (uint32_t)-1)
315
case TARGET_SYS_TMPNAM:
316
/* XXX: Not implemented. */
318
case TARGET_SYS_REMOVE:
319
if (use_gdb_syscalls()) {
320
gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
323
if (!(s = lock_user_string(ARG(0))))
324
/* FIXME - should this error code be -TARGET_EFAULT ? */
326
ret = set_swi_errno(ts, remove(s));
327
unlock_user(s, ARG(0), 0);
330
case TARGET_SYS_RENAME:
331
if (use_gdb_syscalls()) {
332
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
333
ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
337
s = lock_user_string(ARG(0));
338
s2 = lock_user_string(ARG(2));
340
/* FIXME - should this error code be -TARGET_EFAULT ? */
343
ret = set_swi_errno(ts, rename(s, s2));
345
unlock_user(s2, ARG(2), 0);
347
unlock_user(s, ARG(0), 0);
350
case TARGET_SYS_CLOCK:
351
return clock() / (CLOCKS_PER_SEC / 100);
352
case TARGET_SYS_TIME:
353
return set_swi_errno(ts, time(NULL));
354
case TARGET_SYS_SYSTEM:
355
if (use_gdb_syscalls()) {
356
gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
359
if (!(s = lock_user_string(ARG(0))))
360
/* FIXME - should this error code be -TARGET_EFAULT ? */
362
ret = set_swi_errno(ts, system(s));
363
unlock_user(s, ARG(0), 0);
366
case TARGET_SYS_ERRNO:
367
#ifdef CONFIG_USER_ONLY
368
return ts->swi_errno;
372
case TARGET_SYS_GET_CMDLINE:
374
/* Build a command-line from the original argv.
377
* * ARG(0), pointer to a buffer of at least the size
378
* specified in ARG(1).
379
* * ARG(1), size of the buffer pointed to by ARG(0) in
383
* * ARG(0), pointer to null-terminated string of the
385
* * ARG(1), length of the string pointed to by ARG(0).
389
size_t input_size = ARG(1);
393
/* Compute the size of the output string. */
394
#if !defined(CONFIG_USER_ONLY)
395
output_size = strlen(ts->boot_info->kernel_filename)
396
+ 1 /* Separating space. */
397
+ strlen(ts->boot_info->kernel_cmdline)
398
+ 1; /* Terminating null byte. */
402
output_size = ts->info->arg_end - ts->info->arg_start;
404
/* We special-case the "empty command line" case (argc==0).
405
Just provide the terminating 0. */
410
if (output_size > input_size) {
411
/* Not enough space to store command-line arguments. */
415
/* Adjust the command-line length. */
416
SET_ARG(1, output_size - 1);
418
/* Lock the buffer on the ARM side. */
419
output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0);
420
if (!output_buffer) {
424
/* Copy the command-line arguments. */
425
#if !defined(CONFIG_USER_ONLY)
426
pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
427
pstrcat(output_buffer, output_size, " ");
428
pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
430
if (output_size == 1) {
431
/* Empty command-line. */
432
output_buffer[0] = '\0';
436
if (copy_from_user(output_buffer, ts->info->arg_start,
442
/* Separate arguments by white spaces. */
443
for (i = 0; i < output_size - 1; i++) {
444
if (output_buffer[i] == 0) {
445
output_buffer[i] = ' ';
450
/* Unlock the buffer on the ARM side. */
451
unlock_user(output_buffer, ARG(0), output_size);
455
case TARGET_SYS_HEAPINFO:
460
#ifdef CONFIG_USER_ONLY
461
/* Some C libraries assume the heap immediately follows .bss, so
462
allocate it using sbrk. */
463
if (!ts->heap_limit) {
466
ts->heap_base = do_brk(0);
467
limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
468
/* Try a big heap, and reduce the size if that fails. */
474
limit = (ts->heap_base >> 1) + (limit >> 1);
476
ts->heap_limit = limit;
479
if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
480
/* FIXME - should this error code be -TARGET_EFAULT ? */
482
ptr[0] = tswap32(ts->heap_base);
483
ptr[1] = tswap32(ts->heap_limit);
484
ptr[2] = tswap32(ts->stack_base);
485
ptr[3] = tswap32(0); /* Stack limit. */
486
unlock_user(ptr, ARG(0), 16);
489
if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
490
/* FIXME - should this error code be -TARGET_EFAULT ? */
492
/* TODO: Make this use the limit of the loaded application. */
493
ptr[0] = tswap32(limit / 2);
494
ptr[1] = tswap32(limit);
495
ptr[2] = tswap32(limit); /* Stack base */
496
ptr[3] = tswap32(0); /* Stack limit. */
497
unlock_user(ptr, ARG(0), 16);
501
case TARGET_SYS_EXIT:
505
fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
506
cpu_dump_state(env, stderr, fprintf, 0);