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