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