1 /* 2 * m68k/ColdFire Semihosting syscall interface 3 * 4 * Copyright (c) 2005-2007 CodeSourcery. 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, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 22 #include "cpu.h" 23 #include "exec/gdbstub.h" 24 #if defined(CONFIG_USER_ONLY) 25 #include "qemu.h" 26 #define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024) 27 #else 28 #include "semihosting/softmmu-uaccess.h" 29 #include "hw/boards.h" 30 #endif 31 #include "qemu/log.h" 32 33 #define HOSTED_EXIT 0 34 #define HOSTED_INIT_SIM 1 35 #define HOSTED_OPEN 2 36 #define HOSTED_CLOSE 3 37 #define HOSTED_READ 4 38 #define HOSTED_WRITE 5 39 #define HOSTED_LSEEK 6 40 #define HOSTED_RENAME 7 41 #define HOSTED_UNLINK 8 42 #define HOSTED_STAT 9 43 #define HOSTED_FSTAT 10 44 #define HOSTED_GETTIMEOFDAY 11 45 #define HOSTED_ISATTY 12 46 #define HOSTED_SYSTEM 13 47 48 typedef uint32_t gdb_mode_t; 49 typedef uint32_t gdb_time_t; 50 51 struct m68k_gdb_stat { 52 uint32_t gdb_st_dev; /* device */ 53 uint32_t gdb_st_ino; /* inode */ 54 gdb_mode_t gdb_st_mode; /* protection */ 55 uint32_t gdb_st_nlink; /* number of hard links */ 56 uint32_t gdb_st_uid; /* user ID of owner */ 57 uint32_t gdb_st_gid; /* group ID of owner */ 58 uint32_t gdb_st_rdev; /* device type (if inode device) */ 59 uint64_t gdb_st_size; /* total size, in bytes */ 60 uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ 61 uint64_t gdb_st_blocks; /* number of blocks allocated */ 62 gdb_time_t gdb_st_atime; /* time of last access */ 63 gdb_time_t gdb_st_mtime; /* time of last modification */ 64 gdb_time_t gdb_st_ctime; /* time of last change */ 65 } QEMU_PACKED; 66 67 struct gdb_timeval { 68 gdb_time_t tv_sec; /* second */ 69 uint64_t tv_usec; /* microsecond */ 70 } QEMU_PACKED; 71 72 static int translate_openflags(int flags) 73 { 74 int hf; 75 76 if (flags & GDB_O_WRONLY) 77 hf = O_WRONLY; 78 else if (flags & GDB_O_RDWR) 79 hf = O_RDWR; 80 else 81 hf = O_RDONLY; 82 83 if (flags & GDB_O_APPEND) hf |= O_APPEND; 84 if (flags & GDB_O_CREAT) hf |= O_CREAT; 85 if (flags & GDB_O_TRUNC) hf |= O_TRUNC; 86 if (flags & GDB_O_EXCL) hf |= O_EXCL; 87 88 return hf; 89 } 90 91 static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) 92 { 93 struct m68k_gdb_stat *p; 94 95 if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0))) 96 /* FIXME - should this return an error code? */ 97 return; 98 p->gdb_st_dev = cpu_to_be32(s->st_dev); 99 p->gdb_st_ino = cpu_to_be32(s->st_ino); 100 p->gdb_st_mode = cpu_to_be32(s->st_mode); 101 p->gdb_st_nlink = cpu_to_be32(s->st_nlink); 102 p->gdb_st_uid = cpu_to_be32(s->st_uid); 103 p->gdb_st_gid = cpu_to_be32(s->st_gid); 104 p->gdb_st_rdev = cpu_to_be32(s->st_rdev); 105 p->gdb_st_size = cpu_to_be64(s->st_size); 106 #ifdef _WIN32 107 /* Windows stat is missing some fields. */ 108 p->gdb_st_blksize = 0; 109 p->gdb_st_blocks = 0; 110 #else 111 p->gdb_st_blksize = cpu_to_be64(s->st_blksize); 112 p->gdb_st_blocks = cpu_to_be64(s->st_blocks); 113 #endif 114 p->gdb_st_atime = cpu_to_be32(s->st_atime); 115 p->gdb_st_mtime = cpu_to_be32(s->st_mtime); 116 p->gdb_st_ctime = cpu_to_be32(s->st_ctime); 117 unlock_user(p, addr, sizeof(struct m68k_gdb_stat)); 118 } 119 120 static void m68k_semi_return_u32(CPUM68KState *env, uint32_t ret, uint32_t err) 121 { 122 target_ulong args = env->dregs[1]; 123 if (put_user_u32(ret, args) || 124 put_user_u32(err, args + 4)) { 125 /* 126 * The m68k semihosting ABI does not provide any way to report this 127 * error to the guest, so the best we can do is log it in qemu. 128 * It is always a guest error not to pass us a valid argument block. 129 */ 130 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 131 "discarded because argument block not writable\n"); 132 } 133 } 134 135 static void m68k_semi_return_u64(CPUM68KState *env, uint64_t ret, uint32_t err) 136 { 137 target_ulong args = env->dregs[1]; 138 if (put_user_u32(ret >> 32, args) || 139 put_user_u32(ret, args + 4) || 140 put_user_u32(err, args + 8)) { 141 /* No way to report this via m68k semihosting ABI; just log it */ 142 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 143 "discarded because argument block not writable\n"); 144 } 145 } 146 147 static int m68k_semi_is_fseek; 148 149 static void m68k_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) 150 { 151 M68kCPU *cpu = M68K_CPU(cs); 152 CPUM68KState *env = &cpu->env; 153 154 if (m68k_semi_is_fseek) { 155 /* 156 * FIXME: We've already lost the high bits of the fseek 157 * return value. 158 */ 159 m68k_semi_return_u64(env, ret, err); 160 m68k_semi_is_fseek = 0; 161 } else { 162 m68k_semi_return_u32(env, ret, err); 163 } 164 } 165 166 /* 167 * Read the input value from the argument block; fail the semihosting 168 * call if the memory read fails. 169 */ 170 #define GET_ARG(n) do { \ 171 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 172 result = -1; \ 173 errno = EFAULT; \ 174 goto failed; \ 175 } \ 176 } while (0) 177 178 void do_m68k_semihosting(CPUM68KState *env, int nr) 179 { 180 uint32_t args; 181 target_ulong arg0, arg1, arg2, arg3; 182 void *p; 183 void *q; 184 uint32_t len; 185 uint32_t result; 186 187 args = env->dregs[1]; 188 switch (nr) { 189 case HOSTED_EXIT: 190 gdb_exit(env->dregs[0]); 191 exit(env->dregs[0]); 192 case HOSTED_OPEN: 193 GET_ARG(0); 194 GET_ARG(1); 195 GET_ARG(2); 196 GET_ARG(3); 197 if (use_gdb_syscalls()) { 198 gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, 199 arg2, arg3); 200 return; 201 } else { 202 p = lock_user_string(arg0); 203 if (!p) { 204 /* FIXME - check error code? */ 205 result = -1; 206 } else { 207 result = open(p, translate_openflags(arg2), arg3); 208 unlock_user(p, arg0, 0); 209 } 210 } 211 break; 212 case HOSTED_CLOSE: 213 { 214 /* Ignore attempts to close stdin/out/err. */ 215 GET_ARG(0); 216 int fd = arg0; 217 if (fd > 2) { 218 if (use_gdb_syscalls()) { 219 gdb_do_syscall(m68k_semi_cb, "close,%x", arg0); 220 return; 221 } else { 222 result = close(fd); 223 } 224 } else { 225 result = 0; 226 } 227 break; 228 } 229 case HOSTED_READ: 230 GET_ARG(0); 231 GET_ARG(1); 232 GET_ARG(2); 233 len = arg2; 234 if (use_gdb_syscalls()) { 235 gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x", 236 arg0, arg1, len); 237 return; 238 } else { 239 p = lock_user(VERIFY_WRITE, arg1, len, 0); 240 if (!p) { 241 /* FIXME - check error code? */ 242 result = -1; 243 } else { 244 result = read(arg0, p, len); 245 unlock_user(p, arg1, len); 246 } 247 } 248 break; 249 case HOSTED_WRITE: 250 GET_ARG(0); 251 GET_ARG(1); 252 GET_ARG(2); 253 len = arg2; 254 if (use_gdb_syscalls()) { 255 gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x", 256 arg0, arg1, len); 257 return; 258 } else { 259 p = lock_user(VERIFY_READ, arg1, len, 1); 260 if (!p) { 261 /* FIXME - check error code? */ 262 result = -1; 263 } else { 264 result = write(arg0, p, len); 265 unlock_user(p, arg0, 0); 266 } 267 } 268 break; 269 case HOSTED_LSEEK: 270 { 271 uint64_t off; 272 GET_ARG(0); 273 GET_ARG(1); 274 GET_ARG(2); 275 GET_ARG(3); 276 off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); 277 if (use_gdb_syscalls()) { 278 m68k_semi_is_fseek = 1; 279 gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x", 280 arg0, off, arg3); 281 } else { 282 off = lseek(arg0, off, arg3); 283 m68k_semi_return_u64(env, off, errno); 284 } 285 return; 286 } 287 case HOSTED_RENAME: 288 GET_ARG(0); 289 GET_ARG(1); 290 GET_ARG(2); 291 GET_ARG(3); 292 if (use_gdb_syscalls()) { 293 gdb_do_syscall(m68k_semi_cb, "rename,%s,%s", 294 arg0, (int)arg1, arg2, (int)arg3); 295 return; 296 } else { 297 p = lock_user_string(arg0); 298 q = lock_user_string(arg2); 299 if (!p || !q) { 300 /* FIXME - check error code? */ 301 result = -1; 302 } else { 303 result = rename(p, q); 304 } 305 unlock_user(p, arg0, 0); 306 unlock_user(q, arg2, 0); 307 } 308 break; 309 case HOSTED_UNLINK: 310 GET_ARG(0); 311 GET_ARG(1); 312 if (use_gdb_syscalls()) { 313 gdb_do_syscall(m68k_semi_cb, "unlink,%s", 314 arg0, (int)arg1); 315 return; 316 } else { 317 p = lock_user_string(arg0); 318 if (!p) { 319 /* FIXME - check error code? */ 320 result = -1; 321 } else { 322 result = unlink(p); 323 unlock_user(p, arg0, 0); 324 } 325 } 326 break; 327 case HOSTED_STAT: 328 GET_ARG(0); 329 GET_ARG(1); 330 GET_ARG(2); 331 if (use_gdb_syscalls()) { 332 gdb_do_syscall(m68k_semi_cb, "stat,%s,%x", 333 arg0, (int)arg1, arg2); 334 return; 335 } else { 336 struct stat s; 337 p = lock_user_string(arg0); 338 if (!p) { 339 /* FIXME - check error code? */ 340 result = -1; 341 } else { 342 result = stat(p, &s); 343 unlock_user(p, arg0, 0); 344 } 345 if (result == 0) { 346 translate_stat(env, arg2, &s); 347 } 348 } 349 break; 350 case HOSTED_FSTAT: 351 GET_ARG(0); 352 GET_ARG(1); 353 if (use_gdb_syscalls()) { 354 gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x", 355 arg0, arg1); 356 return; 357 } else { 358 struct stat s; 359 result = fstat(arg0, &s); 360 if (result == 0) { 361 translate_stat(env, arg1, &s); 362 } 363 } 364 break; 365 case HOSTED_GETTIMEOFDAY: 366 GET_ARG(0); 367 GET_ARG(1); 368 if (use_gdb_syscalls()) { 369 gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x", 370 arg0, arg1); 371 return; 372 } else { 373 struct gdb_timeval *p; 374 int64_t rt = g_get_real_time(); 375 p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); 376 if (!p) { 377 /* FIXME - check error code? */ 378 result = -1; 379 } else { 380 result = 0; 381 p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); 382 p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); 383 unlock_user(p, arg0, sizeof(struct gdb_timeval)); 384 } 385 } 386 break; 387 case HOSTED_ISATTY: 388 GET_ARG(0); 389 if (use_gdb_syscalls()) { 390 gdb_do_syscall(m68k_semi_cb, "isatty,%x", arg0); 391 return; 392 } else { 393 result = isatty(arg0); 394 } 395 break; 396 case HOSTED_SYSTEM: 397 GET_ARG(0); 398 GET_ARG(1); 399 if (use_gdb_syscalls()) { 400 gdb_do_syscall(m68k_semi_cb, "system,%s", 401 arg0, (int)arg1); 402 return; 403 } else { 404 p = lock_user_string(arg0); 405 if (!p) { 406 /* FIXME - check error code? */ 407 result = -1; 408 } else { 409 result = system(p); 410 unlock_user(p, arg0, 0); 411 } 412 } 413 break; 414 case HOSTED_INIT_SIM: 415 #if defined(CONFIG_USER_ONLY) 416 { 417 CPUState *cs = env_cpu(env); 418 TaskState *ts = cs->opaque; 419 /* Allocate the heap using sbrk. */ 420 if (!ts->heap_limit) { 421 abi_ulong ret; 422 uint32_t size; 423 uint32_t base; 424 425 base = do_brk(0); 426 size = SEMIHOSTING_HEAP_SIZE; 427 /* Try a big heap, and reduce the size if that fails. */ 428 for (;;) { 429 ret = do_brk(base + size); 430 if (ret >= (base + size)) { 431 break; 432 } 433 size >>= 1; 434 } 435 ts->heap_limit = base + size; 436 } 437 /* 438 * This call may happen before we have writable memory, so return 439 * values directly in registers. 440 */ 441 env->dregs[1] = ts->heap_limit; 442 env->aregs[7] = ts->stack_base; 443 } 444 #else 445 /* 446 * FIXME: This is wrong for boards where RAM does not start at 447 * address zero. 448 */ 449 env->dregs[1] = current_machine->ram_size; 450 env->aregs[7] = current_machine->ram_size; 451 #endif 452 return; 453 default: 454 cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); 455 result = 0; 456 } 457 failed: 458 m68k_semi_return_u32(env, result, errno); 459 } 460