qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at master 178 lines 5.1 kB view raw
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 "cpu.h" 20#include "hw/semihosting/semihost.h" 21#include "hw/semihosting/console.h" 22#include "exec/gdbstub.h" 23#include "exec/exec-all.h" 24#include "qemu/log.h" 25#include "chardev/char.h" 26#include "chardev/char-fe.h" 27#include "sysemu/sysemu.h" 28#include "qemu/main-loop.h" 29#include "qapi/error.h" 30#include "qemu/fifo8.h" 31 32int qemu_semihosting_log_out(const char *s, int len) 33{ 34 Chardev *chardev = semihosting_get_chardev(); 35 if (chardev) { 36 return qemu_chr_write_all(chardev, (uint8_t *) s, len); 37 } else { 38 return write(STDERR_FILENO, s, len); 39 } 40} 41 42/* 43 * A re-implementation of lock_user_string that we can use locally 44 * instead of relying on softmmu-semi. Hopefully we can deprecate that 45 * in time. Copy string until we find a 0 or address error. 46 */ 47static GString *copy_user_string(CPUArchState *env, target_ulong addr) 48{ 49 CPUState *cpu = env_cpu(env); 50 GString *s = g_string_sized_new(128); 51 uint8_t c; 52 53 do { 54 if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) { 55 s = g_string_append_c(s, c); 56 } else { 57 qemu_log_mask(LOG_GUEST_ERROR, 58 "%s: passed inaccessible address " TARGET_FMT_lx, 59 __func__, addr); 60 break; 61 } 62 } while (c!=0); 63 64 return s; 65} 66 67static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err) 68{ 69 if (ret == (target_ulong) -1) { 70 qemu_log("%s: gdb console output failed ("TARGET_FMT_ld")", 71 __func__, err); 72 } 73} 74 75int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) 76{ 77 GString *s = copy_user_string(env, addr); 78 int out = s->len; 79 80 if (use_gdb_syscalls()) { 81 gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len); 82 } else { 83 out = qemu_semihosting_log_out(s->str, s->len); 84 } 85 86 g_string_free(s, true); 87 return out; 88} 89 90void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) 91{ 92 CPUState *cpu = env_cpu(env); 93 uint8_t c; 94 95 if (cpu_memory_rw_debug(cpu, addr, &c, 1, 0) == 0) { 96 if (use_gdb_syscalls()) { 97 gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, 1); 98 } else { 99 qemu_semihosting_log_out((const char *) &c, 1); 100 } 101 } else { 102 qemu_log_mask(LOG_GUEST_ERROR, 103 "%s: passed inaccessible address " TARGET_FMT_lx, 104 __func__, addr); 105 } 106} 107 108#define FIFO_SIZE 1024 109 110/* Access to this structure is protected by the BQL */ 111typedef struct SemihostingConsole { 112 CharBackend backend; 113 GSList *sleeping_cpus; 114 bool got; 115 Fifo8 fifo; 116} SemihostingConsole; 117 118static SemihostingConsole console; 119 120static int console_can_read(void *opaque) 121{ 122 SemihostingConsole *c = opaque; 123 int ret; 124 g_assert(qemu_mutex_iothread_locked()); 125 ret = (int) fifo8_num_free(&c->fifo); 126 return ret; 127} 128 129static void console_wake_up(gpointer data, gpointer user_data) 130{ 131 CPUState *cs = (CPUState *) data; 132 /* cpu_handle_halt won't know we have work so just unbung here */ 133 cs->halted = 0; 134 qemu_cpu_kick(cs); 135} 136 137static void console_read(void *opaque, const uint8_t *buf, int size) 138{ 139 SemihostingConsole *c = opaque; 140 g_assert(qemu_mutex_iothread_locked()); 141 while (size-- && !fifo8_is_full(&c->fifo)) { 142 fifo8_push(&c->fifo, *buf++); 143 } 144 g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); 145 c->sleeping_cpus = NULL; 146} 147 148target_ulong qemu_semihosting_console_inc(CPUArchState *env) 149{ 150 uint8_t ch; 151 SemihostingConsole *c = &console; 152 g_assert(qemu_mutex_iothread_locked()); 153 g_assert(current_cpu); 154 if (fifo8_is_empty(&c->fifo)) { 155 c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, current_cpu); 156 current_cpu->halted = 1; 157 current_cpu->exception_index = EXCP_HALTED; 158 cpu_loop_exit(current_cpu); 159 /* never returns */ 160 } 161 ch = fifo8_pop(&c->fifo); 162 return (target_ulong) ch; 163} 164 165void qemu_semihosting_console_init(void) 166{ 167 Chardev *chr = semihosting_get_chardev(); 168 169 if (chr) { 170 fifo8_create(&console.fifo, FIFO_SIZE); 171 qemu_chr_fe_init(&console.backend, chr, &error_abort); 172 qemu_chr_fe_set_handlers(&console.backend, 173 console_can_read, 174 console_read, 175 NULL, NULL, &console, 176 NULL, true); 177 } 178}