semihosting: Create semihost_sys_poll_one

This will be used for implementing the xtensa select_one
system call.  Choose "poll" over "select" so that we can
reuse Glib's g_poll constants and to avoid struct timeval.

Reviewed-by: Luc Michel <lmichel@kalray.eu>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-05-02 11:15:40 -07:00
parent 2d010c2719
commit 1b9177f749
4 changed files with 106 additions and 2 deletions

View File

@ -53,4 +53,20 @@ int qemu_semihosting_console_write(void *buf, int len);
*/ */
int qemu_semihosting_log_out(const char *s, int len); int qemu_semihosting_log_out(const char *s, int len);
/*
* qemu_semihosting_console_block_until_ready:
* @cs: CPUState
*
* If no data is available we suspend the CPU and will re-execute the
* instruction when data is available.
*/
void qemu_semihosting_console_block_until_ready(CPUState *cs);
/**
* qemu_semihosting_console_ready:
*
* Return true if characters are available for read; does not block.
*/
bool qemu_semihosting_console_ready(void);
#endif /* SEMIHOST_CONSOLE_H */ #endif /* SEMIHOST_CONSOLE_H */

View File

@ -69,4 +69,7 @@ void semihost_sys_system(CPUState *cs, gdb_syscall_complete_cb complete,
void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
target_ulong tv_addr, target_ulong tz_addr); target_ulong tv_addr, target_ulong tz_addr);
void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, GIOCondition cond, int timeout);
#endif /* SEMIHOSTING_SYSCALLS_H */ #endif /* SEMIHOSTING_SYSCALLS_H */

View File

@ -77,10 +77,17 @@ static void console_read(void *opaque, const uint8_t *buf, int size)
c->sleeping_cpus = NULL; c->sleeping_cpus = NULL;
} }
int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) bool qemu_semihosting_console_ready(void)
{
SemihostingConsole *c = &console;
g_assert(qemu_mutex_iothread_locked());
return !fifo8_is_empty(&c->fifo);
}
void qemu_semihosting_console_block_until_ready(CPUState *cs)
{ {
SemihostingConsole *c = &console; SemihostingConsole *c = &console;
int ret = 0;
g_assert(qemu_mutex_iothread_locked()); g_assert(qemu_mutex_iothread_locked());
@ -92,6 +99,14 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
cpu_loop_exit(cs); cpu_loop_exit(cs);
/* never returns */ /* never returns */
} }
}
int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
{
SemihostingConsole *c = &console;
int ret = 0;
qemu_semihosting_console_block_until_ready(cs);
/* Read until buffer full or fifo exhausted. */ /* Read until buffer full or fifo exhausted. */
do { do {

View File

@ -520,6 +520,21 @@ static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
unlock_user(p, tv_addr, sizeof(struct gdb_timeval)); unlock_user(p, tv_addr, sizeof(struct gdb_timeval));
} }
#ifndef CONFIG_USER_ONLY
static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, GIOCondition cond, int timeout)
{
/*
* Since this is only used by xtensa in system mode, and stdio is
* handled through GuestFDConsole, and there are no semihosting
* system calls for sockets and the like, that means this descriptor
* must be a normal file. Normal files never block and are thus
* always ready.
*/
complete(cs, cond & (G_IO_IN | G_IO_OUT), 0);
}
#endif
/* /*
* Static file semihosting syscall implementations. * Static file semihosting syscall implementations.
*/ */
@ -628,6 +643,34 @@ static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
complete(cs, ret ? -1 : 0, ret ? -ret : 0); complete(cs, ret ? -1 : 0, ret ? -ret : 0);
} }
#ifndef CONFIG_USER_ONLY
static void console_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
GuestFD *gf, GIOCondition cond, int timeout)
{
/* The semihosting console does not support urgent data or errors. */
cond &= G_IO_IN | G_IO_OUT;
/*
* Since qemu_semihosting_console_write never blocks, we can
* consider output always ready -- leave G_IO_OUT alone.
* All that remains is to conditionally signal input ready.
* Since output ready causes an immediate return, only block
* for G_IO_IN alone.
*
* TODO: Implement proper timeout. For now, only support
* indefinite wait or immediate poll.
*/
if (cond == G_IO_IN && timeout < 0) {
qemu_semihosting_console_block_until_ready(cs);
/* We returned -- input must be ready. */
} else if ((cond & G_IO_IN) && !qemu_semihosting_console_ready()) {
cond &= ~G_IO_IN;
}
complete(cs, cond, 0);
}
#endif
/* /*
* Syscall entry points. * Syscall entry points.
*/ */
@ -906,3 +949,30 @@ void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
host_gettimeofday(cs, complete, tv_addr, tz_addr); host_gettimeofday(cs, complete, tv_addr, tz_addr);
} }
} }
#ifndef CONFIG_USER_ONLY
void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, GIOCondition cond, int timeout)
{
GuestFD *gf = get_guestfd(fd);
if (!gf) {
complete(cs, G_IO_NVAL, 1);
return;
}
switch (gf->type) {
case GuestFDGDB:
complete(cs, G_IO_NVAL, 1);
break;
case GuestFDHost:
host_poll_one(cs, complete, gf, cond, timeout);
break;
case GuestFDConsole:
console_poll_one(cs, complete, gf, cond, timeout);
break;
case GuestFDStatic:
default:
g_assert_not_reached();
}
}
#endif