xref: /qemu/semihosting/console.c (revision 57792106562417ba03a1ac0f2a5afc3eb63c5d9e)
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/cpu-common.h"
22  #include "exec/gdbstub.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  #include "hw/core/cpu.h"
30  
31  /* Access to this structure is protected by the BQL */
32  typedef struct SemihostingConsole {
33      CharBackend         backend;
34      Chardev             *chr;
35      GSList              *sleeping_cpus;
36      bool                got;
37      Fifo8               fifo;
38  } SemihostingConsole;
39  
40  static SemihostingConsole console;
41  
42  #define FIFO_SIZE   1024
43  
console_can_read(void * opaque)44  static int console_can_read(void *opaque)
45  {
46      SemihostingConsole *c = opaque;
47      g_assert(bql_locked());
48      return (int)fifo8_num_free(&c->fifo);
49  }
50  
console_wake_up(gpointer data,gpointer user_data)51  static void console_wake_up(gpointer data, gpointer user_data)
52  {
53      CPUState *cs = (CPUState *) data;
54      /* cpu_handle_halt won't know we have work so just unbung here */
55      cs->halted = 0;
56      qemu_cpu_kick(cs);
57  }
58  
console_read(void * opaque,const uint8_t * buf,int size)59  static void console_read(void *opaque, const uint8_t *buf, int size)
60  {
61      SemihostingConsole *c = opaque;
62      g_assert(bql_locked());
63      while (size-- && !fifo8_is_full(&c->fifo)) {
64          fifo8_push(&c->fifo, *buf++);
65      }
66      g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL);
67      c->sleeping_cpus = NULL;
68  }
69  
qemu_semihosting_console_ready(void)70  bool qemu_semihosting_console_ready(void)
71  {
72      SemihostingConsole *c = &console;
73  
74      g_assert(bql_locked());
75      return !fifo8_is_empty(&c->fifo);
76  }
77  
qemu_semihosting_console_block_until_ready(CPUState * cs)78  void qemu_semihosting_console_block_until_ready(CPUState *cs)
79  {
80      SemihostingConsole *c = &console;
81  
82      g_assert(bql_locked());
83  
84      /* Block if the fifo is completely empty. */
85      if (fifo8_is_empty(&c->fifo)) {
86          c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs);
87          cs->halted = 1;
88          cs->exception_index = EXCP_HALTED;
89          cpu_loop_exit(cs);
90          /* never returns */
91      }
92  }
93  
qemu_semihosting_console_read(CPUState * cs,void * buf,int len)94  int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
95  {
96      SemihostingConsole *c = &console;
97      int ret = 0;
98  
99      qemu_semihosting_console_block_until_ready(cs);
100  
101      /* Read until buffer full or fifo exhausted. */
102      do {
103          *(char *)(buf + ret) = fifo8_pop(&c->fifo);
104          ret++;
105      } while (ret < len && !fifo8_is_empty(&c->fifo));
106  
107      return ret;
108  }
109  
qemu_semihosting_console_write(void * buf,int len)110  int qemu_semihosting_console_write(void *buf, int len)
111  {
112      if (console.chr) {
113          int r = qemu_chr_write_all(console.chr, (uint8_t *)buf, len);
114          return r < 0 ? 0 : r;
115      } else {
116          return fwrite(buf, 1, len, stderr);
117      }
118  }
119  
qemu_semihosting_console_init(Chardev * chr)120  void qemu_semihosting_console_init(Chardev *chr)
121  {
122      console.chr = chr;
123      if  (chr) {
124          fifo8_create(&console.fifo, FIFO_SIZE);
125          qemu_chr_fe_init(&console.backend, chr, &error_abort);
126          qemu_chr_fe_set_handlers(&console.backend,
127                                   console_can_read,
128                                   console_read,
129                                   NULL, NULL, &console,
130                                   NULL, true);
131      }
132  
133      qemu_semihosting_guestfd_init();
134  }
135