• 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, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20  *  MA 02110-1301, USA.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <time.h>
30 
31 #include "cpu.h"
32 #ifdef CONFIG_USER_ONLY
33 #include "qemu.h"
34 
35 #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
36 #else
37 #include "qemu-common.h"
38 #include "sysemu.h"
39 #include "gdbstub.h"
40 #endif
41 
42 #define SYS_OPEN        0x01
43 #define SYS_CLOSE       0x02
44 #define SYS_WRITEC      0x03
45 #define SYS_WRITE0      0x04
46 #define SYS_WRITE       0x05
47 #define SYS_READ        0x06
48 #define SYS_READC       0x07
49 #define SYS_ISTTY       0x09
50 #define SYS_SEEK        0x0a
51 #define SYS_FLEN        0x0c
52 #define SYS_TMPNAM      0x0d
53 #define SYS_REMOVE      0x0e
54 #define SYS_RENAME      0x0f
55 #define SYS_CLOCK       0x10
56 #define SYS_TIME        0x11
57 #define SYS_SYSTEM      0x12
58 #define SYS_ERRNO       0x13
59 #define SYS_GET_CMDLINE 0x15
60 #define SYS_HEAPINFO    0x16
61 #define SYS_EXIT        0x18
62 
63 #ifndef O_BINARY
64 #define O_BINARY 0
65 #endif
66 
67 #define GDB_O_RDONLY  0x000
68 #define GDB_O_WRONLY  0x001
69 #define GDB_O_RDWR    0x002
70 #define GDB_O_APPEND  0x008
71 #define GDB_O_CREAT   0x200
72 #define GDB_O_TRUNC   0x400
73 #define GDB_O_BINARY  0
74 
75 static int gdb_open_modeflags[12] = {
76     GDB_O_RDONLY,
77     GDB_O_RDONLY | GDB_O_BINARY,
78     GDB_O_RDWR,
79     GDB_O_RDWR | GDB_O_BINARY,
80     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
81     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
82     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
83     GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
84     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
85     GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
86     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
87     GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
88 };
89 
90 static int open_modeflags[12] = {
91     O_RDONLY,
92     O_RDONLY | O_BINARY,
93     O_RDWR,
94     O_RDWR | O_BINARY,
95     O_WRONLY | O_CREAT | O_TRUNC,
96     O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
97     O_RDWR | O_CREAT | O_TRUNC,
98     O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
99     O_WRONLY | O_CREAT | O_APPEND,
100     O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
101     O_RDWR | O_CREAT | O_APPEND,
102     O_RDWR | O_CREAT | O_APPEND | O_BINARY
103 };
104 
105 #ifdef CONFIG_USER_ONLY
set_swi_errno(TaskState * ts,uint32_t code)106 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
107 {
108     if (code == (uint32_t)-1)
109         ts->swi_errno = errno;
110     return code;
111 }
112 #else
set_swi_errno(CPUState * env,uint32_t code)113 static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
114 {
115     return code;
116 }
117 
118 #include "softmmu-semi.h"
119 #endif
120 
121 static target_ulong arm_semi_syscall_len;
122 
123 #if !defined(CONFIG_USER_ONLY)
124 static target_ulong syscall_err;
125 #endif
126 
arm_semi_cb(CPUState * env,target_ulong ret,target_ulong err)127 static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
128 {
129 #ifdef CONFIG_USER_ONLY
130     TaskState *ts = env->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 SYS_WRITE:
144         case SYS_READ:
145             env->regs[0] = arm_semi_syscall_len - ret;
146             break;
147         case SYS_SEEK:
148             env->regs[0] = 0;
149             break;
150         default:
151             env->regs[0] = ret;
152             break;
153         }
154     }
155 }
156 
arm_semi_flen_cb(CPUState * env,target_ulong ret,target_ulong err)157 static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
158 {
159     /* The size is always stored in big-endian order, extract
160        the value. We assume the size always fit in 32 bits.  */
161     uint32_t size;
162     cpu_memory_rw_debug(env, 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 #define ARG(n)					\
172 ({						\
173     target_ulong __arg;				\
174     /* FIXME - handle get_user() failure */	\
175     get_user_ual(__arg, args + (n) * 4);	\
176     __arg;					\
177 })
178 #define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
do_arm_semihosting(CPUState * env)179 uint32_t do_arm_semihosting(CPUState *env)
180 {
181     target_ulong args;
182     char * s;
183     int nr;
184     uint32_t ret;
185     uint32_t len;
186 #ifdef CONFIG_USER_ONLY
187     TaskState *ts = env->opaque;
188 #else
189     CPUState *ts = env;
190 #endif
191 
192     nr = env->regs[0];
193     args = env->regs[1];
194     switch (nr) {
195     case SYS_OPEN:
196         if (!(s = lock_user_string(ARG(0))))
197             /* FIXME - should this error code be -TARGET_EFAULT ? */
198             return (uint32_t)-1;
199         if (ARG(1) >= 12)
200             return (uint32_t)-1;
201         if (strcmp(s, ":tt") == 0) {
202             if (ARG(1) < 4)
203                 return STDIN_FILENO;
204             else
205                 return STDOUT_FILENO;
206         }
207         if (use_gdb_syscalls()) {
208             gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
209 			   (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
210             return env->regs[0];
211         } else {
212             ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
213         }
214         unlock_user(s, ARG(0), 0);
215         return ret;
216     case SYS_CLOSE:
217         if (use_gdb_syscalls()) {
218             gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
219             return env->regs[0];
220         } else {
221             return set_swi_errno(ts, close(ARG(0)));
222         }
223     case SYS_WRITEC:
224         {
225           char c;
226 
227           if (get_user_u8(c, args))
228               /* FIXME - should this error code be -TARGET_EFAULT ? */
229               return (uint32_t)-1;
230           /* Write to debug console.  stderr is near enough.  */
231           if (use_gdb_syscalls()) {
232                 gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
233                 return env->regs[0];
234           } else {
235                 return write(STDERR_FILENO, &c, 1);
236           }
237         }
238     case SYS_WRITE0:
239         if (!(s = lock_user_string(args)))
240             /* FIXME - should this error code be -TARGET_EFAULT ? */
241             return (uint32_t)-1;
242         len = strlen(s);
243         if (use_gdb_syscalls()) {
244             gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
245             ret = env->regs[0];
246         } else {
247             ret = write(STDERR_FILENO, s, len);
248         }
249         unlock_user(s, args, 0);
250         return ret;
251     case SYS_WRITE:
252         len = ARG(2);
253         if (use_gdb_syscalls()) {
254             arm_semi_syscall_len = len;
255             gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
256             return env->regs[0];
257         } else {
258             if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
259                 /* FIXME - should this error code be -TARGET_EFAULT ? */
260                 return (uint32_t)-1;
261             ret = set_swi_errno(ts, write(ARG(0), s, len));
262             unlock_user(s, ARG(1), 0);
263             if (ret == (uint32_t)-1)
264                 return -1;
265             return len - ret;
266         }
267     case SYS_READ:
268         len = ARG(2);
269         if (use_gdb_syscalls()) {
270             arm_semi_syscall_len = len;
271             gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
272             return env->regs[0];
273         } else {
274             if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
275                 /* FIXME - should this error code be -TARGET_EFAULT ? */
276                 return (uint32_t)-1;
277             do
278               ret = set_swi_errno(ts, read(ARG(0), s, len));
279             while (ret == -1 && errno == EINTR);
280             unlock_user(s, ARG(1), len);
281             if (ret == (uint32_t)-1)
282                 return -1;
283             return len - ret;
284         }
285     case SYS_READC:
286        /* XXX: Read from debug cosole. Not implemented.  */
287         return 0;
288     case SYS_ISTTY:
289         if (use_gdb_syscalls()) {
290             gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
291             return env->regs[0];
292         } else {
293             return isatty(ARG(0));
294         }
295     case SYS_SEEK:
296         if (use_gdb_syscalls()) {
297             gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
298             return env->regs[0];
299         } else {
300             ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
301             if (ret == (uint32_t)-1)
302               return -1;
303             return 0;
304         }
305     case SYS_FLEN:
306         if (use_gdb_syscalls()) {
307             gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
308 			   ARG(0), env->regs[13]-64);
309             return env->regs[0];
310         } else {
311             struct stat buf;
312             ret = set_swi_errno(ts, fstat(ARG(0), &buf));
313             if (ret == (uint32_t)-1)
314                 return -1;
315             return buf.st_size;
316         }
317     case SYS_TMPNAM:
318         /* XXX: Not implemented.  */
319         return -1;
320     case SYS_REMOVE:
321         if (use_gdb_syscalls()) {
322             gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
323             ret = env->regs[0];
324         } else {
325             if (!(s = lock_user_string(ARG(0))))
326                 /* FIXME - should this error code be -TARGET_EFAULT ? */
327                 return (uint32_t)-1;
328             ret =  set_swi_errno(ts, remove(s));
329             unlock_user(s, ARG(0), 0);
330         }
331         return ret;
332     case SYS_RENAME:
333         if (use_gdb_syscalls()) {
334             gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
335                            ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
336             return env->regs[0];
337         } else {
338             char *s2;
339             s = lock_user_string(ARG(0));
340             s2 = lock_user_string(ARG(2));
341             if (!s || !s2)
342                 /* FIXME - should this error code be -TARGET_EFAULT ? */
343                 ret = (uint32_t)-1;
344             else
345                 ret = set_swi_errno(ts, rename(s, s2));
346             if (s2)
347                 unlock_user(s2, ARG(2), 0);
348             if (s)
349                 unlock_user(s, ARG(0), 0);
350             return ret;
351         }
352     case SYS_CLOCK:
353         return clock() / (CLOCKS_PER_SEC / 100);
354     case SYS_TIME:
355         return set_swi_errno(ts, time(NULL));
356     case SYS_SYSTEM:
357         if (use_gdb_syscalls()) {
358             gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
359             return env->regs[0];
360         } else {
361             if (!(s = lock_user_string(ARG(0))))
362                 /* FIXME - should this error code be -TARGET_EFAULT ? */
363                 return (uint32_t)-1;
364             ret = set_swi_errno(ts, system(s));
365             unlock_user(s, ARG(0), 0);
366             return ret;
367         }
368     case SYS_ERRNO:
369 #ifdef CONFIG_USER_ONLY
370         return ts->swi_errno;
371 #else
372         return syscall_err;
373 #endif
374     case SYS_GET_CMDLINE:
375 #ifdef CONFIG_USER_ONLY
376         /* Build a commandline from the original argv.  */
377         {
378             char **arg = ts->info->host_argv;
379             int len = ARG(1);
380             /* lock the buffer on the ARM side */
381             char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
382 
383             if (!cmdline_buffer)
384                 /* FIXME - should this error code be -TARGET_EFAULT ? */
385                 return (uint32_t)-1;
386 
387             s = cmdline_buffer;
388             while (*arg && len > 2) {
389                 int n = strlen(*arg);
390 
391                 if (s != cmdline_buffer) {
392                     *(s++) = ' ';
393                     len--;
394                 }
395                 if (n >= len)
396                     n = len - 1;
397                 memcpy(s, *arg, n);
398                 s += n;
399                 len -= n;
400                 arg++;
401             }
402             /* Null terminate the string.  */
403             *s = 0;
404             len = s - cmdline_buffer;
405 
406             /* Unlock the buffer on the ARM side.  */
407             unlock_user(cmdline_buffer, ARG(0), len);
408 
409             /* Adjust the commandline length argument.  */
410             SET_ARG(1, len);
411 
412             /* Return success if commandline fit into buffer.  */
413             return *arg ? -1 : 0;
414         }
415 #else
416       return -1;
417 #endif
418     case SYS_HEAPINFO:
419         {
420             uint32_t *ptr;
421             uint32_t limit;
422 
423 #ifdef CONFIG_USER_ONLY
424             /* Some C libraries assume the heap immediately follows .bss, so
425                allocate it using sbrk.  */
426             if (!ts->heap_limit) {
427                 long ret;
428 
429                 ts->heap_base = do_brk(0);
430                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
431                 /* Try a big heap, and reduce the size if that fails.  */
432                 for (;;) {
433                     ret = do_brk(limit);
434                     if (ret != -1)
435                         break;
436                     limit = (ts->heap_base >> 1) + (limit >> 1);
437                 }
438                 ts->heap_limit = limit;
439             }
440 
441             if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
442                 /* FIXME - should this error code be -TARGET_EFAULT ? */
443                 return (uint32_t)-1;
444             ptr[0] = tswap32(ts->heap_base);
445             ptr[1] = tswap32(ts->heap_limit);
446             ptr[2] = tswap32(ts->stack_base);
447             ptr[3] = tswap32(0); /* Stack limit.  */
448             unlock_user(ptr, ARG(0), 16);
449 #else
450             limit = ram_size;
451             if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
452                 /* FIXME - should this error code be -TARGET_EFAULT ? */
453                 return (uint32_t)-1;
454             /* TODO: Make this use the limit of the loaded application.  */
455             ptr[0] = tswap32(limit / 2);
456             ptr[1] = tswap32(limit);
457             ptr[2] = tswap32(limit); /* Stack base */
458             ptr[3] = tswap32(0); /* Stack limit.  */
459             unlock_user(ptr, ARG(0), 16);
460 #endif
461             return 0;
462         }
463     case SYS_EXIT:
464         exit(0);
465     default:
466         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
467         cpu_dump_state(env, stderr, fprintf, 0);
468         abort();
469     }
470 }
471