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 "gdbstub/syscalls.h" 24 #include "gdbstub/helpers.h" 25 #include "semihosting/syscalls.h" 26 #include "semihosting/softmmu-uaccess.h" 27 #include "hw/boards.h" 28 #include "qemu/log.h" 29 30 #define HOSTED_EXIT 0 31 #define HOSTED_INIT_SIM 1 32 #define HOSTED_OPEN 2 33 #define HOSTED_CLOSE 3 34 #define HOSTED_READ 4 35 #define HOSTED_WRITE 5 36 #define HOSTED_LSEEK 6 37 #define HOSTED_RENAME 7 38 #define HOSTED_UNLINK 8 39 #define HOSTED_STAT 9 40 #define HOSTED_FSTAT 10 41 #define HOSTED_GETTIMEOFDAY 11 42 #define HOSTED_ISATTY 12 43 #define HOSTED_SYSTEM 13 44 45 static int host_to_gdb_errno(int err) 46 { 47 #define E(X) case E##X: return GDB_E##X 48 switch (err) { 49 E(PERM); 50 E(NOENT); 51 E(INTR); 52 E(BADF); 53 E(ACCES); 54 E(FAULT); 55 E(BUSY); 56 E(EXIST); 57 E(NODEV); 58 E(NOTDIR); 59 E(ISDIR); 60 E(INVAL); 61 E(NFILE); 62 E(MFILE); 63 E(FBIG); 64 E(NOSPC); 65 E(SPIPE); 66 E(ROFS); 67 E(NAMETOOLONG); 68 default: 69 return GDB_EUNKNOWN; 70 } 71 #undef E 72 } 73 74 static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) 75 { 76 M68kCPU *cpu = M68K_CPU(cs); 77 CPUM68KState *env = &cpu->env; 78 79 target_ulong args = env->dregs[1]; 80 if (put_user_u32(ret, args) || 81 put_user_u32(host_to_gdb_errno(err), args + 4)) { 82 /* 83 * The m68k semihosting ABI does not provide any way to report this 84 * error to the guest, so the best we can do is log it in qemu. 85 * It is always a guest error not to pass us a valid argument block. 86 */ 87 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 88 "discarded because argument block not writable\n"); 89 } 90 } 91 92 static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) 93 { 94 M68kCPU *cpu = M68K_CPU(cs); 95 CPUM68KState *env = &cpu->env; 96 97 target_ulong args = env->dregs[1]; 98 if (put_user_u32(ret >> 32, args) || 99 put_user_u32(ret, args + 4) || 100 put_user_u32(host_to_gdb_errno(err), args + 8)) { 101 /* No way to report this via m68k semihosting ABI; just log it */ 102 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 103 "discarded because argument block not writable\n"); 104 } 105 } 106 107 /* 108 * Read the input value from the argument block; fail the semihosting 109 * call if the memory read fails. 110 */ 111 #define GET_ARG(n) do { \ 112 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 113 goto failed; \ 114 } \ 115 } while (0) 116 117 #define GET_ARG64(n) do { \ 118 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 119 goto failed64; \ 120 } \ 121 } while (0) 122 123 124 void do_m68k_semihosting(CPUM68KState *env, int nr) 125 { 126 CPUState *cs = env_cpu(env); 127 uint32_t args; 128 target_ulong arg0, arg1, arg2, arg3; 129 130 args = env->dregs[1]; 131 switch (nr) { 132 case HOSTED_EXIT: 133 gdb_exit(env->dregs[0]); 134 exit(env->dregs[0]); 135 136 case HOSTED_OPEN: 137 GET_ARG(0); 138 GET_ARG(1); 139 GET_ARG(2); 140 GET_ARG(3); 141 semihost_sys_open(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); 142 break; 143 144 case HOSTED_CLOSE: 145 GET_ARG(0); 146 semihost_sys_close(cs, m68k_semi_u32_cb, arg0); 147 break; 148 149 case HOSTED_READ: 150 GET_ARG(0); 151 GET_ARG(1); 152 GET_ARG(2); 153 semihost_sys_read(cs, m68k_semi_u32_cb, arg0, arg1, arg2); 154 break; 155 156 case HOSTED_WRITE: 157 GET_ARG(0); 158 GET_ARG(1); 159 GET_ARG(2); 160 semihost_sys_write(cs, m68k_semi_u32_cb, arg0, arg1, arg2); 161 break; 162 163 case HOSTED_LSEEK: 164 GET_ARG64(0); 165 GET_ARG64(1); 166 GET_ARG64(2); 167 GET_ARG64(3); 168 semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0, 169 deposit64(arg2, arg1, 32, 32), arg3); 170 break; 171 172 case HOSTED_RENAME: 173 GET_ARG(0); 174 GET_ARG(1); 175 GET_ARG(2); 176 GET_ARG(3); 177 semihost_sys_rename(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); 178 break; 179 180 case HOSTED_UNLINK: 181 GET_ARG(0); 182 GET_ARG(1); 183 semihost_sys_remove(cs, m68k_semi_u32_cb, arg0, arg1); 184 break; 185 186 case HOSTED_STAT: 187 GET_ARG(0); 188 GET_ARG(1); 189 GET_ARG(2); 190 semihost_sys_stat(cs, m68k_semi_u32_cb, arg0, arg1, arg2); 191 break; 192 193 case HOSTED_FSTAT: 194 GET_ARG(0); 195 GET_ARG(1); 196 semihost_sys_fstat(cs, m68k_semi_u32_cb, arg0, arg1); 197 break; 198 199 case HOSTED_GETTIMEOFDAY: 200 GET_ARG(0); 201 GET_ARG(1); 202 semihost_sys_gettimeofday(cs, m68k_semi_u32_cb, arg0, arg1); 203 break; 204 205 case HOSTED_ISATTY: 206 GET_ARG(0); 207 semihost_sys_isatty(cs, m68k_semi_u32_cb, arg0); 208 break; 209 210 case HOSTED_SYSTEM: 211 GET_ARG(0); 212 GET_ARG(1); 213 semihost_sys_system(cs, m68k_semi_u32_cb, arg0, arg1); 214 break; 215 216 case HOSTED_INIT_SIM: 217 /* 218 * FIXME: This is wrong for boards where RAM does not start at 219 * address zero. 220 */ 221 env->dregs[1] = current_machine->ram_size; 222 env->aregs[7] = current_machine->ram_size; 223 return; 224 225 default: 226 cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); 227 228 failed: 229 m68k_semi_u32_cb(cs, -1, EFAULT); 230 break; 231 failed64: 232 m68k_semi_u64_cb(cs, -1, EFAULT); 233 break; 234 } 235 } 236