qemu/semihosting/console.c
<<
>>
Prefs
   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
  30int qemu_semihosting_log_out(const char *s, int len)
  31{
  32    Chardev *chardev = semihosting_get_chardev();
  33    if (chardev) {
  34        return qemu_chr_write_all(chardev, (uint8_t *) s, len);
  35    } else {
  36        return write(STDERR_FILENO, s, len);
  37    }
  38}
  39
  40/*
  41 * A re-implementation of lock_user_string that we can use locally
  42 * instead of relying on softmmu-semi. Hopefully we can deprecate that
  43 * in time. Copy string until we find a 0 or address error.
  44 */
  45static GString *copy_user_string(CPUArchState *env, target_ulong addr)
  46{
  47    CPUState *cpu = env_cpu(env);
  48    GString *s = g_string_sized_new(128);
  49    uint8_t c;
  50
  51    do {
  52        if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) {
  53            if (c) {
  54                s = g_string_append_c(s, c);
  55            }
  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}
 179