Avoid accessing guest memory directly in usermode emulation.
[qemu] / linux-user / arm-semi.c
1 /*
2  *  Arm "Angel" semihosting syscalls
3  * 
4  *  Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 "qemu.h"
30
31 #define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
32
33 #define SYS_OPEN        0x01
34 #define SYS_CLOSE       0x02
35 #define SYS_WRITEC      0x03
36 #define SYS_WRITE0      0x04
37 #define SYS_WRITE       0x05
38 #define SYS_READ        0x06
39 #define SYS_READC       0x07
40 #define SYS_ISTTY       0x09
41 #define SYS_SEEK        0x0a
42 #define SYS_FLEN        0x0c
43 #define SYS_TMPNAM      0x0d
44 #define SYS_REMOVE      0x0e
45 #define SYS_RENAME      0x0f
46 #define SYS_CLOCK       0x10
47 #define SYS_TIME        0x11
48 #define SYS_SYSTEM      0x12
49 #define SYS_ERRNO       0x13
50 #define SYS_GET_CMDLINE 0x15
51 #define SYS_HEAPINFO    0x16
52 #define SYS_EXIT        0x18
53
54 #ifndef O_BINARY
55 #define O_BINARY 0
56 #endif
57
58 int open_modeflags[12] = {
59     O_RDONLY,
60     O_RDONLY | O_BINARY,
61     O_RDWR,
62     O_RDWR | O_BINARY,
63     O_WRONLY | O_CREAT | O_TRUNC,
64     O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
65     O_RDWR | O_CREAT | O_TRUNC,
66     O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
67     O_WRONLY | O_CREAT | O_APPEND,
68     O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
69     O_RDWR | O_CREAT | O_APPEND,
70     O_RDWR | O_CREAT | O_APPEND | O_BINARY
71 };
72
73 static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
74 {
75   if (code == (uint32_t)-1)
76       ts->swi_errno = errno;
77   return code;
78 }
79
80 #define ARG(n) tget32(args + n * 4)
81 uint32_t do_arm_semihosting(CPUState *env)
82 {
83     target_ulong args;
84     char * s;
85     int nr;
86     uint32_t ret;
87     TaskState *ts = env->opaque;
88
89     nr = env->regs[0];
90     args = env->regs[1];
91     switch (nr) {
92     case SYS_OPEN:
93         s = (char *)g2h(ARG(0));
94         if (ARG(1) >= 12)
95           return (uint32_t)-1;
96         if (strcmp(s, ":tt") == 0) {
97             if (ARG(1) < 4)
98                 return STDIN_FILENO;
99             else
100                 return STDOUT_FILENO;
101         }
102         return set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
103     case SYS_CLOSE:
104         return set_swi_errno(ts, close(ARG(0)));
105     case SYS_WRITEC:
106         {
107           char c = tget8(args);
108           /* Write to debug console.  stderr is near enough.  */
109           return write(STDERR_FILENO, &c, 1);
110         }
111     case SYS_WRITE0:
112         s = lock_user_string(args);
113         ret = write(STDERR_FILENO, s, strlen(s));
114         unlock_user(s, args, 0);
115         return ret;
116     case SYS_WRITE:
117         ret = set_swi_errno(ts, write(ARG(0), g2h(ARG(1)), ARG(2)));
118         if (ret == (uint32_t)-1)
119             return -1;
120         return ARG(2) - ret;
121     case SYS_READ:
122         ret = set_swi_errno(ts, read(ARG(0), g2h(ARG(1)), ARG(2)));
123         if (ret == (uint32_t)-1)
124             return -1;
125         return ARG(2) - ret;
126     case SYS_READC:
127        /* XXX: Read from debug cosole. Not implemented.  */
128         return 0;
129     case SYS_ISTTY:
130         return isatty(ARG(0));
131     case SYS_SEEK:
132         ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
133         if (ret == (uint32_t)-1)
134           return -1;
135         return 0;
136     case SYS_FLEN:
137         {
138             struct stat buf;
139             ret = set_swi_errno(ts, fstat(ARG(0), &buf));
140             if (ret == (uint32_t)-1)
141                 return -1;
142             return buf.st_size;
143         }
144     case SYS_TMPNAM:
145         /* XXX: Not implemented.  */
146         return -1;
147     case SYS_REMOVE:
148         return set_swi_errno(ts, remove((char *)g2h(ARG(0))));
149     case SYS_RENAME:
150         return set_swi_errno(ts, rename((char *)g2h(ARG(0)),
151                              (char *)g2h(ARG(2))));
152     case SYS_CLOCK:
153         return clock() / (CLOCKS_PER_SEC / 100);
154     case SYS_TIME:
155         return set_swi_errno(ts, time(NULL));
156     case SYS_SYSTEM:
157         return set_swi_errno(ts, system((char *)g2h(ARG(0))));
158     case SYS_ERRNO:
159         return ts->swi_errno;
160     case SYS_GET_CMDLINE:
161         /* XXX: Not implemented.  */
162         s = (char *)g2h(ARG(0));
163         *s = 0;
164         return -1;
165     case SYS_HEAPINFO:
166         {
167             uint32_t *ptr;
168             uint32_t limit;
169
170             /* Some C llibraries assume the heap immediately follows .bss, so
171                allocate it using sbrk.  */
172             if (!ts->heap_limit) {
173                 long ret;
174
175                 ts->heap_base = do_brk(0);
176                 limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
177                 /* Try a big heap, and reduce the size if that fails.  */
178                 for (;;) {
179                     ret = do_brk(limit);
180                     if (ret != -1)
181                         break;
182                     limit = (ts->heap_base >> 1) + (limit >> 1);
183                 }
184                 ts->heap_limit = limit;
185             }
186               
187             page_unprotect_range (ARG(0), 32);
188             ptr = (uint32_t *)g2h(ARG(0));
189             ptr[0] = tswap32(ts->heap_base);
190             ptr[1] = tswap32(ts->heap_limit);
191             ptr[2] = tswap32(ts->stack_base);
192             ptr[3] = tswap32(0); /* Stack limit.  */
193             return 0;
194         }
195     case SYS_EXIT:
196         exit(0);
197     default:
198         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
199         cpu_dump_state(env, stderr, fprintf, 0);
200         abort();
201     }
202 }
203