]> Git Repo - qemu.git/blob - target-arm/arm-semi.c
arm: fix location of some include files
[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 #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
73 static 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
88 static 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
104 static 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
111 static 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
119 static target_ulong arm_semi_syscall_len;
120
121 #if !defined(CONFIG_USER_ONLY)
122 static target_ulong syscall_err;
123 #endif
124
125 static 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
155 static 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)
179 uint32_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 }
This page took 0.055012 seconds and 4 git commands to generate.