/*
* Lattice Mico32 semihosting syscall interface
*
* Copyright (c) 2014 Michael Walle <michael@walle.cc>
*
* 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 <errno.h>
#include <unistd.h>
#include <string.h>
#include <stddef.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;
}