semihosting: Split out semihost_sys_lseek

Split out the non-ARM specific portions of SYS_SEEK to a
reusable function.  This handles all GuestFD.  Isolate the
curious ARM-specific return value processing to a new
callback, common_semi_seek_cb.

Expand the internal type of the offset to int64_t, and
provide the whence argument, which will be required by
m68k and nios2 semihosting.

Note that gdb_do_syscall %x reads target_ulong, not int.

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-28 12:04:44 -07:00
parent 40f1219a8b
commit 9a89470449
4 changed files with 102 additions and 38 deletions

View file

@ -41,6 +41,11 @@
#define GDB_ENAMETOOLONG 91
#define GDB_EUNKNOWN 9999
/* For gdb file i/o remote protocol lseek whence. */
#define GDB_SEEK_SET 0
#define GDB_SEEK_CUR 1
#define GDB_SEEK_END 2
/* For gdb file i/o stat/fstat. */
typedef uint32_t gdb_mode_t;
typedef uint32_t gdb_time_t;

View file

@ -39,4 +39,7 @@ void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong buf, target_ulong len);
void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, int64_t off, int gdb_whence);
#endif /* SEMIHOSTING_SYSCALLS_H */

View file

@ -224,16 +224,6 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
#else
syscall_err = err;
#endif
} else {
/* Fixup syscalls that use nonstardard return conventions. */
target_ulong reg0 = common_semi_arg(cs, 0);
switch (reg0) {
case TARGET_SYS_SEEK:
ret = 0;
break;
default:
break;
}
}
common_semi_set_ret(cs, ret);
}
@ -257,6 +247,18 @@ static void common_semi_rw_cb(CPUState *cs, target_ulong ret, target_ulong err)
common_semi_set_ret(cs, arg2 - ret);
}
/*
* SYS_SEEK returns 0 on success, not the resulting offset.
*/
static void common_semi_seek_cb(CPUState *cs, target_ulong ret,
target_ulong err)
{
if (!err) {
ret = 0;
}
common_semi_cb(cs, ret, err);
}
/*
* Return an address in target memory of 64 bytes where the remote
* gdb should write its stat struct. (The format of this structure
@ -290,7 +292,6 @@ common_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
* via common_semi_cb.
*/
typedef void sys_isattyfn(CPUState *cs, GuestFD *gf);
typedef void sys_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset);
typedef void sys_flenfn(CPUState *cs, GuestFD *gf);
static void host_isattyfn(CPUState *cs, GuestFD *gf)
@ -298,12 +299,6 @@ static void host_isattyfn(CPUState *cs, GuestFD *gf)
common_semi_cb(cs, isatty(gf->hostfd), 0);
}
static void host_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset)
{
off_t ret = lseek(gf->hostfd, offset, SEEK_SET);
common_semi_cb(cs, ret, ret == -1 ? errno : 0);
}
static void host_flenfn(CPUState *cs, GuestFD *gf)
{
struct stat buf;
@ -320,11 +315,6 @@ static void gdb_isattyfn(CPUState *cs, GuestFD *gf)
gdb_do_syscall(common_semi_cb, "isatty,%x", gf->hostfd);
}
static void gdb_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset)
{
gdb_do_syscall(common_semi_cb, "lseek,%x,%x,0", gf->hostfd, offset);
}
static void gdb_flenfn(CPUState *cs, GuestFD *gf)
{
gdb_do_syscall(common_semi_flen_cb, "fstat,%x,%x",
@ -353,12 +343,6 @@ static void staticfile_isattyfn(CPUState *cs, GuestFD *gf)
common_semi_cb(cs, 0, 0);
}
static void staticfile_seekfn(CPUState *cs, GuestFD *gf, target_ulong offset)
{
gf->staticfile.off = offset;
common_semi_cb(cs, 0, 0);
}
static void staticfile_flenfn(CPUState *cs, GuestFD *gf)
{
common_semi_cb(cs, gf->staticfile.len, 0);
@ -366,24 +350,20 @@ static void staticfile_flenfn(CPUState *cs, GuestFD *gf)
typedef struct GuestFDFunctions {
sys_isattyfn *isattyfn;
sys_seekfn *seekfn;
sys_flenfn *flenfn;
} GuestFDFunctions;
static const GuestFDFunctions guestfd_fns[] = {
[GuestFDHost] = {
.isattyfn = host_isattyfn,
.seekfn = host_seekfn,
.flenfn = host_flenfn,
},
[GuestFDGDB] = {
.isattyfn = gdb_isattyfn,
.seekfn = gdb_seekfn,
.flenfn = gdb_flenfn,
},
[GuestFDStatic] = {
.isattyfn = staticfile_isattyfn,
.seekfn = staticfile_seekfn,
.flenfn = staticfile_flenfn,
},
};
@ -520,12 +500,7 @@ void do_common_semihosting(CPUState *cs)
case TARGET_SYS_SEEK:
GET_ARG(0);
GET_ARG(1);
gf = get_guestfd(arg0);
if (!gf) {
goto do_badf;
}
guestfd_fns[gf->type].seekfn(cs, gf, arg1);
semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET);
break;
case TARGET_SYS_FLEN:

View file

@ -114,6 +114,13 @@ static void gdb_write(CPUState *cs, gdb_syscall_complete_cb complete,
(target_ulong)gf->hostfd, buf, len);
}
static void gdb_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, int64_t off, int gdb_whence)
{
gdb_do_syscall(complete, "lseek,%x,%lx,%x",
(target_ulong)gf->hostfd, off, (target_ulong)gdb_whence);
}
/*
* Host semihosting syscall implementations.
*/
@ -216,6 +223,29 @@ static void host_write(CPUState *cs, gdb_syscall_complete_cb complete,
unlock_user(ptr, buf, 0);
}
static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, int64_t off, int whence)
{
/* So far, all hosts use the same values. */
QEMU_BUILD_BUG_ON(GDB_SEEK_SET != SEEK_SET);
QEMU_BUILD_BUG_ON(GDB_SEEK_CUR != SEEK_CUR);
QEMU_BUILD_BUG_ON(GDB_SEEK_END != SEEK_END);
off_t ret = off;
int err = 0;
if (ret == off) {
ret = lseek(gf->hostfd, ret, whence);
if (ret == -1) {
err = errno;
}
} else {
ret = -1;
err = EINVAL;
}
complete(cs, ret, err);
}
/*
* Static file semihosting syscall implementations.
*/
@ -241,6 +271,33 @@ static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete,
unlock_user(ptr, buf, len);
}
static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, int64_t off, int gdb_whence)
{
int64_t ret;
switch (gdb_whence) {
case GDB_SEEK_SET:
ret = off;
break;
case GDB_SEEK_CUR:
ret = gf->staticfile.off + off;
break;
case GDB_SEEK_END:
ret = gf->staticfile.len + off;
break;
default:
ret = -1;
break;
}
if (ret >= 0 && ret <= gf->staticfile.len) {
gf->staticfile.off = ret;
complete(cs, ret, 0);
} else {
complete(cs, -1, EINVAL);
}
}
/*
* Syscall entry points.
*/
@ -356,3 +413,27 @@ void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
complete(cs, -1, EBADF);
}
}
void semihost_sys_lseek(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, int64_t off, int gdb_whence)
{
GuestFD *gf = get_guestfd(fd);
if (!gf) {
complete(cs, -1, EBADF);
return;
}
switch (gf->type) {
case GuestFDGDB:
gdb_lseek(cs, complete, gf, off, gdb_whence);
return;
case GuestFDHost:
host_lseek(cs, complete, gf, off, gdb_whence);
break;
case GuestFDStatic:
staticfile_lseek(cs, complete, gf, off, gdb_whence);
break;
default:
g_assert_not_reached();
}
}