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