• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
set_swi_errno(TaskState * ts,uint32_t code)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
set_swi_errno(CPUARMState * env,uint32_t code)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 
arm_semi_cb(CPUState * cpu,target_ulong ret,target_ulong err)125 static void arm_semi_cb(CPUState *cpu, target_ulong ret, target_ulong err)
126 {
127     CPUARMState *env = cpu->env_ptr;
128 #ifdef CONFIG_USER_ONLY
129     TaskState *ts = env->opaque;
130 #endif
131 
132     if (ret == (target_ulong)-1) {
133 #ifdef CONFIG_USER_ONLY
134         ts->swi_errno = err;
135 #else
136 	syscall_err = err;
137 #endif
138         env->regs[0] = ret;
139     } else {
140         /* Fixup syscalls that use nonstardard return conventions.  */
141         switch (env->regs[0]) {
142         case TARGET_SYS_WRITE:
143         case TARGET_SYS_READ:
144             env->regs[0] = arm_semi_syscall_len - ret;
145             break;
146         case TARGET_SYS_SEEK:
147             env->regs[0] = 0;
148             break;
149         default:
150             env->regs[0] = ret;
151             break;
152         }
153     }
154 }
155 
arm_semi_flen_cb(CPUState * cpu,target_ulong ret,target_ulong err)156 static void arm_semi_flen_cb(CPUState *cpu, target_ulong ret, target_ulong err)
157 {
158     /* The size is always stored in big-endian order, extract
159        the value. We assume the size always fit in 32 bits.  */
160     CPUARMState *env = cpu->env_ptr;
161     uint32_t size;
162     cpu_memory_rw_debug(cpu, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
163     env->regs[0] = be32_to_cpu(size);
164 #ifdef CONFIG_USER_ONLY
165     ((TaskState *)env->opaque)->swi_errno = err;
166 #else
167     syscall_err = err;
168 #endif
169 }
170 
171 /* Read the input value from the argument block; fail the semihosting
172  * call if the memory read fails.
173  */
174 #define GET_ARG(n) do {                                 \
175     if (get_user_ual(arg ## n, args + (n) * 4)) {       \
176         return (uint32_t)-1;                            \
177     }                                                   \
178 } while (0)
179 
180 #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
do_arm_semihosting(CPUARMState * env)181 uint32_t do_arm_semihosting(CPUARMState *env)
182 {
183     target_ulong args;
184     target_ulong arg0, arg1, arg2, arg3;
185     char * s;
186     int nr;
187     uint32_t ret;
188     uint32_t len;
189 #ifdef CONFIG_USER_ONLY
190     TaskState *ts = env->opaque;
191 #else
192     CPUARMState *ts = env;
193 #endif
194 
195     nr = env->regs[0];
196     args = env->regs[1];
197     switch (nr) {
198     case TARGET_SYS_OPEN:
199         GET_ARG(0);
200         GET_ARG(1);
201         GET_ARG(2);
202         s = lock_user_string(arg0);
203         if (!s) {
204             /* FIXME - should this error code be -TARGET_EFAULT ? */
205             return (uint32_t)-1;
206         }
207         if (arg1 >= 12) {
208             unlock_user(s, arg0, 0);
209             return (uint32_t)-1;
210         }
211         if (strcmp(s, ":tt") == 0) {
212             int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
213             unlock_user(s, arg0, 0);
214             return result_fileno;
215         }
216         if (use_gdb_syscalls()) {
217             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
218                            (int)arg2+1, gdb_open_modeflags[arg1]);
219             ret = env->regs[0];
220         } else {
221             ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
222         }
223         unlock_user(s, arg0, 0);
224         return ret;
225     case TARGET_SYS_CLOSE:
226         GET_ARG(0);
227         if (use_gdb_syscalls()) {
228             gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
229             return env->regs[0];
230         } else {
231             return set_swi_errno(ts, close(arg0));
232         }
233     case TARGET_SYS_WRITEC:
234         {
235           char c;
236 
237           if (get_user_u8(c, args))
238               /* FIXME - should this error code be -TARGET_EFAULT ? */
239               return (uint32_t)-1;
240           /* Write to debug console.  stderr is near enough.  */
241           if (use_gdb_syscalls()) {
242                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
243                 return env->regs[0];
244           } else {
245                 return write(STDERR_FILENO, &c, 1);
246           }
247         }
248     case TARGET_SYS_WRITE0:
249         if (!(s = lock_user_string(args)))
250             /* FIXME - should this error code be -TARGET_EFAULT ? */
251             return (uint32_t)-1;
252         len = strlen(s);
253         if (use_gdb_syscalls()) {
254             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
255             ret = env->regs[0];
256         } else {
257             ret = write(STDERR_FILENO, s, len);
258         }
259         unlock_user(s, args, 0);
260         return ret;
261     case TARGET_SYS_WRITE:
262         GET_ARG(0);
263         GET_ARG(1);
264         GET_ARG(2);
265         len = arg2;
266         if (use_gdb_syscalls()) {
267             arm_semi_syscall_len = len;
268             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
269             return env->regs[0];
270         } else {
271             s = lock_user(VERIFY_READ, arg1, len, 1);
272             if (!s) {
273                 /* FIXME - should this error code be -TARGET_EFAULT ? */
274                 return (uint32_t)-1;
275             }
276             ret = set_swi_errno(ts, write(arg0, s, len));
277             unlock_user(s, arg1, 0);
278             if (ret == (uint32_t)-1)
279                 return -1;
280             return len - ret;
281         }
282     case TARGET_SYS_READ:
283         GET_ARG(0);
284         GET_ARG(1);
285         GET_ARG(2);
286         len = arg2;
287         if (use_gdb_syscalls()) {
288             arm_semi_syscall_len = len;
289             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
290             return env->regs[0];
291         } else {
292             s = lock_user(VERIFY_WRITE, arg1, len, 0);
293             if (!s) {
294                 /* FIXME - should this error code be -TARGET_EFAULT ? */
295                 return (uint32_t)-1;
296             }
297             do {
298                 ret = set_swi_errno(ts, read(arg0, s, len));
299             } while (ret == -1 && errno == EINTR);
300             unlock_user(s, arg1, len);
301             if (ret == (uint32_t)-1)
302                 return -1;
303             return len - ret;
304         }
305     case TARGET_SYS_READC:
306        /* XXX: Read from debug console. Not implemented.  */
307         return 0;
308     case TARGET_SYS_ISTTY:
309         GET_ARG(0);
310         if (use_gdb_syscalls()) {
311             gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
312             return env->regs[0];
313         } else {
314             return isatty(arg0);
315         }
316     case TARGET_SYS_SEEK:
317         GET_ARG(0);
318         GET_ARG(1);
319         if (use_gdb_syscalls()) {
320             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
321             return env->regs[0];
322         } else {
323             ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
324             if (ret == (uint32_t)-1)
325               return -1;
326             return 0;
327         }
328     case TARGET_SYS_FLEN:
329         GET_ARG(0);
330         if (use_gdb_syscalls()) {
331             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
332                            arg0, env->regs[13]-64);
333             return env->regs[0];
334         } else {
335             struct stat buf;
336             ret = set_swi_errno(ts, fstat(arg0, &buf));
337             if (ret == (uint32_t)-1)
338                 return -1;
339             return buf.st_size;
340         }
341     case TARGET_SYS_TMPNAM:
342         /* XXX: Not implemented.  */
343         return -1;
344     case TARGET_SYS_REMOVE:
345         GET_ARG(0);
346         GET_ARG(1);
347         if (use_gdb_syscalls()) {
348             gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
349             ret = env->regs[0];
350         } else {
351             s = lock_user_string(arg0);
352             if (!s) {
353                 /* FIXME - should this error code be -TARGET_EFAULT ? */
354                 return (uint32_t)-1;
355             }
356             ret =  set_swi_errno(ts, remove(s));
357             unlock_user(s, arg0, 0);
358         }
359         return ret;
360     case TARGET_SYS_RENAME:
361         GET_ARG(0);
362         GET_ARG(1);
363         GET_ARG(2);
364         GET_ARG(3);
365         if (use_gdb_syscalls()) {
366             gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
367                            arg0, (int)arg1+1, arg2, (int)arg3+1);
368             return env->regs[0];
369         } else {
370             char *s2;
371             s = lock_user_string(arg0);
372             s2 = lock_user_string(arg2);
373             if (!s || !s2)
374                 /* FIXME - should this error code be -TARGET_EFAULT ? */
375                 ret = (uint32_t)-1;
376             else
377                 ret = set_swi_errno(ts, rename(s, s2));
378             if (s2)
379                 unlock_user(s2, arg2, 0);
380             if (s)
381                 unlock_user(s, arg0, 0);
382             return ret;
383         }
384     case TARGET_SYS_CLOCK:
385         return clock() / (CLOCKS_PER_SEC / 100);
386     case TARGET_SYS_TIME:
387         return set_swi_errno(ts, time(NULL));
388     case TARGET_SYS_SYSTEM:
389         GET_ARG(0);
390         GET_ARG(1);
391         if (use_gdb_syscalls()) {
392             gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
393             return env->regs[0];
394         } else {
395             s = lock_user_string(arg0);
396             if (!s) {
397                 /* FIXME - should this error code be -TARGET_EFAULT ? */
398                 return (uint32_t)-1;
399             }
400             ret = set_swi_errno(ts, system(s));
401             unlock_user(s, arg0, 0);
402             return ret;
403         }
404     case TARGET_SYS_ERRNO:
405 #ifdef CONFIG_USER_ONLY
406         return ts->swi_errno;
407 #else
408         return syscall_err;
409 #endif
410     case TARGET_SYS_GET_CMDLINE:
411         {
412             /* Build a command-line from the original argv.
413              *
414              * The inputs are:
415              *     * arg0, pointer to a buffer of at least the size
416              *               specified in arg1.
417              *     * arg1, size of the buffer pointed to by arg0 in
418              *               bytes.
419              *
420              * The outputs are:
421              *     * arg0, pointer to null-terminated string of the
422              *               command line.
423              *     * arg1, length of the string pointed to by arg0.
424              */
425 
426             char *output_buffer;
427             size_t input_size;
428             size_t output_size;
429             int status = 0;
430             GET_ARG(0);
431             GET_ARG(1);
432             input_size = arg1;
433             /* Compute the size of the output string.  */
434 #if !defined(CONFIG_USER_ONLY)
435             output_size = strlen(ts->boot_info->kernel_filename)
436                         + 1  /* Separating space.  */
437                         + strlen(ts->boot_info->kernel_cmdline)
438                         + 1; /* Terminating null byte.  */
439 #else
440             unsigned int i;
441 
442             output_size = ts->info->arg_end - ts->info->arg_start;
443             if (!output_size) {
444                 /* We special-case the "empty command line" case (argc==0).
445                    Just provide the terminating 0. */
446                 output_size = 1;
447             }
448 #endif
449 
450             if (output_size > input_size) {
451                  /* Not enough space to store command-line arguments.  */
452                 return -1;
453             }
454 
455             /* Adjust the command-line length.  */
456             if (SET_ARG(1, output_size - 1)) {
457                 /* Couldn't write back to argument block */
458                 return -1;
459             }
460 
461             /* Lock the buffer on the ARM side.  */
462             output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
463             if (!output_buffer) {
464                 return -1;
465             }
466 
467             /* Copy the command-line arguments.  */
468 #if !defined(CONFIG_USER_ONLY)
469             pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
470             pstrcat(output_buffer, output_size, " ");
471             pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
472 #else
473             if (output_size == 1) {
474                 /* Empty command-line.  */
475                 output_buffer[0] = '\0';
476                 goto out;
477             }
478 
479             if (copy_from_user(output_buffer, ts->info->arg_start,
480                                output_size)) {
481                 status = -1;
482                 goto out;
483             }
484 
485             /* Separate arguments by white spaces.  */
486             for (i = 0; i < output_size - 1; i++) {
487                 if (output_buffer[i] == 0) {
488                     output_buffer[i] = ' ';
489                 }
490             }
491         out:
492 #endif
493             /* Unlock the buffer on the ARM side.  */
494             unlock_user(output_buffer, arg0, output_size);
495 
496             return status;
497         }
498     case TARGET_SYS_HEAPINFO:
499         {
500             uint32_t *ptr;
501             uint32_t limit;
502             GET_ARG(0);
503 
504 #ifdef CONFIG_USER_ONLY
505             /* Some C libraries assume the heap immediately follows .bss, so
506                allocate it using sbrk.  */
507             if (!ts->heap_limit) {
508                 abi_ulong ret;
509 
510                 ts->heap_base = do_brk(0);
511                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
512                 /* Try a big heap, and reduce the size if that fails.  */
513                 for (;;) {
514                     ret = do_brk(limit);
515                     if (ret >= limit) {
516                         break;
517                     }
518                     limit = (ts->heap_base >> 1) + (limit >> 1);
519                 }
520                 ts->heap_limit = limit;
521             }
522 
523             ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
524             if (!ptr) {
525                 /* FIXME - should this error code be -TARGET_EFAULT ? */
526                 return (uint32_t)-1;
527             }
528             ptr[0] = tswap32(ts->heap_base);
529             ptr[1] = tswap32(ts->heap_limit);
530             ptr[2] = tswap32(ts->stack_base);
531             ptr[3] = tswap32(0); /* Stack limit.  */
532             unlock_user(ptr, arg0, 16);
533 #else
534             limit = ram_size;
535             ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
536             if (!ptr) {
537                 /* FIXME - should this error code be -TARGET_EFAULT ? */
538                 return (uint32_t)-1;
539             }
540             /* TODO: Make this use the limit of the loaded application.  */
541             ptr[0] = tswap32(limit / 2);
542             ptr[1] = tswap32(limit);
543             ptr[2] = tswap32(limit); /* Stack base */
544             ptr[3] = tswap32(0); /* Stack limit.  */
545             unlock_user(ptr, arg0, 16);
546 #endif
547             return 0;
548         }
549     case TARGET_SYS_EXIT:
550         //gdb_exit(env, 0);
551         exit(0);
552     default:
553         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
554         cpu_dump_state(ENV_GET_CPU(env), stderr, fprintf, 0);
555         abort();
556     }
557 }
558