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_ENAMETOOLONG 91
#define GDB_EUNKNOWN 9999 #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. */ /* For gdb file i/o stat/fstat. */
typedef uint32_t gdb_mode_t; typedef uint32_t gdb_mode_t;
typedef uint32_t gdb_time_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, void semihost_sys_write_gf(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, target_ulong buf, target_ulong len); 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 */ #endif /* SEMIHOSTING_SYSCALLS_H */

View File

@ -224,16 +224,6 @@ static void common_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
#else #else
syscall_err = err; syscall_err = err;
#endif #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); 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); 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 * Return an address in target memory of 64 bytes where the remote
* gdb should write its stat struct. (The format of this structure * 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. * via common_semi_cb.
*/ */
typedef void sys_isattyfn(CPUState *cs, GuestFD *gf); 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); typedef void sys_flenfn(CPUState *cs, GuestFD *gf);
static void host_isattyfn(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); 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) static void host_flenfn(CPUState *cs, GuestFD *gf)
{ {
struct stat buf; 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); 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) static void gdb_flenfn(CPUState *cs, GuestFD *gf)
{ {
gdb_do_syscall(common_semi_flen_cb, "fstat,%x,%x", 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); 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) static void staticfile_flenfn(CPUState *cs, GuestFD *gf)
{ {
common_semi_cb(cs, gf->staticfile.len, 0); common_semi_cb(cs, gf->staticfile.len, 0);
@ -366,24 +350,20 @@ static void staticfile_flenfn(CPUState *cs, GuestFD *gf)
typedef struct GuestFDFunctions { typedef struct GuestFDFunctions {
sys_isattyfn *isattyfn; sys_isattyfn *isattyfn;
sys_seekfn *seekfn;
sys_flenfn *flenfn; sys_flenfn *flenfn;
} GuestFDFunctions; } GuestFDFunctions;
static const GuestFDFunctions guestfd_fns[] = { static const GuestFDFunctions guestfd_fns[] = {
[GuestFDHost] = { [GuestFDHost] = {
.isattyfn = host_isattyfn, .isattyfn = host_isattyfn,
.seekfn = host_seekfn,
.flenfn = host_flenfn, .flenfn = host_flenfn,
}, },
[GuestFDGDB] = { [GuestFDGDB] = {
.isattyfn = gdb_isattyfn, .isattyfn = gdb_isattyfn,
.seekfn = gdb_seekfn,
.flenfn = gdb_flenfn, .flenfn = gdb_flenfn,
}, },
[GuestFDStatic] = { [GuestFDStatic] = {
.isattyfn = staticfile_isattyfn, .isattyfn = staticfile_isattyfn,
.seekfn = staticfile_seekfn,
.flenfn = staticfile_flenfn, .flenfn = staticfile_flenfn,
}, },
}; };
@ -520,12 +500,7 @@ void do_common_semihosting(CPUState *cs)
case TARGET_SYS_SEEK: case TARGET_SYS_SEEK:
GET_ARG(0); GET_ARG(0);
GET_ARG(1); GET_ARG(1);
semihost_sys_lseek(cs, common_semi_seek_cb, arg0, arg1, GDB_SEEK_SET);
gf = get_guestfd(arg0);
if (!gf) {
goto do_badf;
}
guestfd_fns[gf->type].seekfn(cs, gf, arg1);
break; break;
case TARGET_SYS_FLEN: 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); (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. * Host semihosting syscall implementations.
*/ */
@ -216,6 +223,29 @@ static void host_write(CPUState *cs, gdb_syscall_complete_cb complete,
unlock_user(ptr, buf, 0); 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. * 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); 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. * Syscall entry points.
*/ */
@ -356,3 +413,27 @@ void semihost_sys_write(CPUState *cs, gdb_syscall_complete_cb complete,
complete(cs, -1, EBADF); 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();
}
}