]> Git Repo - qemu.git/blob - target-arm/arm-semi.c
Merge remote-tracking branch 'remotes/afaerber/tags/prep-for-2.0' 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 #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(CPUState *cs, target_ulong ret, target_ulong err)
126 {
127     ARMCPU *cpu = ARM_CPU(cs);
128     CPUARMState *env = &cpu->env;
129 #ifdef CONFIG_USER_ONLY
130     TaskState *ts = cs->opaque;
131 #endif
132
133     if (ret == (target_ulong)-1) {
134 #ifdef CONFIG_USER_ONLY
135         ts->swi_errno = err;
136 #else
137         syscall_err = err;
138 #endif
139         env->regs[0] = ret;
140     } else {
141         /* Fixup syscalls that use nonstardard return conventions.  */
142         switch (env->regs[0]) {
143         case TARGET_SYS_WRITE:
144         case TARGET_SYS_READ:
145             env->regs[0] = arm_semi_syscall_len - ret;
146             break;
147         case TARGET_SYS_SEEK:
148             env->regs[0] = 0;
149             break;
150         default:
151             env->regs[0] = ret;
152             break;
153         }
154     }
155 }
156
157 static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
158 {
159     ARMCPU *cpu = ARM_CPU(cs);
160     CPUARMState *env = &cpu->env;
161     /* The size is always stored in big-endian order, extract
162        the value. We assume the size always fit in 32 bits.  */
163     uint32_t size;
164     cpu_memory_rw_debug(cs, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
165     env->regs[0] = be32_to_cpu(size);
166 #ifdef CONFIG_USER_ONLY
167     ((TaskState *)cs->opaque)->swi_errno = err;
168 #else
169     syscall_err = err;
170 #endif
171 }
172
173 /* Read the input value from the argument block; fail the semihosting
174  * call if the memory read fails.
175  */
176 #define GET_ARG(n) do {                                 \
177     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
178         return (uint32_t)-1;                            \
179     }                                                   \
180 } while (0)
181
182 #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
183 uint32_t do_arm_semihosting(CPUARMState *env)
184 {
185     ARMCPU *cpu = arm_env_get_cpu(env);
186     CPUState *cs = CPU(cpu);
187     target_ulong args;
188     target_ulong arg0, arg1, arg2, arg3;
189     char * s;
190     int nr;
191     uint32_t ret;
192     uint32_t len;
193 #ifdef CONFIG_USER_ONLY
194     TaskState *ts = cs->opaque;
195 #else
196     CPUARMState *ts = env;
197 #endif
198
199     nr = env->regs[0];
200     args = env->regs[1];
201     switch (nr) {
202     case TARGET_SYS_OPEN:
203         GET_ARG(0);
204         GET_ARG(1);
205         GET_ARG(2);
206         s = lock_user_string(arg0);
207         if (!s) {
208             /* FIXME - should this error code be -TARGET_EFAULT ? */
209             return (uint32_t)-1;
210         }
211         if (arg1 >= 12) {
212             unlock_user(s, arg0, 0);
213             return (uint32_t)-1;
214         }
215         if (strcmp(s, ":tt") == 0) {
216             int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
217             unlock_user(s, arg0, 0);
218             return result_fileno;
219         }
220         if (use_gdb_syscalls()) {
221             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
222                            (int)arg2+1, gdb_open_modeflags[arg1]);
223             ret = env->regs[0];
224         } else {
225             ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
226         }
227         unlock_user(s, arg0, 0);
228         return ret;
229     case TARGET_SYS_CLOSE:
230         GET_ARG(0);
231         if (use_gdb_syscalls()) {
232             gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
233             return env->regs[0];
234         } else {
235             return set_swi_errno(ts, close(arg0));
236         }
237     case TARGET_SYS_WRITEC:
238         {
239           char c;
240
241           if (get_user_u8(c, args))
242               /* FIXME - should this error code be -TARGET_EFAULT ? */
243               return (uint32_t)-1;
244           /* Write to debug console.  stderr is near enough.  */
245           if (use_gdb_syscalls()) {
246                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
247                 return env->regs[0];
248           } else {
249                 return write(STDERR_FILENO, &c, 1);
250           }
251         }
252     case TARGET_SYS_WRITE0:
253         if (!(s = lock_user_string(args)))
254             /* FIXME - should this error code be -TARGET_EFAULT ? */
255             return (uint32_t)-1;
256         len = strlen(s);
257         if (use_gdb_syscalls()) {
258             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
259             ret = env->regs[0];
260         } else {
261             ret = write(STDERR_FILENO, s, len);
262         }
263         unlock_user(s, args, 0);
264         return ret;
265     case TARGET_SYS_WRITE:
266         GET_ARG(0);
267         GET_ARG(1);
268         GET_ARG(2);
269         len = arg2;
270         if (use_gdb_syscalls()) {
271             arm_semi_syscall_len = len;
272             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
273             return env->regs[0];
274         } else {
275             s = lock_user(VERIFY_READ, arg1, len, 1);
276             if (!s) {
277                 /* FIXME - should this error code be -TARGET_EFAULT ? */
278                 return (uint32_t)-1;
279             }
280             ret = set_swi_errno(ts, write(arg0, s, len));
281             unlock_user(s, arg1, 0);
282             if (ret == (uint32_t)-1)
283                 return -1;
284             return len - ret;
285         }
286     case TARGET_SYS_READ:
287         GET_ARG(0);
288         GET_ARG(1);
289         GET_ARG(2);
290         len = arg2;
291         if (use_gdb_syscalls()) {
292             arm_semi_syscall_len = len;
293             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
294             return env->regs[0];
295         } else {
296             s = lock_user(VERIFY_WRITE, arg1, len, 0);
297             if (!s) {
298                 /* FIXME - should this error code be -TARGET_EFAULT ? */
299                 return (uint32_t)-1;
300             }
301             do {
302                 ret = set_swi_errno(ts, read(arg0, s, len));
303             } while (ret == -1 && errno == EINTR);
304             unlock_user(s, arg1, len);
305             if (ret == (uint32_t)-1)
306                 return -1;
307             return len - ret;
308         }
309     case TARGET_SYS_READC:
310        /* XXX: Read from debug console. Not implemented.  */
311         return 0;
312     case TARGET_SYS_ISTTY:
313         GET_ARG(0);
314         if (use_gdb_syscalls()) {
315             gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
316             return env->regs[0];
317         } else {
318             return isatty(arg0);
319         }
320     case TARGET_SYS_SEEK:
321         GET_ARG(0);
322         GET_ARG(1);
323         if (use_gdb_syscalls()) {
324             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
325             return env->regs[0];
326         } else {
327             ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
328             if (ret == (uint32_t)-1)
329               return -1;
330             return 0;
331         }
332     case TARGET_SYS_FLEN:
333         GET_ARG(0);
334         if (use_gdb_syscalls()) {
335             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
336                            arg0, env->regs[13]-64);
337             return env->regs[0];
338         } else {
339             struct stat buf;
340             ret = set_swi_errno(ts, fstat(arg0, &buf));
341             if (ret == (uint32_t)-1)
342                 return -1;
343             return buf.st_size;
344         }
345     case TARGET_SYS_TMPNAM:
346         /* XXX: Not implemented.  */
347         return -1;
348     case TARGET_SYS_REMOVE:
349         GET_ARG(0);
350         GET_ARG(1);
351         if (use_gdb_syscalls()) {
352             gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
353             ret = env->regs[0];
354         } else {
355             s = lock_user_string(arg0);
356             if (!s) {
357                 /* FIXME - should this error code be -TARGET_EFAULT ? */
358                 return (uint32_t)-1;
359             }
360             ret =  set_swi_errno(ts, remove(s));
361             unlock_user(s, arg0, 0);
362         }
363         return ret;
364     case TARGET_SYS_RENAME:
365         GET_ARG(0);
366         GET_ARG(1);
367         GET_ARG(2);
368         GET_ARG(3);
369         if (use_gdb_syscalls()) {
370             gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
371                            arg0, (int)arg1+1, arg2, (int)arg3+1);
372             return env->regs[0];
373         } else {
374             char *s2;
375             s = lock_user_string(arg0);
376             s2 = lock_user_string(arg2);
377             if (!s || !s2)
378                 /* FIXME - should this error code be -TARGET_EFAULT ? */
379                 ret = (uint32_t)-1;
380             else
381                 ret = set_swi_errno(ts, rename(s, s2));
382             if (s2)
383                 unlock_user(s2, arg2, 0);
384             if (s)
385                 unlock_user(s, arg0, 0);
386             return ret;
387         }
388     case TARGET_SYS_CLOCK:
389         return clock() / (CLOCKS_PER_SEC / 100);
390     case TARGET_SYS_TIME:
391         return set_swi_errno(ts, time(NULL));
392     case TARGET_SYS_SYSTEM:
393         GET_ARG(0);
394         GET_ARG(1);
395         if (use_gdb_syscalls()) {
396             gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
397             return env->regs[0];
398         } else {
399             s = lock_user_string(arg0);
400             if (!s) {
401                 /* FIXME - should this error code be -TARGET_EFAULT ? */
402                 return (uint32_t)-1;
403             }
404             ret = set_swi_errno(ts, system(s));
405             unlock_user(s, arg0, 0);
406             return ret;
407         }
408     case TARGET_SYS_ERRNO:
409 #ifdef CONFIG_USER_ONLY
410         return ts->swi_errno;
411 #else
412         return syscall_err;
413 #endif
414     case TARGET_SYS_GET_CMDLINE:
415         {
416             /* Build a command-line from the original argv.
417              *
418              * The inputs are:
419              *     * arg0, pointer to a buffer of at least the size
420              *               specified in arg1.
421              *     * arg1, size of the buffer pointed to by arg0 in
422              *               bytes.
423              *
424              * The outputs are:
425              *     * arg0, pointer to null-terminated string of the
426              *               command line.
427              *     * arg1, length of the string pointed to by arg0.
428              */
429
430             char *output_buffer;
431             size_t input_size;
432             size_t output_size;
433             int status = 0;
434             GET_ARG(0);
435             GET_ARG(1);
436             input_size = arg1;
437             /* Compute the size of the output string.  */
438 #if !defined(CONFIG_USER_ONLY)
439             output_size = strlen(ts->boot_info->kernel_filename)
440                         + 1  /* Separating space.  */
441                         + strlen(ts->boot_info->kernel_cmdline)
442                         + 1; /* Terminating null byte.  */
443 #else
444             unsigned int i;
445
446             output_size = ts->info->arg_end - ts->info->arg_start;
447             if (!output_size) {
448                 /* We special-case the "empty command line" case (argc==0).
449                    Just provide the terminating 0. */
450                 output_size = 1;
451             }
452 #endif
453
454             if (output_size > input_size) {
455                  /* Not enough space to store command-line arguments.  */
456                 return -1;
457             }
458
459             /* Adjust the command-line length.  */
460             if (SET_ARG(1, output_size - 1)) {
461                 /* Couldn't write back to argument block */
462                 return -1;
463             }
464
465             /* Lock the buffer on the ARM side.  */
466             output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
467             if (!output_buffer) {
468                 return -1;
469             }
470
471             /* Copy the command-line arguments.  */
472 #if !defined(CONFIG_USER_ONLY)
473             pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
474             pstrcat(output_buffer, output_size, " ");
475             pstrcat(output_buffer, output_size, ts->boot_info->kernel_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         gdb_exit(env, 0);
555         exit(0);
556     default:
557         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
558         cpu_dump_state(cs, stderr, fprintf, 0);
559         abort();
560     }
561 }
This page took 0.095367 seconds and 4 git commands to generate.