@@ -20,9 +20,11 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "qemu/log.h"
+#include "exec/gdbstub.h"
#include "semihosting/softmmu-uaccess.h"
#include "semihosting/semihost.h"
#include "semihosting/console.h"
+#include "semihosting/syscalls.h"
#include "internal.h"
typedef enum UHIOp {
@@ -121,101 +123,79 @@ static void report_fault(CPUMIPSState *env)
abort();
}
-static int errno_mips(int host_errno)
+static void uhi_cb(CPUState *cs, uint64_t ret, int err)
{
- /* Errno values taken from asm-mips/errno.h */
- switch (host_errno) {
- case 0: return 0;
- case ENAMETOOLONG: return 78;
-#ifdef EOVERFLOW
- case EOVERFLOW: return 79;
-#endif
-#ifdef ELOOP
- case ELOOP: return 90;
-#endif
- default: return EINVAL;
- }
-}
+ CPUMIPSState *env = cs->env_ptr;
-static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
- target_ulong vaddr)
-{
- hwaddr len = sizeof(struct UHIStat);
- UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
- if (!dst) {
+#define E(N) case E##N: err = UHI_E##N; break
+
+ switch (err) {
+ case 0:
+ break;
+ E(PERM);
+ E(NOENT);
+ E(INTR);
+ E(BADF);
+ E(BUSY);
+ E(EXIST);
+ E(NOTDIR);
+ E(ISDIR);
+ E(INVAL);
+ E(NFILE);
+ E(MFILE);
+ E(FBIG);
+ E(NOSPC);
+ E(SPIPE);
+ E(ROFS);
+ E(NAMETOOLONG);
+ default:
+ err = UHI_EINVAL;
+ break;
+ case EFAULT:
report_fault(env);
}
- dst->uhi_st_dev = tswap16(src->st_dev);
- dst->uhi_st_ino = tswap16(src->st_ino);
- dst->uhi_st_mode = tswap32(src->st_mode);
- dst->uhi_st_nlink = tswap16(src->st_nlink);
- dst->uhi_st_uid = tswap16(src->st_uid);
- dst->uhi_st_gid = tswap16(src->st_gid);
- dst->uhi_st_rdev = tswap16(src->st_rdev);
- dst->uhi_st_size = tswap64(src->st_size);
- dst->uhi_st_atime = tswap64(src->st_atime);
- dst->uhi_st_mtime = tswap64(src->st_mtime);
- dst->uhi_st_ctime = tswap64(src->st_ctime);
-#ifdef _WIN32
- dst->uhi_st_blksize = 0;
- dst->uhi_st_blocks = 0;
-#else
- dst->uhi_st_blksize = tswap64(src->st_blksize);
- dst->uhi_st_blocks = tswap64(src->st_blocks);
-#endif
- unlock_user(dst, vaddr, len);
- return 0;
+#undef E
+
+ env->active_tc.gpr[2] = ret;
+ env->active_tc.gpr[3] = err;
}
-static int get_open_flags(target_ulong target_flags)
+static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err)
{
- int open_flags = 0;
+ QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat));
- if (target_flags & UHIOpen_RDWR) {
- open_flags |= O_RDWR;
- } else if (target_flags & UHIOpen_WRONLY) {
- open_flags |= O_WRONLY;
- } else {
- open_flags |= O_RDONLY;
+ if (!err) {
+ CPUMIPSState *env = cs->env_ptr;
+ target_ulong addr = env->active_tc.gpr[5];
+ UHIStat *dst = lock_user(VERIFY_WRITE, addr, sizeof(UHIStat), 1);
+ struct gdb_stat s;
+
+ if (!dst) {
+ report_fault(env);
+ }
+
+ memcpy(&s, dst, sizeof(struct gdb_stat));
+ memset(dst, 0, sizeof(UHIStat));
+
+ dst->uhi_st_dev = tswap16(be32_to_cpu(s.gdb_st_dev));
+ dst->uhi_st_ino = tswap16(be32_to_cpu(s.gdb_st_ino));
+ dst->uhi_st_mode = tswap32(be32_to_cpu(s.gdb_st_mode));
+ dst->uhi_st_nlink = tswap16(be32_to_cpu(s.gdb_st_nlink));
+ dst->uhi_st_uid = tswap16(be32_to_cpu(s.gdb_st_uid));
+ dst->uhi_st_gid = tswap16(be32_to_cpu(s.gdb_st_gid));
+ dst->uhi_st_rdev = tswap16(be32_to_cpu(s.gdb_st_rdev));
+ dst->uhi_st_size = tswap64(be64_to_cpu(s.gdb_st_size));
+ dst->uhi_st_atime = tswap64(be32_to_cpu(s.gdb_st_atime));
+ dst->uhi_st_mtime = tswap64(be32_to_cpu(s.gdb_st_mtime));
+ dst->uhi_st_ctime = tswap64(be32_to_cpu(s.gdb_st_ctime));
+ dst->uhi_st_blksize = tswap64(be64_to_cpu(s.gdb_st_blksize));
+ dst->uhi_st_blocks = tswap64(be64_to_cpu(s.gdb_st_blocks));
+
+ unlock_user(dst, addr, sizeof(UHIStat));
}
- open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
- open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0;
- open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0;
- open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0;
-
- return open_flags;
-}
-
-static int write_to_file(CPUMIPSState *env, target_ulong fd,
- target_ulong vaddr, target_ulong len)
-{
- int num_of_bytes;
- void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
- if (!dst) {
- report_fault(env);
- }
-
- num_of_bytes = write(fd, dst, len);
-
- unlock_user(dst, vaddr, 0);
- return num_of_bytes;
-}
-
-static int read_from_file(CPUMIPSState *env, target_ulong fd,
- target_ulong vaddr, target_ulong len)
-{
- int num_of_bytes;
- void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
- if (!dst) {
- report_fault(env);
- }
-
- num_of_bytes = read(fd, dst, len);
-
- unlock_user(dst, vaddr, len);
- return num_of_bytes;
+ uhi_cb(cs, ret, err);
}
static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
@@ -260,68 +240,59 @@ static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
void mips_semihosting(CPUMIPSState *env)
{
+ CPUState *cs = env_cpu(env);
target_ulong *gpr = env->active_tc.gpr;
const UHIOp op = gpr[25];
char *p, *p2;
switch (op) {
case UHI_exit:
- qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
+ gdb_exit(gpr[4]);
exit(gpr[4]);
+
case UHI_open:
- GET_TARGET_STRING(p, gpr[4]);
- if (!strcmp("/dev/stdin", p)) {
- gpr[2] = 0;
- } else if (!strcmp("/dev/stdout", p)) {
- gpr[2] = 1;
- } else if (!strcmp("/dev/stderr", p)) {
- gpr[2] = 2;
- } else {
- gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
- gpr[3] = errno_mips(errno);
+ {
+ int ret = -1;
+
+ GET_TARGET_STRING(p, gpr[4]);
+ if (!strcmp("/dev/stdin", p)) {
+ ret = 0;
+ } else if (!strcmp("/dev/stdout", p)) {
+ ret = 1;
+ } else if (!strcmp("/dev/stderr", p)) {
+ ret = 2;
+ }
+ FREE_TARGET_STRING(p, gpr[4]);
+
+ /* FIXME: reusing a guest fd doesn't seem correct. */
+ if (ret >= 0) {
+ gpr[2] = ret;
+ break;
+ }
+
+ semihost_sys_open(cs, uhi_cb, gpr[4], 0, gpr[5], gpr[6]);
}
- FREE_TARGET_STRING(p, gpr[4]);
break;
+
case UHI_close:
- if (gpr[4] < 3) {
- /* ignore closing stdin/stdout/stderr */
- gpr[2] = 0;
- return;
- }
- gpr[2] = close(gpr[4]);
- gpr[3] = errno_mips(errno);
+ semihost_sys_close(cs, uhi_cb, gpr[4]);
break;
case UHI_read:
- gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6]);
- gpr[3] = errno_mips(errno);
+ semihost_sys_read(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
break;
case UHI_write:
- gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6]);
- gpr[3] = errno_mips(errno);
+ semihost_sys_write(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
break;
case UHI_lseek:
- gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
- gpr[3] = errno_mips(errno);
+ semihost_sys_lseek(cs, uhi_cb, gpr[4], gpr[5], gpr[6]);
break;
case UHI_unlink:
- GET_TARGET_STRING(p, gpr[4]);
- gpr[2] = remove(p);
- gpr[3] = errno_mips(errno);
- FREE_TARGET_STRING(p, gpr[4]);
+ semihost_sys_remove(cs, uhi_cb, gpr[4], 0);
break;
case UHI_fstat:
- {
- struct stat sbuf;
- memset(&sbuf, 0, sizeof(sbuf));
- gpr[2] = fstat(gpr[4], &sbuf);
- gpr[3] = errno_mips(errno);
- if (gpr[2]) {
- return;
- }
- gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
- gpr[3] = errno_mips(errno);
- }
+ semihost_sys_fstat(cs, uhi_fstat_cb, gpr[4], gpr[5]);
break;
+
case UHI_argc:
gpr[2] = semihosting_get_argc();
break;
This separates guest file descriptors from host file descriptors, and utilizes shared infrastructure for integration with gdbstub. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- target/mips/tcg/sysemu/mips-semi.c | 219 +++++++++++++---------------- 1 file changed, 95 insertions(+), 124 deletions(-)