arm-semi.c revision 82a591c621f7c03a03d998383093bdcfdf95c0a8
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#endif
38
39#define SYS_OPEN        0x01
40#define SYS_CLOSE       0x02
41#define SYS_WRITEC      0x03
42#define SYS_WRITE0      0x04
43#define SYS_WRITE       0x05
44#define SYS_READ        0x06
45#define SYS_READC       0x07
46#define SYS_ISTTY       0x09
47#define SYS_SEEK        0x0a
48#define SYS_FLEN        0x0c
49#define SYS_TMPNAM      0x0d
50#define SYS_REMOVE      0x0e
51#define SYS_RENAME      0x0f
52#define SYS_CLOCK       0x10
53#define SYS_TIME        0x11
54#define SYS_SYSTEM      0x12
55#define SYS_ERRNO       0x13
56#define SYS_GET_CMDLINE 0x15
57#define SYS_HEAPINFO    0x16
58#define SYS_EXIT        0x18
59
60#ifndef O_BINARY
61#define O_BINARY 0
62#endif
63
64#define GDB_O_RDONLY  0x000
65#define GDB_O_WRONLY  0x001
66#define GDB_O_RDWR    0x002
67#define GDB_O_APPEND  0x008
68#define GDB_O_CREAT   0x200
69#define GDB_O_TRUNC   0x400
70#define GDB_O_BINARY  0
71
72static int gdb_open_modeflags[12] = {
73    GDB_O_RDONLY,
74    GDB_O_RDONLY | GDB_O_BINARY,
75    GDB_O_RDWR,
76    GDB_O_RDWR | GDB_O_BINARY,
77    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
78    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
79    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
80    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
81    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
82    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
83    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
84    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
85};
86
87static int open_modeflags[12] = {
88    O_RDONLY,
89    O_RDONLY | O_BINARY,
90    O_RDWR,
91    O_RDWR | O_BINARY,
92    O_WRONLY | O_CREAT | O_TRUNC,
93    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
94    O_RDWR | O_CREAT | O_TRUNC,
95    O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
96    O_WRONLY | O_CREAT | O_APPEND,
97    O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
98    O_RDWR | O_CREAT | O_APPEND,
99    O_RDWR | O_CREAT | O_APPEND | O_BINARY
100};
101
102#ifdef CONFIG_USER_ONLY
103static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
104{
105    if (code == (uint32_t)-1)
106        ts->swi_errno = errno;
107    return code;
108}
109#else
110static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
111{
112    return code;
113}
114
115#include "exec/softmmu-semi.h"
116#endif
117
118static target_ulong arm_semi_syscall_len;
119
120#if !defined(CONFIG_USER_ONLY)
121static target_ulong syscall_err;
122#endif
123
124static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
125{
126#ifdef CONFIG_USER_ONLY
127    TaskState *ts = env->opaque;
128#endif
129
130    if (ret == (target_ulong)-1) {
131#ifdef CONFIG_USER_ONLY
132        ts->swi_errno = err;
133#else
134	syscall_err = err;
135#endif
136        env->regs[0] = ret;
137    } else {
138        /* Fixup syscalls that use nonstardard return conventions.  */
139        switch (env->regs[0]) {
140        case SYS_WRITE:
141        case SYS_READ:
142            env->regs[0] = arm_semi_syscall_len - ret;
143            break;
144        case SYS_SEEK:
145            env->regs[0] = 0;
146            break;
147        default:
148            env->regs[0] = ret;
149            break;
150        }
151    }
152}
153
154static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
155{
156    /* The size is always stored in big-endian order, extract
157       the value. We assume the size always fit in 32 bits.  */
158    uint32_t size;
159    cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
160    env->regs[0] = be32_to_cpu(size);
161#ifdef CONFIG_USER_ONLY
162    ((TaskState *)env->opaque)->swi_errno = err;
163#else
164    syscall_err = err;
165#endif
166}
167
168#define ARG(n)					\
169({						\
170    target_ulong __arg;				\
171    /* FIXME - handle get_user() failure */	\
172    get_user_ual(__arg, args + (n) * 4);	\
173    __arg;					\
174})
175#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
176uint32_t do_arm_semihosting(CPUState *env)
177{
178    target_ulong args;
179    char * s;
180    int nr;
181    uint32_t ret;
182    uint32_t len;
183#ifdef CONFIG_USER_ONLY
184    TaskState *ts = env->opaque;
185#else
186    CPUState *ts = env;
187#endif
188
189    nr = env->regs[0];
190    args = env->regs[1];
191    switch (nr) {
192    case SYS_OPEN:
193        if (!(s = lock_user_string(ARG(0))))
194            /* FIXME - should this error code be -TARGET_EFAULT ? */
195            return (uint32_t)-1;
196        if (ARG(1) >= 12)
197            return (uint32_t)-1;
198        if (strcmp(s, ":tt") == 0) {
199            if (ARG(1) < 4)
200                return STDIN_FILENO;
201            else
202                return STDOUT_FILENO;
203        }
204        if (use_gdb_syscalls()) {
205            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
206			   (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
207            return env->regs[0];
208        } else {
209            ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
210        }
211        unlock_user(s, ARG(0), 0);
212        return ret;
213    case SYS_CLOSE:
214        if (use_gdb_syscalls()) {
215            gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
216            return env->regs[0];
217        } else {
218            return set_swi_errno(ts, close(ARG(0)));
219        }
220    case SYS_WRITEC:
221        {
222          char c;
223
224          if (get_user_u8(c, args))
225              /* FIXME - should this error code be -TARGET_EFAULT ? */
226              return (uint32_t)-1;
227          /* Write to debug console.  stderr is near enough.  */
228          if (use_gdb_syscalls()) {
229                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
230                return env->regs[0];
231          } else {
232                return write(STDERR_FILENO, &c, 1);
233          }
234        }
235    case SYS_WRITE0:
236        if (!(s = lock_user_string(args)))
237            /* FIXME - should this error code be -TARGET_EFAULT ? */
238            return (uint32_t)-1;
239        len = strlen(s);
240        if (use_gdb_syscalls()) {
241            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
242            ret = env->regs[0];
243        } else {
244            ret = write(STDERR_FILENO, s, len);
245        }
246        unlock_user(s, args, 0);
247        return ret;
248    case SYS_WRITE:
249        len = ARG(2);
250        if (use_gdb_syscalls()) {
251            arm_semi_syscall_len = len;
252            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
253            return env->regs[0];
254        } else {
255            if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
256                /* FIXME - should this error code be -TARGET_EFAULT ? */
257                return (uint32_t)-1;
258            ret = set_swi_errno(ts, write(ARG(0), s, len));
259            unlock_user(s, ARG(1), 0);
260            if (ret == (uint32_t)-1)
261                return -1;
262            return len - ret;
263        }
264    case SYS_READ:
265        len = ARG(2);
266        if (use_gdb_syscalls()) {
267            arm_semi_syscall_len = len;
268            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
269            return env->regs[0];
270        } else {
271            if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
272                /* FIXME - should this error code be -TARGET_EFAULT ? */
273                return (uint32_t)-1;
274            do
275              ret = set_swi_errno(ts, read(ARG(0), s, len));
276            while (ret == -1 && errno == EINTR);
277            unlock_user(s, ARG(1), len);
278            if (ret == (uint32_t)-1)
279                return -1;
280            return len - ret;
281        }
282    case SYS_READC:
283       /* XXX: Read from debug cosole. Not implemented.  */
284        return 0;
285    case SYS_ISTTY:
286        if (use_gdb_syscalls()) {
287            gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
288            return env->regs[0];
289        } else {
290            return isatty(ARG(0));
291        }
292    case SYS_SEEK:
293        if (use_gdb_syscalls()) {
294            gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
295            return env->regs[0];
296        } else {
297            ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
298            if (ret == (uint32_t)-1)
299              return -1;
300            return 0;
301        }
302    case SYS_FLEN:
303        if (use_gdb_syscalls()) {
304            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
305			   ARG(0), env->regs[13]-64);
306            return env->regs[0];
307        } else {
308            struct stat buf;
309            ret = set_swi_errno(ts, fstat(ARG(0), &buf));
310            if (ret == (uint32_t)-1)
311                return -1;
312            return buf.st_size;
313        }
314    case SYS_TMPNAM:
315        /* XXX: Not implemented.  */
316        return -1;
317    case SYS_REMOVE:
318        if (use_gdb_syscalls()) {
319            gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
320            ret = env->regs[0];
321        } else {
322            if (!(s = lock_user_string(ARG(0))))
323                /* FIXME - should this error code be -TARGET_EFAULT ? */
324                return (uint32_t)-1;
325            ret =  set_swi_errno(ts, remove(s));
326            unlock_user(s, ARG(0), 0);
327        }
328        return ret;
329    case SYS_RENAME:
330        if (use_gdb_syscalls()) {
331            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
332                           ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
333            return env->regs[0];
334        } else {
335            char *s2;
336            s = lock_user_string(ARG(0));
337            s2 = lock_user_string(ARG(2));
338            if (!s || !s2)
339                /* FIXME - should this error code be -TARGET_EFAULT ? */
340                ret = (uint32_t)-1;
341            else
342                ret = set_swi_errno(ts, rename(s, s2));
343            if (s2)
344                unlock_user(s2, ARG(2), 0);
345            if (s)
346                unlock_user(s, ARG(0), 0);
347            return ret;
348        }
349    case SYS_CLOCK:
350        return clock() / (CLOCKS_PER_SEC / 100);
351    case SYS_TIME:
352        return set_swi_errno(ts, time(NULL));
353    case SYS_SYSTEM:
354        if (use_gdb_syscalls()) {
355            gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
356            return env->regs[0];
357        } else {
358            if (!(s = lock_user_string(ARG(0))))
359                /* FIXME - should this error code be -TARGET_EFAULT ? */
360                return (uint32_t)-1;
361            ret = set_swi_errno(ts, system(s));
362            unlock_user(s, ARG(0), 0);
363            return ret;
364        }
365    case SYS_ERRNO:
366#ifdef CONFIG_USER_ONLY
367        return ts->swi_errno;
368#else
369        return syscall_err;
370#endif
371    case SYS_GET_CMDLINE:
372#ifdef CONFIG_USER_ONLY
373        /* Build a commandline from the original argv.  */
374        {
375            char *arm_cmdline_buffer;
376            const char *host_cmdline_buffer;
377
378            unsigned int i;
379            unsigned int arm_cmdline_len = ARG(1);
380            unsigned int host_cmdline_len =
381                ts->info->arg_end-ts->info->arg_start;
382
383            if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) {
384                return -1; /* not enough space to store command line */
385            }
386
387            if (!host_cmdline_len) {
388                /* We special-case the "empty command line" case (argc==0).
389                   Just provide the terminating 0. */
390                arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0);
391                arm_cmdline_buffer[0] = 0;
392                unlock_user(arm_cmdline_buffer, ARG(0), 1);
393
394                /* Adjust the commandline length argument. */
395                SET_ARG(1, 0);
396                return 0;
397            }
398
399            /* lock the buffers on the ARM side */
400            arm_cmdline_buffer =
401                lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0);
402            host_cmdline_buffer =
403                lock_user(VERIFY_READ, ts->info->arg_start,
404                                       host_cmdline_len, 1);
405
406            if (arm_cmdline_buffer && host_cmdline_buffer)
407            {
408                /* the last argument is zero-terminated;
409                   no need for additional termination */
410                memcpy(arm_cmdline_buffer, host_cmdline_buffer,
411                       host_cmdline_len);
412
413                /* separate arguments by white spaces */
414                for (i = 0; i < host_cmdline_len-1; i++) {
415                    if (arm_cmdline_buffer[i] == 0) {
416                        arm_cmdline_buffer[i] = ' ';
417                    }
418                }
419
420                /* Adjust the commandline length argument. */
421                SET_ARG(1, host_cmdline_len-1);
422            }
423
424            /* Unlock the buffers on the ARM side.  */
425            unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len);
426            unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0);
427
428            /* Return success if we could return a commandline.  */
429            return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1;
430        }
431#else
432        return -1;
433#endif
434    case SYS_HEAPINFO:
435        {
436            uint32_t *ptr;
437            uint32_t limit;
438
439#ifdef CONFIG_USER_ONLY
440            /* Some C libraries assume the heap immediately follows .bss, so
441               allocate it using sbrk.  */
442            if (!ts->heap_limit) {
443                long ret;
444
445                ts->heap_base = do_brk(0);
446                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
447                /* Try a big heap, and reduce the size if that fails.  */
448                for (;;) {
449                    ret = do_brk(limit);
450                    if (ret != -1)
451                        break;
452                    limit = (ts->heap_base >> 1) + (limit >> 1);
453                }
454                ts->heap_limit = limit;
455            }
456
457            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
458                /* FIXME - should this error code be -TARGET_EFAULT ? */
459                return (uint32_t)-1;
460            ptr[0] = tswap32(ts->heap_base);
461            ptr[1] = tswap32(ts->heap_limit);
462            ptr[2] = tswap32(ts->stack_base);
463            ptr[3] = tswap32(0); /* Stack limit.  */
464            unlock_user(ptr, ARG(0), 16);
465#else
466            limit = ram_size;
467            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
468                /* FIXME - should this error code be -TARGET_EFAULT ? */
469                return (uint32_t)-1;
470            /* TODO: Make this use the limit of the loaded application.  */
471            ptr[0] = tswap32(limit / 2);
472            ptr[1] = tswap32(limit);
473            ptr[2] = tswap32(limit); /* Stack base */
474            ptr[3] = tswap32(0); /* Stack limit.  */
475            unlock_user(ptr, ARG(0), 16);
476#endif
477            return 0;
478        }
479    case SYS_EXIT:
480        //gdb_exit(env, 0);
481        exit(0);
482    default:
483        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
484        cpu_dump_state(env, stderr, fprintf, 0);
485        abort();
486    }
487}
488