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 M68kCPU *cpu = M68K_CPU(cs); 81 CPUM68KState *env = &cpu->env; 82 83 target_ulong args = env->dregs[1]; 84 if (put_user_u32(ret, args) || 85 put_user_u32(host_to_gdb_errno(err), args + 4)) { 86 /* 87 * The m68k semihosting ABI does not provide any way to report this 88 * error to the guest, so the best we can do is log it in qemu. 89 * It is always a guest error not to pass us a valid argument block. 90 */ 91 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 92 "discarded because argument block not writable\n"); 93 } 94 } 95 96 static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) 97 { 98 M68kCPU *cpu = M68K_CPU(cs); 99 CPUM68KState *env = &cpu->env; 100 101 target_ulong args = env->dregs[1]; 102 if (put_user_u32(ret >> 32, args) || 103 put_user_u32(ret, args + 4) || 104 put_user_u32(host_to_gdb_errno(err), args + 8)) { 105 /* No way to report this via m68k semihosting ABI; just log it */ 106 qemu_log_mask(LOG_GUEST_ERROR, "m68k-semihosting: return value " 107 "discarded because argument block not writable\n"); 108 } 109 } 110 111 /* 112 * Read the input value from the argument block; fail the semihosting 113 * call if the memory read fails. 114 */ 115 #define GET_ARG(n) do { \ 116 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 117 goto failed; \ 118 } \ 119 } while (0) 120 121 #define GET_ARG64(n) do { \ 122 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 123 goto failed64; \ 124 } \ 125 } while (0) 126 127 128 void do_m68k_semihosting(CPUM68KState *env, int nr) 129 { 130 CPUState *cs = env_cpu(env); 131 uint32_t args; 132 target_ulong arg0, arg1, arg2, arg3; 133 134 args = env->dregs[1]; 135 switch (nr) { 136 case HOSTED_EXIT: 137 gdb_exit(env->dregs[0]); 138 exit(env->dregs[0]); 139 140 case HOSTED_OPEN: 141 GET_ARG(0); 142 GET_ARG(1); 143 GET_ARG(2); 144 GET_ARG(3); 145 semihost_sys_open(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); 146 break; 147 148 case HOSTED_CLOSE: 149 GET_ARG(0); 150 semihost_sys_close(cs, m68k_semi_u32_cb, arg0); 151 break; 152 153 case HOSTED_READ: 154 GET_ARG(0); 155 GET_ARG(1); 156 GET_ARG(2); 157 semihost_sys_read(cs, m68k_semi_u32_cb, arg0, arg1, arg2); 158 break; 159 160 case HOSTED_WRITE: 161 GET_ARG(0); 162 GET_ARG(1); 163 GET_ARG(2); 164 semihost_sys_write(cs, m68k_semi_u32_cb, arg0, arg1, arg2); 165 break; 166 167 case HOSTED_LSEEK: 168 GET_ARG64(0); 169 GET_ARG64(1); 170 GET_ARG64(2); 171 GET_ARG64(3); 172 semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0, 173 deposit64(arg2, 32, 32, arg1), arg3); 174 break; 175 176 case HOSTED_RENAME: 177 GET_ARG(0); 178 GET_ARG(1); 179 GET_ARG(2); 180 GET_ARG(3); 181 semihost_sys_rename(cs, m68k_semi_u32_cb, arg0, arg1, arg2, arg3); 182 break; 183 184 case HOSTED_UNLINK: 185 GET_ARG(0); 186 GET_ARG(1); 187 semihost_sys_remove(cs, m68k_semi_u32_cb, arg0, arg1); 188 break; 189 190 case HOSTED_STAT: 191 GET_ARG(0); 192 GET_ARG(1); 193 GET_ARG(2); 194 semihost_sys_stat(cs, m68k_semi_u32_cb, arg0, arg1, arg2); 195 break; 196 197 case HOSTED_FSTAT: 198 GET_ARG(0); 199 GET_ARG(1); 200 semihost_sys_fstat(cs, m68k_semi_u32_cb, arg0, arg1); 201 break; 202 203 case HOSTED_GETTIMEOFDAY: 204 GET_ARG(0); 205 GET_ARG(1); 206 semihost_sys_gettimeofday(cs, m68k_semi_u32_cb, arg0, arg1); 207 break; 208 209 case HOSTED_ISATTY: 210 GET_ARG(0); 211 semihost_sys_isatty(cs, m68k_semi_u32_cb, arg0); 212 break; 213 214 case HOSTED_SYSTEM: 215 GET_ARG(0); 216 GET_ARG(1); 217 semihost_sys_system(cs, m68k_semi_u32_cb, arg0, arg1); 218 break; 219 220 case HOSTED_INIT_SIM: 221 /* 222 * FIXME: This is wrong for boards where RAM does not start at 223 * address zero. 224 */ 225 env->dregs[1] = current_machine->ram_size; 226 env->aregs[7] = current_machine->ram_size; 227 return; 228 229 default: 230 cpu_abort(env_cpu(env), "Unsupported semihosting syscall %d\n", nr); 231 232 failed: 233 m68k_semi_u32_cb(cs, -1, EFAULT); 234 break; 235 failed64: 236 m68k_semi_u64_cb(cs, -1, EFAULT); 237 break; 238 } 239 } 240