1 /* 2 * Nios II Semihosting syscall interface. 3 * This code is derived from m68k-semi.c. 4 * The semihosting protocol implemented here is described in the 5 * libgloss sources: 6 * https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD 7 * 8 * Copyright (c) 2017-2019 Mentor Graphics 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, see <http://www.gnu.org/licenses/>. 22 */ 23 24 #include "qemu/osdep.h" 25 #include "cpu.h" 26 #include "exec/gdbstub.h" 27 #include "semihosting/syscalls.h" 28 #include "semihosting/softmmu-uaccess.h" 29 #include "qemu/log.h" 30 31 #define HOSTED_EXIT 0 32 #define HOSTED_INIT_SIM 1 33 #define HOSTED_OPEN 2 34 #define HOSTED_CLOSE 3 35 #define HOSTED_READ 4 36 #define HOSTED_WRITE 5 37 #define HOSTED_LSEEK 6 38 #define HOSTED_RENAME 7 39 #define HOSTED_UNLINK 8 40 #define HOSTED_STAT 9 41 #define HOSTED_FSTAT 10 42 #define HOSTED_GETTIMEOFDAY 11 43 #define HOSTED_ISATTY 12 44 #define HOSTED_SYSTEM 13 45 46 static int host_to_gdb_errno(int err) 47 { 48 #define E(X) case E##X: return GDB_E##X 49 switch (err) { 50 E(PERM); 51 E(NOENT); 52 E(INTR); 53 E(BADF); 54 E(ACCES); 55 E(FAULT); 56 E(BUSY); 57 E(EXIST); 58 E(NODEV); 59 E(NOTDIR); 60 E(ISDIR); 61 E(INVAL); 62 E(NFILE); 63 E(MFILE); 64 E(FBIG); 65 E(NOSPC); 66 E(SPIPE); 67 E(ROFS); 68 E(NAMETOOLONG); 69 default: 70 return GDB_EUNKNOWN; 71 } 72 #undef E 73 } 74 75 static void nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) 76 { 77 Nios2CPU *cpu = NIOS2_CPU(cs); 78 CPUNios2State *env = &cpu->env; 79 target_ulong args = env->regs[R_ARG1]; 80 81 if (put_user_u32(ret, args) || 82 put_user_u32(host_to_gdb_errno(err), args + 4)) { 83 /* 84 * The nios2 semihosting ABI does not provide any way to report this 85 * error to the guest, so the best we can do is log it in qemu. 86 * It is always a guest error not to pass us a valid argument block. 87 */ 88 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " 89 "discarded because argument block not writable\n"); 90 } 91 } 92 93 static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) 94 { 95 Nios2CPU *cpu = NIOS2_CPU(cs); 96 CPUNios2State *env = &cpu->env; 97 target_ulong args = env->regs[R_ARG1]; 98 99 if (put_user_u32(ret >> 32, args) || 100 put_user_u32(ret, args + 4) || 101 put_user_u32(host_to_gdb_errno(err), args + 8)) { 102 /* No way to report this via nios2 semihosting ABI; just log it */ 103 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " 104 "discarded because argument block not writable\n"); 105 } 106 } 107 108 /* 109 * Read the input value from the argument block; fail the semihosting 110 * call if the memory read fails. 111 */ 112 #define GET_ARG(n) do { \ 113 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 114 goto failed; \ 115 } \ 116 } while (0) 117 118 #define GET_ARG64(n) do { \ 119 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 120 goto failed64; \ 121 } \ 122 } while (0) 123 124 void do_nios2_semihosting(CPUNios2State *env) 125 { 126 CPUState *cs = env_cpu(env); 127 int nr; 128 uint32_t args; 129 target_ulong arg0, arg1, arg2, arg3; 130 131 nr = env->regs[R_ARG0]; 132 args = env->regs[R_ARG1]; 133 switch (nr) { 134 case HOSTED_EXIT: 135 gdb_exit(env->regs[R_ARG0]); 136 exit(env->regs[R_ARG0]); 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, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); 144 break; 145 146 case HOSTED_CLOSE: 147 GET_ARG(0); 148 semihost_sys_close(cs, nios2_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, nios2_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, nios2_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, nios2_semi_u64_cb, arg0, 171 deposit64(arg2, arg1, 32, 32), 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, nios2_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, nios2_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, nios2_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, nios2_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, nios2_semi_u32_cb, arg0, arg1); 205 break; 206 207 case HOSTED_ISATTY: 208 GET_ARG(0); 209 semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0); 210 break; 211 212 case HOSTED_SYSTEM: 213 GET_ARG(0); 214 GET_ARG(1); 215 semihost_sys_system(cs, nios2_semi_u32_cb, arg0, arg1); 216 break; 217 218 default: 219 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported " 220 "semihosting syscall %d\n", nr); 221 nios2_semi_u32_cb(cs, -1, ENOSYS); 222 break; 223 224 failed: 225 nios2_semi_u32_cb(cs, -1, EFAULT); 226 break; 227 failed64: 228 nios2_semi_u64_cb(cs, -1, EFAULT); 229 break; 230 } 231 } 232