|  | /* | 
|  | * Semihosting Console Support | 
|  | * | 
|  | * Copyright (c) 2015 Imagination Technologies | 
|  | * Copyright (c) 2019 Linaro Ltd | 
|  | * | 
|  | * This provides support for outputting to a semihosting console. | 
|  | * | 
|  | * While most semihosting implementations support reading and writing | 
|  | * to arbitrary file descriptors we treat the console as something | 
|  | * specifically for debugging interaction. This means messages can be | 
|  | * re-directed to gdb (if currently being used to debug) or even | 
|  | * re-directed elsewhere. | 
|  | * | 
|  | * SPDX-License-Identifier: GPL-2.0-or-later | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "semihosting/semihost.h" | 
|  | #include "semihosting/console.h" | 
|  | #include "exec/gdbstub.h" | 
|  | #include "exec/exec-all.h" | 
|  | #include "qemu/log.h" | 
|  | #include "chardev/char.h" | 
|  | #include "chardev/char-fe.h" | 
|  | #include "qemu/main-loop.h" | 
|  | #include "qapi/error.h" | 
|  | #include "qemu/fifo8.h" | 
|  |  | 
|  | /* Access to this structure is protected by the BQL */ | 
|  | typedef struct SemihostingConsole { | 
|  | CharBackend         backend; | 
|  | Chardev             *chr; | 
|  | GSList              *sleeping_cpus; | 
|  | bool                got; | 
|  | Fifo8               fifo; | 
|  | } SemihostingConsole; | 
|  |  | 
|  | static SemihostingConsole console; | 
|  |  | 
|  | #define FIFO_SIZE   1024 | 
|  |  | 
|  | static int console_can_read(void *opaque) | 
|  | { | 
|  | SemihostingConsole *c = opaque; | 
|  | g_assert(qemu_mutex_iothread_locked()); | 
|  | return (int)fifo8_num_free(&c->fifo); | 
|  | } | 
|  |  | 
|  | static void console_wake_up(gpointer data, gpointer user_data) | 
|  | { | 
|  | CPUState *cs = (CPUState *) data; | 
|  | /* cpu_handle_halt won't know we have work so just unbung here */ | 
|  | cs->halted = 0; | 
|  | qemu_cpu_kick(cs); | 
|  | } | 
|  |  | 
|  | static void console_read(void *opaque, const uint8_t *buf, int size) | 
|  | { | 
|  | SemihostingConsole *c = opaque; | 
|  | g_assert(qemu_mutex_iothread_locked()); | 
|  | while (size-- && !fifo8_is_full(&c->fifo)) { | 
|  | fifo8_push(&c->fifo, *buf++); | 
|  | } | 
|  | g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); | 
|  | c->sleeping_cpus = NULL; | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | g_assert(qemu_mutex_iothread_locked()); | 
|  |  | 
|  | /* Block if the fifo is completely empty. */ | 
|  | if (fifo8_is_empty(&c->fifo)) { | 
|  | c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); | 
|  | cs->halted = 1; | 
|  | cs->exception_index = EXCP_HALTED; | 
|  | 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 { | 
|  | *(char *)(buf + ret) = fifo8_pop(&c->fifo); | 
|  | ret++; | 
|  | } while (ret < len && !fifo8_is_empty(&c->fifo)); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int qemu_semihosting_console_write(void *buf, int len) | 
|  | { | 
|  | if (console.chr) { | 
|  | int r = qemu_chr_write_all(console.chr, (uint8_t *)buf, len); | 
|  | return r < 0 ? 0 : r; | 
|  | } else { | 
|  | return fwrite(buf, 1, len, stderr); | 
|  | } | 
|  | } | 
|  |  | 
|  | void qemu_semihosting_console_init(Chardev *chr) | 
|  | { | 
|  | console.chr = chr; | 
|  | if  (chr) { | 
|  | fifo8_create(&console.fifo, FIFO_SIZE); | 
|  | qemu_chr_fe_init(&console.backend, chr, &error_abort); | 
|  | qemu_chr_fe_set_handlers(&console.backend, | 
|  | console_can_read, | 
|  | console_read, | 
|  | NULL, NULL, &console, | 
|  | NULL, true); | 
|  | } | 
|  |  | 
|  | qemu_semihosting_guestfd_init(); | 
|  | } |