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