semihosting: Create semihost_sys_{stat,fstat}

These syscalls will be used by m68k and nios2 semihosting.

Reviewed-by: Luc Michel <lmichel@kalray.eu>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-04-29 15:45:25 -07:00
parent 90d8e0b09c
commit dffeb77566
2 changed files with 144 additions and 0 deletions

View File

@ -49,6 +49,13 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb,
gdb_syscall_complete_cb flen_cb,
int fd, target_ulong fstat_addr);
void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, target_ulong addr);
void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len,
target_ulong addr);
void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len);

View File

@ -63,6 +63,52 @@ static int validate_lock_user_string(char **pstr, CPUState *cs,
return ret;
}
/*
* TODO: Note that gdb always stores the stat structure big-endian.
* So far, that's ok, as the only two targets using this are also
* big-endian. Until we do something with gdb, also produce the
* same big-endian result from the host.
*/
static int copy_stat_to_user(CPUState *cs, target_ulong addr,
const struct stat *s)
{
CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
struct gdb_stat *p;
if (s->st_dev != (uint32_t)s->st_dev ||
s->st_ino != (uint32_t)s->st_ino) {
return -EOVERFLOW;
}
p = lock_user(VERIFY_WRITE, addr, sizeof(struct gdb_stat), 0);
if (!p) {
return -EFAULT;
}
p->gdb_st_dev = cpu_to_be32(s->st_dev);
p->gdb_st_ino = cpu_to_be32(s->st_ino);
p->gdb_st_mode = cpu_to_be32(s->st_mode);
p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
p->gdb_st_uid = cpu_to_be32(s->st_uid);
p->gdb_st_gid = cpu_to_be32(s->st_gid);
p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
p->gdb_st_size = cpu_to_be64(s->st_size);
#ifdef _WIN32
/* Windows stat is missing some fields. */
p->gdb_st_blksize = 0;
p->gdb_st_blocks = 0;
#else
p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
#endif
p->gdb_st_atime = cpu_to_be32(s->st_atime);
p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
unlock_user(p, addr, sizeof(struct gdb_stat));
return 0;
}
/*
* GDB semihosting syscall implementations.
*/
@ -133,6 +179,19 @@ static void gdb_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
gdb_do_syscall(complete, "fstat,%x,%x", (target_ulong)gf->hostfd, addr);
}
static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len,
target_ulong addr)
{
int len = validate_strlen(cs, fname, fname_len);
if (len < 0) {
complete(cs, -1, -len);
return;
}
gdb_do_syscall(complete, "stat,%s,%x", fname, len, addr);
}
static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len)
{
@ -321,6 +380,51 @@ static void host_flen(CPUState *cs, gdb_syscall_complete_cb complete,
}
}
static void host_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong addr)
{
struct stat buf;
int ret;
ret = fstat(gf->hostfd, &buf);
if (ret) {
complete(cs, -1, errno);
return;
}
ret = copy_stat_to_user(cs, addr, &buf);
complete(cs, ret ? -1 : 0, ret ? -ret : 0);
}
static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len,
target_ulong addr)
{
CPUArchState *env G_GNUC_UNUSED = cs->env_ptr;
struct stat buf;
char *name;
int ret, err;
ret = validate_lock_user_string(&name, cs, fname, fname_len);
if (ret < 0) {
complete(cs, -1, -ret);
return;
}
ret = stat(name, &buf);
if (ret) {
err = errno;
} else {
ret = copy_stat_to_user(cs, addr, &buf);
err = 0;
if (ret < 0) {
err = -ret;
ret = -1;
}
}
complete(cs, ret, err);
unlock_user(name, fname, 0);
}
static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len)
{
@ -629,6 +733,39 @@ void semihost_sys_flen(CPUState *cs, gdb_syscall_complete_cb fstat_cb,
}
}
void semihost_sys_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, target_ulong addr)
{
GuestFD *gf = get_guestfd(fd);
if (!gf) {
complete(cs, -1, EBADF);
return;
}
switch (gf->type) {
case GuestFDGDB:
gdb_fstat(cs, complete, gf, addr);
break;
case GuestFDHost:
host_fstat(cs, complete, gf, addr);
break;
case GuestFDStatic:
default:
g_assert_not_reached();
}
}
void semihost_sys_stat(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len,
target_ulong addr)
{
if (use_gdb_syscalls()) {
gdb_stat(cs, complete, fname, fname_len, addr);
} else {
host_stat(cs, complete, fname, fname_len, addr);
}
}
void semihost_sys_remove(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong fname, target_ulong fname_len)
{