/* * Lattice Mico32 semihosting syscall interface * * Copyright (c) 2014 Michael Walle * * Based on target-m68k/m68k-semi.c, which is * Copyright (c) 2005-2007 CodeSourcery. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/log.h" #include "exec/softmmu-semi.h" enum { TARGET_SYS_exit = 1, TARGET_SYS_open = 2, TARGET_SYS_close = 3, TARGET_SYS_read = 4, TARGET_SYS_write = 5, TARGET_SYS_lseek = 6, TARGET_SYS_fstat = 10, TARGET_SYS_stat = 15, }; enum { NEWLIB_O_RDONLY = 0x0, NEWLIB_O_WRONLY = 0x1, NEWLIB_O_RDWR = 0x2, NEWLIB_O_APPEND = 0x8, NEWLIB_O_CREAT = 0x200, NEWLIB_O_TRUNC = 0x400, NEWLIB_O_EXCL = 0x800, }; static int translate_openflags(int flags) { int hf; if (flags & NEWLIB_O_WRONLY) { hf = O_WRONLY; } else if (flags & NEWLIB_O_RDWR) { hf = O_RDWR; } else { hf = O_RDONLY; } if (flags & NEWLIB_O_APPEND) { hf |= O_APPEND; } if (flags & NEWLIB_O_CREAT) { hf |= O_CREAT; } if (flags & NEWLIB_O_TRUNC) { hf |= O_TRUNC; } if (flags & NEWLIB_O_EXCL) { hf |= O_EXCL; } return hf; } struct newlib_stat { int16_t newlib_st_dev; /* device */ uint16_t newlib_st_ino; /* inode */ uint16_t newlib_st_mode; /* protection */ uint16_t newlib_st_nlink; /* number of hard links */ uint16_t newlib_st_uid; /* user ID of owner */ uint16_t newlib_st_gid; /* group ID of owner */ int16_t newlib_st_rdev; /* device type (if inode device) */ int32_t newlib_st_size; /* total size, in bytes */ int32_t newlib_st_atime; /* time of last access */ uint32_t newlib_st_spare1; int32_t newlib_st_mtime; /* time of last modification */ uint32_t newlib_st_spare2; int32_t newlib_st_ctime; /* time of last change */ uint32_t newlib_st_spare3; } QEMU_PACKED; static int translate_stat(CPULM32State *env, target_ulong addr, struct stat *s) { struct newlib_stat *p; p = lock_user(VERIFY_WRITE, addr, sizeof(struct newlib_stat), 0); if (!p) { return 0; } p->newlib_st_dev = cpu_to_be16(s->st_dev); p->newlib_st_ino = cpu_to_be16(s->st_ino); p->newlib_st_mode = cpu_to_be16(s->st_mode); p->newlib_st_nlink = cpu_to_be16(s->st_nlink); p->newlib_st_uid = cpu_to_be16(s->st_uid); p->newlib_st_gid = cpu_to_be16(s->st_gid); p->newlib_st_rdev = cpu_to_be16(s->st_rdev); p->newlib_st_size = cpu_to_be32(s->st_size); p->newlib_st_atime = cpu_to_be32(s->st_atime); p->newlib_st_mtime = cpu_to_be32(s->st_mtime); p->newlib_st_ctime = cpu_to_be32(s->st_ctime); unlock_user(p, addr, sizeof(struct newlib_stat)); return 1; } bool lm32_cpu_do_semihosting(CPUState *cs) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; int ret = -1; target_ulong nr, arg0, arg1, arg2; void *p; struct stat s; nr = env->regs[R_R8]; arg0 = env->regs[R_R1]; arg1 = env->regs[R_R2]; arg2 = env->regs[R_R3]; switch (nr) { case TARGET_SYS_exit: /* void _exit(int rc) */ exit(arg0); case TARGET_SYS_open: /* int open(const char *pathname, int flags) */ p = lock_user_string(arg0); if (!p) { ret = -1; } else { ret = open(p, translate_openflags(arg2)); unlock_user(p, arg0, 0); } break; case TARGET_SYS_read: /* ssize_t read(int fd, const void *buf, size_t count) */ p = lock_user(VERIFY_WRITE, arg1, arg2, 0); if (!p) { ret = -1; } else { ret = read(arg0, p, arg2); unlock_user(p, arg1, arg2); } break; case TARGET_SYS_write: /* ssize_t write(int fd, const void *buf, size_t count) */ p = lock_user(VERIFY_READ, arg1, arg2, 1); if (!p) { ret = -1; } else { ret = write(arg0, p, arg2); unlock_user(p, arg1, 0); } break; case TARGET_SYS_close: /* int close(int fd) */ /* don't close stdin/stdout/stderr */ if (arg0 > 2) { ret = close(arg0); } else { ret = 0; } break; case TARGET_SYS_lseek: /* off_t lseek(int fd, off_t offset, int whence */ ret = lseek(arg0, arg1, arg2); break; case TARGET_SYS_stat: /* int stat(const char *path, struct stat *buf) */ p = lock_user_string(arg0); if (!p) { ret = -1; } else { ret = stat(p, &s); unlock_user(p, arg0, 0); if (translate_stat(env, arg1, &s) == 0) { ret = -1; } } break; case TARGET_SYS_fstat: /* int stat(int fd, struct stat *buf) */ ret = fstat(arg0, &s); if (ret == 0) { if (translate_stat(env, arg1, &s) == 0) { ret = -1; } } break; default: /* unhandled */ return false; } env->regs[R_R1] = ret; return true; }