Sleeping in the semihosting code is problematic as we deadlock the whole system. This includes issuing a "quit" via the HMP or presumably if gdbserver got involved. What we really want is to return to the main loop and gt woken up when there is data to process. We can then re-execute the instruction which will succeed this time.
[AJB: So this at least solves the hang of not being able to quit system emulation while blocked. However there are two things we still need to ensure: - the PC has not advanced until completion so we can redo the instruction - we actually wake up the CPU in console_read In my testcase console_read never seems to get called. I've tried with both an external pipe loopback and using the ringbuf: qemu-system-aarch64 -M virt --display none -cpu cortex-a57 -kernel systest-a64-with-console.axf -semihosting-config enable=on,chardev=sh0 -serial mon:stdio -chardev ringbuf,logfile=foo,id=sh0 ] Signed-off-by: Alex Bennée <alex.ben...@linaro.org> --- include/exec/cpu-all.h | 1 + hw/semihosting/console.c | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index e96781a4559..093d7a76edd 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -31,6 +31,7 @@ #define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ #define EXCP_YIELD 0x10004 /* cpu wants to yield timeslice to another */ #define EXCP_ATOMIC 0x10005 /* stop-the-world and emulate atomic */ +#define EXCP_BLOCKED 0x10006 /* cpu is blocked (semihosting) */ /* some important defines: * diff --git a/hw/semihosting/console.c b/hw/semihosting/console.c index 4db68d62270..bda457a0608 100644 --- a/hw/semihosting/console.c +++ b/hw/semihosting/console.c @@ -20,6 +20,7 @@ #include "hw/semihosting/semihost.h" #include "hw/semihosting/console.h" #include "exec/gdbstub.h" +#include "exec/exec-all.h" #include "qemu/log.h" #include "chardev/char.h" #include <pthread.h> @@ -109,50 +110,49 @@ void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) typedef struct SemihostingConsole { CharBackend backend; - pthread_mutex_t mutex; - pthread_cond_t cond; + CPUState *sleeping_cpu; bool got; Fifo8 fifo; } SemihostingConsole; -static SemihostingConsole console = { - .mutex = PTHREAD_MUTEX_INITIALIZER, - .cond = PTHREAD_COND_INITIALIZER -}; +static SemihostingConsole console; static int console_can_read(void *opaque) { SemihostingConsole *c = opaque; int ret; - pthread_mutex_lock(&c->mutex); + g_assert(qemu_mutex_iothread_locked()); ret = (int) fifo8_num_free(&c->fifo); - pthread_mutex_unlock(&c->mutex); return ret; } static void console_read(void *opaque, const uint8_t *buf, int size) { SemihostingConsole *c = opaque; - pthread_mutex_lock(&c->mutex); + g_assert(qemu_mutex_iothread_locked()); while (size-- && !fifo8_is_full(&c->fifo)) { fifo8_push(&c->fifo, *buf++); } - pthread_cond_broadcast(&c->cond); - pthread_mutex_unlock(&c->mutex); + if (c->sleeping_cpu) { + cpu_resume(c->sleeping_cpu); + } } target_ulong qemu_semihosting_console_inc(CPUArchState *env) { uint8_t ch; SemihostingConsole *c = &console; - qemu_mutex_unlock_iothread(); - pthread_mutex_lock(&c->mutex); - while (fifo8_is_empty(&c->fifo)) { - pthread_cond_wait(&c->cond, &c->mutex); + g_assert(qemu_mutex_iothread_locked()); + g_assert(current_cpu); + if (fifo8_is_empty(&c->fifo)) { + c->sleeping_cpu = current_cpu; + c->sleeping_cpu->stop = true; + c->sleeping_cpu->exception_index = EXCP_BLOCKED; + cpu_loop_exit(c->sleeping_cpu); + /* never returns */ } + c->sleeping_cpu = NULL; ch = fifo8_pop(&c->fifo); - pthread_mutex_unlock(&c->mutex); - qemu_mutex_lock_iothread(); return (target_ulong) ch; } -- 2.20.1