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 #include "semihosting/softmmu-uaccess.h" 25 #include "hw/boards.h" 26 #include "qemu/log.h" 27 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 42 43 static int translate_openflags(int flags) 44 { 45 int hf; 46 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; 53 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; 58 59 return hf; 60 } 61 62 static void translate_stat(CPUM68KState *env, target_ulong addr, struct stat *s) 63 { 64 struct gdb_stat *p; 65 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; 70 } 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)); 91 } 92 93 static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) 94 { 95 M68kCPU *cpu = M68K_CPU(cs); 96 CPUM68KState *env = &cpu->env; 97 98 target_ulong args = env->dregs[1]; 99 if (put_user_u32(ret, args) || 100 put_user_u32(err, args + 4)) { 101 /* 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. 105 */ 106 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 107 "discarded because argument block not writable\n"); 108 } 109 } 110 111 static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) 112 { 113 M68kCPU *cpu = M68K_CPU(cs); 114 CPUM68KState *env = &cpu->env; 115 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"); 123 } 124 } 125 126 /* 127 * Read the input value from the argument block; fail the semihosting 128 * call if the memory read fails. 129 */ 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; \ 135 } \ 136 } while (0) 137 138 void do_m68k_semihosting(CPUM68KState *env, int nr) 139 { 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; 147 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); 170 } 171 } 172 break; 173 case HOSTED_CLOSE: 174 { 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); 184 } 185 } else { 186 result = 0; 187 } 188 break; 189 } 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); 207 } 208 } 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); 227 } 228 } 229 break; 230 case HOSTED_LSEEK: 231 { 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); 244 } 245 return; 246 } 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); 264 } 265 unlock_user(p, arg0, 0); 266 unlock_user(q, arg2, 0); 267 } 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); 284 } 285 } 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); 304 } 305 if (result == 0) { 306 translate_stat(env, arg2, &s); 307 } 308 } 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); 322 } 323 } 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)); 344 } 345 } 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); 354 } 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); 371 } 372 } 373 break; 374 case HOSTED_INIT_SIM: 375 /* 376 * FIXME: This is wrong for boards where RAM does not start at 377 * address zero. 378 */ 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; 385 } 386 failed: 387 m68k_semi_u32_cb(cs, result, errno); 388 } 389