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