1 /* 2 * Semihosting Console Support 3 * 4 * Copyright (c) 2015 Imagination Technologies 5 * Copyright (c) 2019 Linaro Ltd 6 * 7 * This provides support for outputting to a semihosting console. 8 * 9 * While most semihosting implementations support reading and writing 10 * to arbitrary file descriptors we treat the console as something 11 * specifically for debugging interaction. This means messages can be 12 * re-directed to gdb (if currently being used to debug) or even 13 * re-directed elsewhere. 14 * 15 * SPDX-License-Identifier: GPL-2.0-or-later 16 */ 17 18 #include "qemu/osdep.h" 19 #include "semihosting/semihost.h" 20 #include "semihosting/console.h" 21 #include "exec/gdbstub.h" 22 #include "exec/exec-all.h" 23 #include "qemu/log.h" 24 #include "chardev/char.h" 25 #include "chardev/char-fe.h" 26 #include "qemu/main-loop.h" 27 #include "qapi/error.h" 28 #include "qemu/fifo8.h" 29 30 /* Access to this structure is protected by the BQL */ 31 typedef struct SemihostingConsole { 32 CharBackend backend; 33 Chardev *chr; 34 GSList *sleeping_cpus; 35 bool got; 36 Fifo8 fifo; 37 } SemihostingConsole; 38 39 static SemihostingConsole console; 40 41 int qemu_semihosting_log_out(const char *s, int len) 42 { 43 if (console.chr) { 44 return qemu_chr_write_all(console.chr, (uint8_t *) s, len); 45 } else { 46 return write(STDERR_FILENO, s, len); 47 } 48 } 49 50 #define FIFO_SIZE 1024 51 52 static int console_can_read(void *opaque) 53 { 54 SemihostingConsole *c = opaque; 55 int ret; 56 g_assert(qemu_mutex_iothread_locked()); 57 ret = (int) fifo8_num_free(&c->fifo); 58 return ret; 59 } 60 61 static void console_wake_up(gpointer data, gpointer user_data) 62 { 63 CPUState *cs = (CPUState *) data; 64 /* cpu_handle_halt won't know we have work so just unbung here */ 65 cs->halted = 0; 66 qemu_cpu_kick(cs); 67 } 68 69 static void console_read(void *opaque, const uint8_t *buf, int size) 70 { 71 SemihostingConsole *c = opaque; 72 g_assert(qemu_mutex_iothread_locked()); 73 while (size-- && !fifo8_is_full(&c->fifo)) { 74 fifo8_push(&c->fifo, *buf++); 75 } 76 g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); 77 c->sleeping_cpus = NULL; 78 } 79 80 bool qemu_semihosting_console_ready(void) 81 { 82 SemihostingConsole *c = &console; 83 84 g_assert(qemu_mutex_iothread_locked()); 85 return !fifo8_is_empty(&c->fifo); 86 } 87 88 void qemu_semihosting_console_block_until_ready(CPUState *cs) 89 { 90 SemihostingConsole *c = &console; 91 92 g_assert(qemu_mutex_iothread_locked()); 93 94 /* Block if the fifo is completely empty. */ 95 if (fifo8_is_empty(&c->fifo)) { 96 c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); 97 cs->halted = 1; 98 cs->exception_index = EXCP_HALTED; 99 cpu_loop_exit(cs); 100 /* never returns */ 101 } 102 } 103 104 int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) 105 { 106 SemihostingConsole *c = &console; 107 int ret = 0; 108 109 qemu_semihosting_console_block_until_ready(cs); 110 111 /* Read until buffer full or fifo exhausted. */ 112 do { 113 *(char *)(buf + ret) = fifo8_pop(&c->fifo); 114 ret++; 115 } while (ret < len && !fifo8_is_empty(&c->fifo)); 116 117 return ret; 118 } 119 120 int qemu_semihosting_console_write(void *buf, int len) 121 { 122 if (console.chr) { 123 return qemu_chr_write_all(console.chr, (uint8_t *)buf, len); 124 } else { 125 return fwrite(buf, 1, len, stderr); 126 } 127 } 128 129 void qemu_semihosting_console_init(Chardev *chr) 130 { 131 console.chr = chr; 132 if (chr) { 133 fifo8_create(&console.fifo, FIFO_SIZE); 134 qemu_chr_fe_init(&console.backend, chr, &error_abort); 135 qemu_chr_fe_set_handlers(&console.backend, 136 console_can_read, 137 console_read, 138 NULL, NULL, &console, 139 NULL, true); 140 } 141 142 qemu_semihosting_guestfd_init(); 143 } 144