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