qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

semihosting: add qemu_semihosting_console_inc for SYS_READC

Provides a blocking call to read a character from the console using
semihosting.chardev, if specified. This takes some careful command
line options to use stdio successfully as the serial ports, monitor
and semihost all want to use stdio. Here's a sample set of command
line options which share stdio between semihost, monitor and serial
ports:

qemu \
-chardev stdio,mux=on,id=stdio0 \
-serial chardev:stdio0 \
-semihosting-config enable=on,chardev=stdio0 \
-mon chardev=stdio0,mode=readline

This creates a chardev hooked to stdio and then connects all of the
subsystems to it. A shorter mechanism would be good to hear about.

Signed-off-by: Keith Packard <keithp@keithp.com>
Message-Id: <20191104204230.12249-1-keithp@keithp.com>
[AJB: fixed up deadlock, minor commit title reword]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Keith Packard <keithp@keithp.com>
Tested-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

authored by

Keith Packard and committed by
Alex Bennée
8de702cb 4ff5ef9e

+134 -2
+79
hw/semihosting/console.c
··· 20 20 #include "hw/semihosting/semihost.h" 21 21 #include "hw/semihosting/console.h" 22 22 #include "exec/gdbstub.h" 23 + #include "exec/exec-all.h" 23 24 #include "qemu/log.h" 24 25 #include "chardev/char.h" 26 + #include <pthread.h> 27 + #include "chardev/char-fe.h" 28 + #include "sysemu/sysemu.h" 29 + #include "qemu/main-loop.h" 30 + #include "qapi/error.h" 31 + #include "qemu/fifo8.h" 25 32 26 33 int qemu_semihosting_log_out(const char *s, int len) 27 34 { ··· 98 105 __func__, addr); 99 106 } 100 107 } 108 + 109 + #define FIFO_SIZE 1024 110 + 111 + /* Access to this structure is protected by the BQL */ 112 + typedef struct SemihostingConsole { 113 + CharBackend backend; 114 + GSList *sleeping_cpus; 115 + bool got; 116 + Fifo8 fifo; 117 + } SemihostingConsole; 118 + 119 + static SemihostingConsole console; 120 + 121 + static int console_can_read(void *opaque) 122 + { 123 + SemihostingConsole *c = opaque; 124 + int ret; 125 + g_assert(qemu_mutex_iothread_locked()); 126 + ret = (int) fifo8_num_free(&c->fifo); 127 + return ret; 128 + } 129 + 130 + static void console_wake_up(gpointer data, gpointer user_data) 131 + { 132 + CPUState *cs = (CPUState *) data; 133 + /* cpu_handle_halt won't know we have work so just unbung here */ 134 + cs->halted = 0; 135 + qemu_cpu_kick(cs); 136 + } 137 + 138 + static void console_read(void *opaque, const uint8_t *buf, int size) 139 + { 140 + SemihostingConsole *c = opaque; 141 + g_assert(qemu_mutex_iothread_locked()); 142 + while (size-- && !fifo8_is_full(&c->fifo)) { 143 + fifo8_push(&c->fifo, *buf++); 144 + } 145 + g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); 146 + c->sleeping_cpus = NULL; 147 + } 148 + 149 + target_ulong qemu_semihosting_console_inc(CPUArchState *env) 150 + { 151 + uint8_t ch; 152 + SemihostingConsole *c = &console; 153 + g_assert(qemu_mutex_iothread_locked()); 154 + g_assert(current_cpu); 155 + if (fifo8_is_empty(&c->fifo)) { 156 + c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, current_cpu); 157 + current_cpu->halted = 1; 158 + current_cpu->exception_index = EXCP_HALTED; 159 + cpu_loop_exit(current_cpu); 160 + /* never returns */ 161 + } 162 + ch = fifo8_pop(&c->fifo); 163 + return (target_ulong) ch; 164 + } 165 + 166 + void qemu_semihosting_console_init(void) 167 + { 168 + Chardev *chr = semihosting_get_chardev(); 169 + 170 + if (chr) { 171 + fifo8_create(&console.fifo, FIFO_SIZE); 172 + qemu_chr_fe_init(&console.backend, chr, &error_abort); 173 + qemu_chr_fe_set_handlers(&console.backend, 174 + console_can_read, 175 + console_read, 176 + NULL, NULL, &console, 177 + NULL, true); 178 + } 179 + }
+16
include/hw/semihosting/console.h
··· 38 38 void qemu_semihosting_console_outc(CPUArchState *env, target_ulong c); 39 39 40 40 /** 41 + * qemu_semihosting_console_inc: 42 + * @env: CPUArchState 43 + * 44 + * Receive single character from debug console. This may be the remote 45 + * gdb session if a softmmu guest is currently being debugged. As this 46 + * call may block if no data is available we suspend the CPU and will 47 + * re-execute the instruction when data is there. Therefore two 48 + * conditions must be met: 49 + * - CPUState is synchronized before calling this function 50 + * - pc is only updated once the character is successfully returned 51 + * 52 + * Returns: character read OR cpu_loop_exit! 53 + */ 54 + target_ulong qemu_semihosting_console_inc(CPUArchState *env); 55 + 56 + /** 41 57 * qemu_semihosting_log_out: 42 58 * @s: pointer to string 43 59 * @len: length of string
+4
include/hw/semihosting/semihost.h
··· 56 56 { 57 57 return NULL; 58 58 } 59 + static inline void qemu_semihosting_console_init(void) 60 + { 61 + } 59 62 #else /* !CONFIG_USER_ONLY */ 60 63 bool semihosting_enabled(void); 61 64 SemihostingTarget semihosting_get_target(void); ··· 68 71 void qemu_semihosting_enable(void); 69 72 int qemu_semihosting_config_options(const char *opt); 70 73 void qemu_semihosting_connect_chardevs(void); 74 + void qemu_semihosting_console_init(void); 71 75 #endif /* CONFIG_USER_ONLY */ 72 76 73 77 #endif /* SEMIHOST_H */
+27
linux-user/arm/semihost.c
··· 14 14 #include "cpu.h" 15 15 #include "hw/semihosting/console.h" 16 16 #include "qemu.h" 17 + #include <termios.h> 17 18 18 19 int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) 19 20 { ··· 47 48 } 48 49 } 49 50 } 51 + 52 + /* 53 + * For linux-user we can safely block. However as we want to return as 54 + * soon as a character is read we need to tweak the termio to disable 55 + * line buffering. We restore the old mode afterwards in case the 56 + * program is expecting more normal behaviour. This is slow but 57 + * nothing using semihosting console reading is expecting to be fast. 58 + */ 59 + target_ulong qemu_semihosting_console_inc(CPUArchState *env) 60 + { 61 + uint8_t c; 62 + struct termios old_tio, new_tio; 63 + 64 + /* Disable line-buffering and echo */ 65 + tcgetattr(STDIN_FILENO, &old_tio); 66 + new_tio = old_tio; 67 + new_tio.c_lflag &= (~ICANON & ~ECHO); 68 + tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); 69 + 70 + c = getchar(); 71 + 72 + /* restore config */ 73 + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); 74 + 75 + return (target_ulong) c; 76 + }
+4
stubs/semihost.c
··· 69 69 void qemu_semihosting_connect_chardevs(void) 70 70 { 71 71 } 72 + 73 + void qemu_semihosting_console_init(void) 74 + { 75 + }
+1 -2
target/arm/arm-semi.c
··· 802 802 803 803 return guestfd_fns[gf->type].readfn(cpu, gf, arg1, len); 804 804 case TARGET_SYS_READC: 805 - qemu_log_mask(LOG_UNIMP, "%s: SYS_READC not implemented", __func__); 806 - return 0; 805 + return qemu_semihosting_console_inc(env); 807 806 case TARGET_SYS_ISTTY: 808 807 GET_ARG(0); 809 808
+3
vl.c
··· 4238 4238 qemu_opts_foreach(qemu_find_opts("mon"), 4239 4239 mon_init_func, NULL, &error_fatal); 4240 4240 4241 + /* connect semihosting console input if requested */ 4242 + qemu_semihosting_console_init(); 4243 + 4241 4244 if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) 4242 4245 exit(1); 4243 4246 if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0)