Update version for v7.1.0-rc2 release
[qemu/ar7.git] / target / m68k / m68k-semi.c
blobd0697ddbd108f84371ee3493fa95656dc3b2f646
1 /*
2 * m68k/ColdFire Semihosting syscall interface
4 * Copyright (c) 2005-2007 CodeSourcery.
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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "exec/gdbstub.h"
24 #include "semihosting/softmmu-uaccess.h"
25 #include "hw/boards.h"
26 #include "qemu/log.h"
28 #define HOSTED_EXIT 0
29 #define HOSTED_INIT_SIM 1
30 #define HOSTED_OPEN 2
31 #define HOSTED_CLOSE 3
32 #define HOSTED_READ 4
33 #define HOSTED_WRITE 5
34 #define HOSTED_LSEEK 6
35 #define HOSTED_RENAME 7
36 #define HOSTED_UNLINK 8
37 #define HOSTED_STAT 9
38 #define HOSTED_FSTAT 10
39 #define HOSTED_GETTIMEOFDAY 11
40 #define HOSTED_ISATTY 12
41 #define HOSTED_SYSTEM 13
43 static int translate_openflags(int flags)
45 int hf;
47 if (flags & GDB_O_WRONLY)
48 hf = O_WRONLY;
49 else if (flags & GDB_O_RDWR)
50 hf = O_RDWR;
51 else
52 hf = O_RDONLY;
54 if (flags & GDB_O_APPEND) hf |= O_APPEND;
55 if (flags & GDB_O_CREAT) hf |= O_CREAT;
56 if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
57 if (flags & GDB_O_EXCL) hf |= O_EXCL;
59 return hf;
62 static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s)
64 struct gdb_stat *p;
66 p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0);
67 if (!p) {
68 /* FIXME - should this return an error code? */
69 return;
71 p->gdb_st_dev = cpu_to_be32(s->st_dev);
72 p->gdb_st_ino = cpu_to_be32(s->st_ino);
73 p->gdb_st_mode = cpu_to_be32(s->st_mode);
74 p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
75 p->gdb_st_uid = cpu_to_be32(s->st_uid);
76 p->gdb_st_gid = cpu_to_be32(s->st_gid);
77 p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
78 p->gdb_st_size = cpu_to_be64(s->st_size);
79 #ifdef _WIN32
80 /* Windows stat is missing some fields. */
81 p->gdb_st_blksize = 0;
82 p->gdb_st_blocks = 0;
83 #else
84 p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
85 p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
86 #endif
87 p->gdb_st_atime = cpu_to_be32(s->st_atime);
88 p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
89 p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
90 unlock_user(p, addr, sizeof(struct gdb_stat));
93 static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err)
95 M68kCPU *cpu = M68K_CPU(cs);
96 CPUM68KState *env = &cpu->env;
98 target_ulong args = env->dregs[1];
99 if (put_user_u32(ret, args) ||
100 put_user_u32(err, args + 4)) {
102 * The m68k semihosting ABI does not provide any way to report this
103 * error to the guest, so the best we can do is log it in qemu.
104 * It is always a guest error not to pass us a valid argument block.
106 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
107 "discarded because argument block not writable\n");
111 static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err)
113 M68kCPU *cpu = M68K_CPU(cs);
114 CPUM68KState *env = &cpu->env;
116 target_ulong args = env->dregs[1];
117 if (put_user_u32(ret >> 32, args) ||
118 put_user_u32(ret, args + 4) ||
119 put_user_u32(err, args + 8)) {
120 /* No way to report this via m68k semihosting ABI; just log it */
121 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value "
122 "discarded because argument block not writable\n");
127 * Read the input value from the argument block; fail the semihosting
128 * call if the memory read fails.
130 #define GET_ARG(n) do { \
131 if (get_user_ual(arg ## n, args + (n) * 4)) { \
132 result = -1; \
133 errno = EFAULT; \
134 goto failed; \
136 } while (0)
138 void do_m68k_semihosting(CPUM68KState *env, int nr)
140 CPUState *cs = env_cpu(env);
141 uint32_t args;
142 target_ulong arg0, arg1, arg2, arg3;
143 void *p;
144 void *q;
145 uint32_t len;
146 uint32_t result;
148 args = env->dregs[1];
149 switch (nr) {
150 case HOSTED_EXIT:
151 gdb_exit(env->dregs[0]);
152 exit(env->dregs[0]);
153 case HOSTED_OPEN:
154 GET_ARG(0);
155 GET_ARG(1);
156 GET_ARG(2);
157 GET_ARG(3);
158 if (use_gdb_syscalls()) {
159 gdb_do_syscall(m68k_semi_u32_cb, "open,%s,%x,%x", arg0, (int)arg1,
160 arg2, arg3);
161 return;
162 } else {
163 p = lock_user_string(arg0);
164 if (!p) {
165 /* FIXME - check error code? */
166 result = -1;
167 } else {
168 result = open(p, translate_openflags(arg2), arg3);
169 unlock_user(p, arg0, 0);
172 break;
173 case HOSTED_CLOSE:
175 /* Ignore attempts to close stdin/out/err. */
176 GET_ARG(0);
177 int fd = arg0;
178 if (fd > 2) {
179 if (use_gdb_syscalls()) {
180 gdb_do_syscall(m68k_semi_u32_cb, "close,%x", arg0);
181 return;
182 } else {
183 result = close(fd);
185 } else {
186 result = 0;
188 break;
190 case HOSTED_READ:
191 GET_ARG(0);
192 GET_ARG(1);
193 GET_ARG(2);
194 len = arg2;
195 if (use_gdb_syscalls()) {
196 gdb_do_syscall(m68k_semi_u32_cb, "read,%x,%x,%x",
197 arg0, arg1, len);
198 return;
199 } else {
200 p = lock_user(VERIFY_WRITE, arg1, len, 0);
201 if (!p) {
202 /* FIXME - check error code? */
203 result = -1;
204 } else {
205 result = read(arg0, p, len);
206 unlock_user(p, arg1, len);
209 break;
210 case HOSTED_WRITE:
211 GET_ARG(0);
212 GET_ARG(1);
213 GET_ARG(2);
214 len = arg2;
215 if (use_gdb_syscalls()) {
216 gdb_do_syscall(m68k_semi_u32_cb, "write,%x,%x,%x",
217 arg0, arg1, len);
218 return;
219 } else {
220 p = lock_user(VERIFY_READ, arg1, len, 1);
221 if (!p) {
222 /* FIXME - check error code? */
223 result = -1;
224 } else {
225 result = write(arg0, p, len);
226 unlock_user(p, arg0, 0);
229 break;
230 case HOSTED_LSEEK:
232 uint64_t off;
233 GET_ARG(0);
234 GET_ARG(1);
235 GET_ARG(2);
236 GET_ARG(3);
237 off = (uint32_t)arg2 | ((uint64_t)arg1 << 32);
238 if (use_gdb_syscalls()) {
239 gdb_do_syscall(m68k_semi_u64_cb, "fseek,%x,%lx,%x",
240 arg0, off, arg3);
241 } else {
242 off = lseek(arg0, off, arg3);
243 m68k_semi_u64_cb(cs, off, errno);
245 return;
247 case HOSTED_RENAME:
248 GET_ARG(0);
249 GET_ARG(1);
250 GET_ARG(2);
251 GET_ARG(3);
252 if (use_gdb_syscalls()) {
253 gdb_do_syscall(m68k_semi_u32_cb, "rename,%s,%s",
254 arg0, (int)arg1, arg2, (int)arg3);
255 return;
256 } else {
257 p = lock_user_string(arg0);
258 q = lock_user_string(arg2);
259 if (!p || !q) {
260 /* FIXME - check error code? */
261 result = -1;
262 } else {
263 result = rename(p, q);
265 unlock_user(p, arg0, 0);
266 unlock_user(q, arg2, 0);
268 break;
269 case HOSTED_UNLINK:
270 GET_ARG(0);
271 GET_ARG(1);
272 if (use_gdb_syscalls()) {
273 gdb_do_syscall(m68k_semi_u32_cb, "unlink,%s",
274 arg0, (int)arg1);
275 return;
276 } else {
277 p = lock_user_string(arg0);
278 if (!p) {
279 /* FIXME - check error code? */
280 result = -1;
281 } else {
282 result = unlink(p);
283 unlock_user(p, arg0, 0);
286 break;
287 case HOSTED_STAT:
288 GET_ARG(0);
289 GET_ARG(1);
290 GET_ARG(2);
291 if (use_gdb_syscalls()) {
292 gdb_do_syscall(m68k_semi_u32_cb, "stat,%s,%x",
293 arg0, (int)arg1, arg2);
294 return;
295 } else {
296 struct stat s;
297 p = lock_user_string(arg0);
298 if (!p) {
299 /* FIXME - check error code? */
300 result = -1;
301 } else {
302 result = stat(p, &s);
303 unlock_user(p, arg0, 0);
305 if (result == 0) {
306 translate_stat(env, arg2, &s);
309 break;
310 case HOSTED_FSTAT:
311 GET_ARG(0);
312 GET_ARG(1);
313 if (use_gdb_syscalls()) {
314 gdb_do_syscall(m68k_semi_u32_cb, "fstat,%x,%x",
315 arg0, arg1);
316 return;
317 } else {
318 struct stat s;
319 result = fstat(arg0, &s);
320 if (result == 0) {
321 translate_stat(env, arg1, &s);
324 break;
325 case HOSTED_GETTIMEOFDAY:
326 GET_ARG(0);
327 GET_ARG(1);
328 if (use_gdb_syscalls()) {
329 gdb_do_syscall(m68k_semi_u32_cb, "gettimeofday,%x,%x",
330 arg0, arg1);
331 return;
332 } else {
333 struct gdb_timeval *p;
334 int64_t rt = g_get_real_time();
335 p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0);
336 if (!p) {
337 /* FIXME - check error code? */
338 result = -1;
339 } else {
340 result = 0;
341 p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC);
342 p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC);
343 unlock_user(p, arg0, sizeof(struct gdb_timeval));
346 break;
347 case HOSTED_ISATTY:
348 GET_ARG(0);
349 if (use_gdb_syscalls()) {
350 gdb_do_syscall(m68k_semi_u32_cb, "isatty,%x", arg0);
351 return;
352 } else {
353 result = isatty(arg0);
355 break;
356 case HOSTED_SYSTEM:
357 GET_ARG(0);
358 GET_ARG(1);
359 if (use_gdb_syscalls()) {
360 gdb_do_syscall(m68k_semi_u32_cb, "system,%s",
361 arg0, (int)arg1);
362 return;
363 } else {
364 p = lock_user_string(arg0);
365 if (!p) {
366 /* FIXME - check error code? */
367 result = -1;
368 } else {
369 result = system(p);
370 unlock_user(p, arg0, 0);
373 break;
374 case HOSTED_INIT_SIM:
376 * FIXME: This is wrong for boards where RAM does not start at
377 * address zero.
379 env->dregs[1] = current_machine->ram_size;
380 env->aregs[7] = current_machine->ram_size;
381 return;
382 default:
383 cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr);
384 result = 0;
386 failed:
387 m68k_semi_u32_cb(cs, result, errno);