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 26 #include "cpu.h" 27 #include "exec/gdbstub.h" 28 #if defined(CONFIG_USER_ONLY) 29 #include "qemu.h" 30 #else 31 #include "exec/softmmu-semi.h" 32 #endif 33 #include "qemu/log.h" 34 35 #define HOSTED_EXIT 0 36 #define HOSTED_INIT_SIM 1 37 #define HOSTED_OPEN 2 38 #define HOSTED_CLOSE 3 39 #define HOSTED_READ 4 40 #define HOSTED_WRITE 5 41 #define HOSTED_LSEEK 6 42 #define HOSTED_RENAME 7 43 #define HOSTED_UNLINK 8 44 #define HOSTED_STAT 9 45 #define HOSTED_FSTAT 10 46 #define HOSTED_GETTIMEOFDAY 11 47 #define HOSTED_ISATTY 12 48 #define HOSTED_SYSTEM 13 49 50 typedef uint32_t gdb_mode_t; 51 typedef uint32_t gdb_time_t; 52 53 struct nios2_gdb_stat { 54 uint32_t gdb_st_dev; /* device */ 55 uint32_t gdb_st_ino; /* inode */ 56 gdb_mode_t gdb_st_mode; /* protection */ 57 uint32_t gdb_st_nlink; /* number of hard links */ 58 uint32_t gdb_st_uid; /* user ID of owner */ 59 uint32_t gdb_st_gid; /* group ID of owner */ 60 uint32_t gdb_st_rdev; /* device type (if inode device) */ 61 uint64_t gdb_st_size; /* total size, in bytes */ 62 uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */ 63 uint64_t gdb_st_blocks; /* number of blocks allocated */ 64 gdb_time_t gdb_st_atime; /* time of last access */ 65 gdb_time_t gdb_st_mtime; /* time of last modification */ 66 gdb_time_t gdb_st_ctime; /* time of last change */ 67 } QEMU_PACKED; 68 69 struct gdb_timeval { 70 gdb_time_t tv_sec; /* second */ 71 uint64_t tv_usec; /* microsecond */ 72 } QEMU_PACKED; 73 74 #define GDB_O_RDONLY 0x0 75 #define GDB_O_WRONLY 0x1 76 #define GDB_O_RDWR 0x2 77 #define GDB_O_APPEND 0x8 78 #define GDB_O_CREAT 0x200 79 #define GDB_O_TRUNC 0x400 80 #define GDB_O_EXCL 0x800 81 82 static int translate_openflags(int flags) 83 { 84 int hf; 85 86 if (flags & GDB_O_WRONLY) { 87 hf = O_WRONLY; 88 } else if (flags & GDB_O_RDWR) { 89 hf = O_RDWR; 90 } else { 91 hf = O_RDONLY; 92 } 93 94 if (flags & GDB_O_APPEND) { 95 hf |= O_APPEND; 96 } 97 if (flags & GDB_O_CREAT) { 98 hf |= O_CREAT; 99 } 100 if (flags & GDB_O_TRUNC) { 101 hf |= O_TRUNC; 102 } 103 if (flags & GDB_O_EXCL) { 104 hf |= O_EXCL; 105 } 106 107 return hf; 108 } 109 110 static bool translate_stat(CPUNios2State *env, target_ulong addr, 111 struct stat *s) 112 { 113 struct nios2_gdb_stat *p; 114 115 p = lock_user(VERIFY_WRITE, addr, sizeof(struct nios2_gdb_stat), 0); 116 117 if (!p) { 118 return false; 119 } 120 p->gdb_st_dev = cpu_to_be32(s->st_dev); 121 p->gdb_st_ino = cpu_to_be32(s->st_ino); 122 p->gdb_st_mode = cpu_to_be32(s->st_mode); 123 p->gdb_st_nlink = cpu_to_be32(s->st_nlink); 124 p->gdb_st_uid = cpu_to_be32(s->st_uid); 125 p->gdb_st_gid = cpu_to_be32(s->st_gid); 126 p->gdb_st_rdev = cpu_to_be32(s->st_rdev); 127 p->gdb_st_size = cpu_to_be64(s->st_size); 128 #ifdef _WIN32 129 /* Windows stat is missing some fields. */ 130 p->gdb_st_blksize = 0; 131 p->gdb_st_blocks = 0; 132 #else 133 p->gdb_st_blksize = cpu_to_be64(s->st_blksize); 134 p->gdb_st_blocks = cpu_to_be64(s->st_blocks); 135 #endif 136 p->gdb_st_atime = cpu_to_be32(s->st_atime); 137 p->gdb_st_mtime = cpu_to_be32(s->st_mtime); 138 p->gdb_st_ctime = cpu_to_be32(s->st_ctime); 139 unlock_user(p, addr, sizeof(struct nios2_gdb_stat)); 140 return true; 141 } 142 143 static void nios2_semi_return_u32(CPUNios2State *env, uint32_t ret, 144 uint32_t err) 145 { 146 target_ulong args = env->regs[R_ARG1]; 147 if (put_user_u32(ret, args) || 148 put_user_u32(err, args + 4)) { 149 /* 150 * The nios2 semihosting ABI does not provide any way to report this 151 * error to the guest, so the best we can do is log it in qemu. 152 * It is always a guest error not to pass us a valid argument block. 153 */ 154 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " 155 "discarded because argument block not writable\n"); 156 } 157 } 158 159 static void nios2_semi_return_u64(CPUNios2State *env, uint64_t ret, 160 uint32_t err) 161 { 162 target_ulong args = env->regs[R_ARG1]; 163 if (put_user_u32(ret >> 32, args) || 164 put_user_u32(ret, args + 4) || 165 put_user_u32(err, args + 8)) { 166 /* No way to report this via nios2 semihosting ABI; just log it */ 167 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " 168 "discarded because argument block not writable\n"); 169 } 170 } 171 172 static int nios2_semi_is_lseek; 173 174 static void nios2_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) 175 { 176 Nios2CPU *cpu = NIOS2_CPU(cs); 177 CPUNios2State *env = &cpu->env; 178 179 if (nios2_semi_is_lseek) { 180 /* 181 * FIXME: We've already lost the high bits of the lseek 182 * return value. 183 */ 184 nios2_semi_return_u64(env, ret, err); 185 nios2_semi_is_lseek = 0; 186 } else { 187 nios2_semi_return_u32(env, ret, err); 188 } 189 } 190 191 /* 192 * Read the input value from the argument block; fail the semihosting 193 * call if the memory read fails. 194 */ 195 #define GET_ARG(n) do { \ 196 if (get_user_ual(arg ## n, args + (n) * 4)) { \ 197 result = -1; \ 198 errno = EFAULT; \ 199 goto failed; \ 200 } \ 201 } while (0) 202 203 void do_nios2_semihosting(CPUNios2State *env) 204 { 205 int nr; 206 uint32_t args; 207 target_ulong arg0, arg1, arg2, arg3; 208 void *p; 209 void *q; 210 uint32_t len; 211 uint32_t result; 212 213 nr = env->regs[R_ARG0]; 214 args = env->regs[R_ARG1]; 215 switch (nr) { 216 case HOSTED_EXIT: 217 gdb_exit(env->regs[R_ARG0]); 218 exit(env->regs[R_ARG0]); 219 case HOSTED_OPEN: 220 GET_ARG(0); 221 GET_ARG(1); 222 GET_ARG(2); 223 GET_ARG(3); 224 if (use_gdb_syscalls()) { 225 gdb_do_syscall(nios2_semi_cb, "open,%s,%x,%x", arg0, (int)arg1, 226 arg2, arg3); 227 return; 228 } else { 229 p = lock_user_string(arg0); 230 if (!p) { 231 result = -1; 232 errno = EFAULT; 233 } else { 234 result = open(p, translate_openflags(arg2), arg3); 235 unlock_user(p, arg0, 0); 236 } 237 } 238 break; 239 case HOSTED_CLOSE: 240 { 241 /* Ignore attempts to close stdin/out/err. */ 242 GET_ARG(0); 243 int fd = arg0; 244 if (fd > 2) { 245 if (use_gdb_syscalls()) { 246 gdb_do_syscall(nios2_semi_cb, "close,%x", arg0); 247 return; 248 } else { 249 result = close(fd); 250 } 251 } else { 252 result = 0; 253 } 254 break; 255 } 256 case HOSTED_READ: 257 GET_ARG(0); 258 GET_ARG(1); 259 GET_ARG(2); 260 len = arg2; 261 if (use_gdb_syscalls()) { 262 gdb_do_syscall(nios2_semi_cb, "read,%x,%x,%x", 263 arg0, arg1, len); 264 return; 265 } else { 266 p = lock_user(VERIFY_WRITE, arg1, len, 0); 267 if (!p) { 268 result = -1; 269 errno = EFAULT; 270 } else { 271 result = read(arg0, p, len); 272 unlock_user(p, arg1, len); 273 } 274 } 275 break; 276 case HOSTED_WRITE: 277 GET_ARG(0); 278 GET_ARG(1); 279 GET_ARG(2); 280 len = arg2; 281 if (use_gdb_syscalls()) { 282 gdb_do_syscall(nios2_semi_cb, "write,%x,%x,%x", 283 arg0, arg1, len); 284 return; 285 } else { 286 p = lock_user(VERIFY_READ, arg1, len, 1); 287 if (!p) { 288 result = -1; 289 errno = EFAULT; 290 } else { 291 result = write(arg0, p, len); 292 unlock_user(p, arg0, 0); 293 } 294 } 295 break; 296 case HOSTED_LSEEK: 297 { 298 uint64_t off; 299 GET_ARG(0); 300 GET_ARG(1); 301 GET_ARG(2); 302 GET_ARG(3); 303 off = (uint32_t)arg2 | ((uint64_t)arg1 << 32); 304 if (use_gdb_syscalls()) { 305 nios2_semi_is_lseek = 1; 306 gdb_do_syscall(nios2_semi_cb, "lseek,%x,%lx,%x", 307 arg0, off, arg3); 308 } else { 309 off = lseek(arg0, off, arg3); 310 nios2_semi_return_u64(env, off, errno); 311 } 312 return; 313 } 314 case HOSTED_RENAME: 315 GET_ARG(0); 316 GET_ARG(1); 317 GET_ARG(2); 318 GET_ARG(3); 319 if (use_gdb_syscalls()) { 320 gdb_do_syscall(nios2_semi_cb, "rename,%s,%s", 321 arg0, (int)arg1, arg2, (int)arg3); 322 return; 323 } else { 324 p = lock_user_string(arg0); 325 q = lock_user_string(arg2); 326 if (!p || !q) { 327 result = -1; 328 errno = EFAULT; 329 } else { 330 result = rename(p, q); 331 } 332 unlock_user(p, arg0, 0); 333 unlock_user(q, arg2, 0); 334 } 335 break; 336 case HOSTED_UNLINK: 337 GET_ARG(0); 338 GET_ARG(1); 339 if (use_gdb_syscalls()) { 340 gdb_do_syscall(nios2_semi_cb, "unlink,%s", 341 arg0, (int)arg1); 342 return; 343 } else { 344 p = lock_user_string(arg0); 345 if (!p) { 346 result = -1; 347 errno = EFAULT; 348 } else { 349 result = unlink(p); 350 unlock_user(p, arg0, 0); 351 } 352 } 353 break; 354 case HOSTED_STAT: 355 GET_ARG(0); 356 GET_ARG(1); 357 GET_ARG(2); 358 if (use_gdb_syscalls()) { 359 gdb_do_syscall(nios2_semi_cb, "stat,%s,%x", 360 arg0, (int)arg1, arg2); 361 return; 362 } else { 363 struct stat s; 364 p = lock_user_string(arg0); 365 if (!p) { 366 result = -1; 367 errno = EFAULT; 368 } else { 369 result = stat(p, &s); 370 unlock_user(p, arg0, 0); 371 } 372 if (result == 0 && !translate_stat(env, arg2, &s)) { 373 result = -1; 374 errno = EFAULT; 375 } 376 } 377 break; 378 case HOSTED_FSTAT: 379 GET_ARG(0); 380 GET_ARG(1); 381 if (use_gdb_syscalls()) { 382 gdb_do_syscall(nios2_semi_cb, "fstat,%x,%x", 383 arg0, arg1); 384 return; 385 } else { 386 struct stat s; 387 result = fstat(arg0, &s); 388 if (result == 0 && !translate_stat(env, arg1, &s)) { 389 result = -1; 390 errno = EFAULT; 391 } 392 } 393 break; 394 case HOSTED_GETTIMEOFDAY: 395 /* Only the tv parameter is used. tz is assumed NULL. */ 396 GET_ARG(0); 397 if (use_gdb_syscalls()) { 398 gdb_do_syscall(nios2_semi_cb, "gettimeofday,%x,%x", 399 arg0, 0); 400 return; 401 } else { 402 struct gdb_timeval *p; 403 int64_t rt = g_get_real_time(); 404 p = lock_user(VERIFY_WRITE, arg0, sizeof(struct gdb_timeval), 0); 405 if (!p) { 406 result = -1; 407 errno = EFAULT; 408 } else { 409 result = 0; 410 p->tv_sec = cpu_to_be32(rt / G_USEC_PER_SEC); 411 p->tv_usec = cpu_to_be64(rt % G_USEC_PER_SEC); 412 unlock_user(p, arg0, sizeof(struct gdb_timeval)); 413 } 414 } 415 break; 416 case HOSTED_ISATTY: 417 GET_ARG(0); 418 if (use_gdb_syscalls()) { 419 gdb_do_syscall(nios2_semi_cb, "isatty,%x", arg0); 420 return; 421 } else { 422 result = isatty(arg0); 423 } 424 break; 425 case HOSTED_SYSTEM: 426 GET_ARG(0); 427 GET_ARG(1); 428 if (use_gdb_syscalls()) { 429 gdb_do_syscall(nios2_semi_cb, "system,%s", 430 arg0, (int)arg1); 431 return; 432 } else { 433 p = lock_user_string(arg0); 434 if (!p) { 435 result = -1; 436 errno = EFAULT; 437 } else { 438 result = system(p); 439 unlock_user(p, arg0, 0); 440 } 441 } 442 break; 443 default: 444 qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: unsupported " 445 "semihosting syscall %d\n", nr); 446 result = 0; 447 } 448 failed: 449 nios2_semi_return_u32(env, result, errno); 450 } 451