@@ -53,4 +53,20 @@ int qemu_semihosting_console_write(void *buf, 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 */
@@ -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,
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 */
@@ -77,10 +77,17 @@ static void console_read(void *opaque, const uint8_t *buf, int size)
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;
- int ret = 0;
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);
/* 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. */
do {
@@ -550,6 +550,21 @@ static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
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.
*/
@@ -658,6 +673,34 @@ static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete,
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.
*/
@@ -936,3 +979,30 @@ void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
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
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. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- include/semihosting/console.h | 16 ++++++++ include/semihosting/syscalls.h | 3 ++ semihosting/console.c | 19 ++++++++- semihosting/syscalls.c | 70 ++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-)