Add qemu 2.4.0
[kvmfornfv.git] / qemu / 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 #if !defined(CONFIG_USER_ONLY)
440             const char *cmdline;
441 #endif
442             GET_ARG(0);
443             GET_ARG(1);
444             input_size = arg1;
445             /* Compute the size of the output string.  */
446 #if !defined(CONFIG_USER_ONLY)
447             cmdline = semihosting_get_cmdline();
448             if (cmdline == NULL) {
449                 cmdline = ""; /* Default to an empty line. */
450             }
451             output_size = strlen(cmdline) + 1; /* Count terminating 0. */
452 #else
453             unsigned int i;
454
455             output_size = ts->info->arg_end - ts->info->arg_start;
456             if (!output_size) {
457                 /* We special-case the "empty command line" case (argc==0).
458                    Just provide the terminating 0. */
459                 output_size = 1;
460             }
461 #endif
462
463             if (output_size > input_size) {
464                  /* Not enough space to store command-line arguments.  */
465                 return -1;
466             }
467
468             /* Adjust the command-line length.  */
469             if (SET_ARG(1, output_size - 1)) {
470                 /* Couldn't write back to argument block */
471                 return -1;
472             }
473
474             /* Lock the buffer on the ARM side.  */
475             output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
476             if (!output_buffer) {
477                 return -1;
478             }
479
480             /* Copy the command-line arguments.  */
481 #if !defined(CONFIG_USER_ONLY)
482             pstrcpy(output_buffer, output_size, cmdline);
483 #else
484             if (output_size == 1) {
485                 /* Empty command-line.  */
486                 output_buffer[0] = '\0';
487                 goto out;
488             }
489
490             if (copy_from_user(output_buffer, ts->info->arg_start,
491                                output_size)) {
492                 status = -1;
493                 goto out;
494             }
495
496             /* Separate arguments by white spaces.  */
497             for (i = 0; i < output_size - 1; i++) {
498                 if (output_buffer[i] == 0) {
499                     output_buffer[i] = ' ';
500                 }
501             }
502         out:
503 #endif
504             /* Unlock the buffer on the ARM side.  */
505             unlock_user(output_buffer, arg0, output_size);
506
507             return status;
508         }
509     case TARGET_SYS_HEAPINFO:
510         {
511             uint32_t *ptr;
512             uint32_t limit;
513             GET_ARG(0);
514
515 #ifdef CONFIG_USER_ONLY
516             /* Some C libraries assume the heap immediately follows .bss, so
517                allocate it using sbrk.  */
518             if (!ts->heap_limit) {
519                 abi_ulong ret;
520
521                 ts->heap_base = do_brk(0);
522                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
523                 /* Try a big heap, and reduce the size if that fails.  */
524                 for (;;) {
525                     ret = do_brk(limit);
526                     if (ret >= limit) {
527                         break;
528                     }
529                     limit = (ts->heap_base >> 1) + (limit >> 1);
530                 }
531                 ts->heap_limit = limit;
532             }
533
534             ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
535             if (!ptr) {
536                 /* FIXME - should this error code be -TARGET_EFAULT ? */
537                 return (uint32_t)-1;
538             }
539             ptr[0] = tswap32(ts->heap_base);
540             ptr[1] = tswap32(ts->heap_limit);
541             ptr[2] = tswap32(ts->stack_base);
542             ptr[3] = tswap32(0); /* Stack limit.  */
543             unlock_user(ptr, arg0, 16);
544 #else
545             limit = ram_size;
546             ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
547             if (!ptr) {
548                 /* FIXME - should this error code be -TARGET_EFAULT ? */
549                 return (uint32_t)-1;
550             }
551             /* TODO: Make this use the limit of the loaded application.  */
552             ptr[0] = tswap32(limit / 2);
553             ptr[1] = tswap32(limit);
554             ptr[2] = tswap32(limit); /* Stack base */
555             ptr[3] = tswap32(0); /* Stack limit.  */
556             unlock_user(ptr, arg0, 16);
557 #endif
558             return 0;
559         }
560     case TARGET_SYS_EXIT:
561         /* ARM specifies only Stopped_ApplicationExit as normal
562          * exit, everything else is considered an error */
563         ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
564         gdb_exit(env, ret);
565         exit(ret);
566     default:
567         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
568         cpu_dump_state(cs, stderr, fprintf, 0);
569         abort();
570     }
571 }