arm-semi.c revision 85125480c07e11d5dd98f69b71bded86ee903075
1/*
2 *  Arm "Angel" semihosting syscalls
3 *
4 *  Copyright (c) 2005, 2007 CodeSourcery.
5 *  Written by Paul Brook.
6 *
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.
11 *
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.
16 *
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/>.
19 */
20
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fcntl.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <time.h>
28
29#include "cpu.h"
30#ifdef CONFIG_USER_ONLY
31#include "qemu.h"
32
33#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
34#else
35#include "qemu-common.h"
36#include "exec/gdbstub.h"
37#include "hw/arm/arm.h"
38#endif
39
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
60
61#ifndef O_BINARY
62#define O_BINARY 0
63#endif
64
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
72
73static int gdb_open_modeflags[12] = {
74    GDB_O_RDONLY,
75    GDB_O_RDONLY | GDB_O_BINARY,
76    GDB_O_RDWR,
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
86};
87
88static int open_modeflags[12] = {
89    O_RDONLY,
90    O_RDONLY | O_BINARY,
91    O_RDWR,
92    O_RDWR | O_BINARY,
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
101};
102
103#ifdef CONFIG_USER_ONLY
104static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
105{
106    if (code == (uint32_t)-1)
107        ts->swi_errno = errno;
108    return code;
109}
110#else
111static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
112{
113    return code;
114}
115
116#include "exec/softmmu-semi.h"
117#endif
118
119static target_ulong arm_semi_syscall_len;
120
121#if !defined(CONFIG_USER_ONLY)
122static target_ulong syscall_err;
123#endif
124
125static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
126{
127#ifdef CONFIG_USER_ONLY
128    TaskState *ts = env->opaque;
129#endif
130
131    if (ret == (target_ulong)-1) {
132#ifdef CONFIG_USER_ONLY
133        ts->swi_errno = err;
134#else
135	syscall_err = err;
136#endif
137        env->regs[0] = ret;
138    } else {
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;
144            break;
145        case TARGET_SYS_SEEK:
146            env->regs[0] = 0;
147            break;
148        default:
149            env->regs[0] = ret;
150            break;
151        }
152    }
153}
154
155static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong err)
156{
157    /* The size is always stored in big-endian order, extract
158       the value. We assume the size always fit in 32 bits.  */
159    uint32_t size;
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;
164#else
165    syscall_err = err;
166#endif
167}
168
169/* Read the input value from the argument block; fail the semihosting
170 * call if the memory read fails.
171 */
172#define GET_ARG(n) do {                                 \
173    if (get_user_ual(arg ## n, args + (n) * 4)) {       \
174        return (uint32_t)-1;                            \
175    }                                                   \
176} while (0)
177
178#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
179uint32_t do_arm_semihosting(CPUARMState *env)
180{
181    target_ulong args;
182    target_ulong arg0, arg1, arg2, arg3;
183    char * s;
184    int nr;
185    uint32_t ret;
186    uint32_t len;
187#ifdef CONFIG_USER_ONLY
188    TaskState *ts = env->opaque;
189#else
190    CPUARMState *ts = env;
191#endif
192
193    nr = env->regs[0];
194    args = env->regs[1];
195    switch (nr) {
196    case TARGET_SYS_OPEN:
197        GET_ARG(0);
198        GET_ARG(1);
199        GET_ARG(2);
200        s = lock_user_string(arg0);
201        if (!s) {
202            /* FIXME - should this error code be -TARGET_EFAULT ? */
203            return (uint32_t)-1;
204        }
205        if (arg1 >= 12) {
206            unlock_user(s, arg0, 0);
207            return (uint32_t)-1;
208        }
209        if (strcmp(s, ":tt") == 0) {
210            int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
211            unlock_user(s, arg0, 0);
212            return result_fileno;
213        }
214        if (use_gdb_syscalls()) {
215            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
216                           (int)arg2+1, gdb_open_modeflags[arg1]);
217            ret = env->regs[0];
218        } else {
219            ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
220        }
221        unlock_user(s, arg0, 0);
222        return ret;
223    case TARGET_SYS_CLOSE:
224        GET_ARG(0);
225        if (use_gdb_syscalls()) {
226            gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
227            return env->regs[0];
228        } else {
229            return set_swi_errno(ts, close(arg0));
230        }
231    case TARGET_SYS_WRITEC:
232        {
233          char c;
234
235          if (get_user_u8(c, args))
236              /* FIXME - should this error code be -TARGET_EFAULT ? */
237              return (uint32_t)-1;
238          /* Write to debug console.  stderr is near enough.  */
239          if (use_gdb_syscalls()) {
240                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
241                return env->regs[0];
242          } else {
243                return write(STDERR_FILENO, &c, 1);
244          }
245        }
246    case TARGET_SYS_WRITE0:
247        if (!(s = lock_user_string(args)))
248            /* FIXME - should this error code be -TARGET_EFAULT ? */
249            return (uint32_t)-1;
250        len = strlen(s);
251        if (use_gdb_syscalls()) {
252            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
253            ret = env->regs[0];
254        } else {
255            ret = write(STDERR_FILENO, s, len);
256        }
257        unlock_user(s, args, 0);
258        return ret;
259    case TARGET_SYS_WRITE:
260        GET_ARG(0);
261        GET_ARG(1);
262        GET_ARG(2);
263        len = arg2;
264        if (use_gdb_syscalls()) {
265            arm_semi_syscall_len = len;
266            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
267            return env->regs[0];
268        } else {
269            s = lock_user(VERIFY_READ, arg1, len, 1);
270            if (!s) {
271                /* FIXME - should this error code be -TARGET_EFAULT ? */
272                return (uint32_t)-1;
273            }
274            ret = set_swi_errno(ts, write(arg0, s, len));
275            unlock_user(s, arg1, 0);
276            if (ret == (uint32_t)-1)
277                return -1;
278            return len - ret;
279        }
280    case TARGET_SYS_READ:
281        GET_ARG(0);
282        GET_ARG(1);
283        GET_ARG(2);
284        len = arg2;
285        if (use_gdb_syscalls()) {
286            arm_semi_syscall_len = len;
287            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
288            return env->regs[0];
289        } else {
290            s = lock_user(VERIFY_WRITE, arg1, len, 0);
291            if (!s) {
292                /* FIXME - should this error code be -TARGET_EFAULT ? */
293                return (uint32_t)-1;
294            }
295            do {
296                ret = set_swi_errno(ts, read(arg0, s, len));
297            } while (ret == -1 && errno == EINTR);
298            unlock_user(s, arg1, len);
299            if (ret == (uint32_t)-1)
300                return -1;
301            return len - ret;
302        }
303    case TARGET_SYS_READC:
304       /* XXX: Read from debug console. Not implemented.  */
305        return 0;
306    case TARGET_SYS_ISTTY:
307        GET_ARG(0);
308        if (use_gdb_syscalls()) {
309            gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
310            return env->regs[0];
311        } else {
312            return isatty(arg0);
313        }
314    case TARGET_SYS_SEEK:
315        GET_ARG(0);
316        GET_ARG(1);
317        if (use_gdb_syscalls()) {
318            gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
319            return env->regs[0];
320        } else {
321            ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
322            if (ret == (uint32_t)-1)
323              return -1;
324            return 0;
325        }
326    case TARGET_SYS_FLEN:
327        GET_ARG(0);
328        if (use_gdb_syscalls()) {
329            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
330                           arg0, env->regs[13]-64);
331            return env->regs[0];
332        } else {
333            struct stat buf;
334            ret = set_swi_errno(ts, fstat(arg0, &buf));
335            if (ret == (uint32_t)-1)
336                return -1;
337            return buf.st_size;
338        }
339    case TARGET_SYS_TMPNAM:
340        /* XXX: Not implemented.  */
341        return -1;
342    case TARGET_SYS_REMOVE:
343        GET_ARG(0);
344        GET_ARG(1);
345        if (use_gdb_syscalls()) {
346            gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
347            ret = env->regs[0];
348        } else {
349            s = lock_user_string(arg0);
350            if (!s) {
351                /* FIXME - should this error code be -TARGET_EFAULT ? */
352                return (uint32_t)-1;
353            }
354            ret =  set_swi_errno(ts, remove(s));
355            unlock_user(s, arg0, 0);
356        }
357        return ret;
358    case TARGET_SYS_RENAME:
359        GET_ARG(0);
360        GET_ARG(1);
361        GET_ARG(2);
362        GET_ARG(3);
363        if (use_gdb_syscalls()) {
364            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
365                           arg0, (int)arg1+1, arg2, (int)arg3+1);
366            return env->regs[0];
367        } else {
368            char *s2;
369            s = lock_user_string(arg0);
370            s2 = lock_user_string(arg2);
371            if (!s || !s2)
372                /* FIXME - should this error code be -TARGET_EFAULT ? */
373                ret = (uint32_t)-1;
374            else
375                ret = set_swi_errno(ts, rename(s, s2));
376            if (s2)
377                unlock_user(s2, arg2, 0);
378            if (s)
379                unlock_user(s, arg0, 0);
380            return ret;
381        }
382    case TARGET_SYS_CLOCK:
383        return clock() / (CLOCKS_PER_SEC / 100);
384    case TARGET_SYS_TIME:
385        return set_swi_errno(ts, time(NULL));
386    case TARGET_SYS_SYSTEM:
387        GET_ARG(0);
388        GET_ARG(1);
389        if (use_gdb_syscalls()) {
390            gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
391            return env->regs[0];
392        } else {
393            s = lock_user_string(arg0);
394            if (!s) {
395                /* FIXME - should this error code be -TARGET_EFAULT ? */
396                return (uint32_t)-1;
397            }
398            ret = set_swi_errno(ts, system(s));
399            unlock_user(s, arg0, 0);
400            return ret;
401        }
402    case TARGET_SYS_ERRNO:
403#ifdef CONFIG_USER_ONLY
404        return ts->swi_errno;
405#else
406        return syscall_err;
407#endif
408    case TARGET_SYS_GET_CMDLINE:
409        {
410            /* Build a command-line from the original argv.
411             *
412             * The inputs are:
413             *     * arg0, pointer to a buffer of at least the size
414             *               specified in arg1.
415             *     * arg1, size of the buffer pointed to by arg0 in
416             *               bytes.
417             *
418             * The outputs are:
419             *     * arg0, pointer to null-terminated string of the
420             *               command line.
421             *     * arg1, length of the string pointed to by arg0.
422             */
423
424            char *output_buffer;
425            size_t input_size;
426            size_t output_size;
427            int status = 0;
428            GET_ARG(0);
429            GET_ARG(1);
430            input_size = arg1;
431            /* Compute the size of the output string.  */
432#if !defined(CONFIG_USER_ONLY)
433            output_size = strlen(ts->boot_info->kernel_filename)
434                        + 1  /* Separating space.  */
435                        + strlen(ts->boot_info->kernel_cmdline)
436                        + 1; /* Terminating null byte.  */
437#else
438            unsigned int i;
439
440            output_size = ts->info->arg_end - ts->info->arg_start;
441            if (!output_size) {
442                /* We special-case the "empty command line" case (argc==0).
443                   Just provide the terminating 0. */
444                output_size = 1;
445            }
446#endif
447
448            if (output_size > input_size) {
449                 /* Not enough space to store command-line arguments.  */
450                return -1;
451            }
452
453            /* Adjust the command-line length.  */
454            if (SET_ARG(1, output_size - 1)) {
455                /* Couldn't write back to argument block */
456                return -1;
457            }
458
459            /* Lock the buffer on the ARM side.  */
460            output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
461            if (!output_buffer) {
462                return -1;
463            }
464
465            /* Copy the command-line arguments.  */
466#if !defined(CONFIG_USER_ONLY)
467            pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
468            pstrcat(output_buffer, output_size, " ");
469            pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
470#else
471            if (output_size == 1) {
472                /* Empty command-line.  */
473                output_buffer[0] = '\0';
474                goto out;
475            }
476
477            if (copy_from_user(output_buffer, ts->info->arg_start,
478                               output_size)) {
479                status = -1;
480                goto out;
481            }
482
483            /* Separate arguments by white spaces.  */
484            for (i = 0; i < output_size - 1; i++) {
485                if (output_buffer[i] == 0) {
486                    output_buffer[i] = ' ';
487                }
488            }
489        out:
490#endif
491            /* Unlock the buffer on the ARM side.  */
492            unlock_user(output_buffer, arg0, output_size);
493
494            return status;
495        }
496    case TARGET_SYS_HEAPINFO:
497        {
498            uint32_t *ptr;
499            uint32_t limit;
500            GET_ARG(0);
501
502#ifdef CONFIG_USER_ONLY
503            /* Some C libraries assume the heap immediately follows .bss, so
504               allocate it using sbrk.  */
505            if (!ts->heap_limit) {
506                abi_ulong ret;
507
508                ts->heap_base = do_brk(0);
509                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
510                /* Try a big heap, and reduce the size if that fails.  */
511                for (;;) {
512                    ret = do_brk(limit);
513                    if (ret >= limit) {
514                        break;
515                    }
516                    limit = (ts->heap_base >> 1) + (limit >> 1);
517                }
518                ts->heap_limit = limit;
519            }
520
521            ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
522            if (!ptr) {
523                /* FIXME - should this error code be -TARGET_EFAULT ? */
524                return (uint32_t)-1;
525            }
526            ptr[0] = tswap32(ts->heap_base);
527            ptr[1] = tswap32(ts->heap_limit);
528            ptr[2] = tswap32(ts->stack_base);
529            ptr[3] = tswap32(0); /* Stack limit.  */
530            unlock_user(ptr, arg0, 16);
531#else
532            limit = ram_size;
533            ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
534            if (!ptr) {
535                /* FIXME - should this error code be -TARGET_EFAULT ? */
536                return (uint32_t)-1;
537            }
538            /* TODO: Make this use the limit of the loaded application.  */
539            ptr[0] = tswap32(limit / 2);
540            ptr[1] = tswap32(limit);
541            ptr[2] = tswap32(limit); /* Stack base */
542            ptr[3] = tswap32(0); /* Stack limit.  */
543            unlock_user(ptr, arg0, 16);
544#endif
545            return 0;
546        }
547    case TARGET_SYS_EXIT:
548        //gdb_exit(env, 0);
549        exit(0);
550    default:
551        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
552        cpu_dump_state(env, stderr, fprintf, 0);
553        abort();
554    }
555}
556