2 * Semihosting Console Support
4 * Copyright (c) 2015 Imagination Technologies
5 * Copyright (c) 2019 Linaro Ltd
7 * This provides support for outputting to a semihosting console.
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.
15 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include "qemu/osdep.h"
20 #include "hw/semihosting/semihost.h"
21 #include "hw/semihosting/console.h"
22 #include "exec/gdbstub.h"
23 #include "exec/exec-all.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"
32 int qemu_semihosting_log_out(const char *s
, int len
)
34 Chardev
*chardev
= semihosting_get_chardev();
36 return qemu_chr_write_all(chardev
, (uint8_t *) s
, len
);
38 return write(STDERR_FILENO
, s
, len
);
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.
47 static GString
*copy_user_string(CPUArchState
*env
, target_ulong addr
)
49 CPUState
*cpu
= env_cpu(env
);
50 GString
*s
= g_string_sized_new(128);
54 if (cpu_memory_rw_debug(cpu
, addr
++, &c
, 1, 0) == 0) {
56 s
= g_string_append_c(s
, c
);
59 qemu_log_mask(LOG_GUEST_ERROR
,
60 "%s: passed inaccessible address " TARGET_FMT_lx
,
69 static void semihosting_cb(CPUState
*cs
, target_ulong ret
, target_ulong err
)
71 if (ret
== (target_ulong
) -1) {
72 qemu_log("%s: gdb console output failed ("TARGET_FMT_ld
")",
77 int qemu_semihosting_console_outs(CPUArchState
*env
, target_ulong addr
)
79 GString
*s
= copy_user_string(env
, addr
);
82 if (use_gdb_syscalls()) {
83 gdb_do_syscall(semihosting_cb
, "write,2,%x,%x", addr
, s
->len
);
85 out
= qemu_semihosting_log_out(s
->str
, s
->len
);
88 g_string_free(s
, true);
92 void qemu_semihosting_console_outc(CPUArchState
*env
, target_ulong addr
)
94 CPUState
*cpu
= env_cpu(env
);
97 if (cpu_memory_rw_debug(cpu
, addr
, &c
, 1, 0) == 0) {
98 if (use_gdb_syscalls()) {
99 gdb_do_syscall(semihosting_cb
, "write,2,%x,%x", addr
, 1);
101 qemu_semihosting_log_out((const char *) &c
, 1);
104 qemu_log_mask(LOG_GUEST_ERROR
,
105 "%s: passed inaccessible address " TARGET_FMT_lx
,
110 #define FIFO_SIZE 1024
112 /* Access to this structure is protected by the BQL */
113 typedef struct SemihostingConsole
{
115 GSList
*sleeping_cpus
;
118 } SemihostingConsole
;
120 static SemihostingConsole console
;
122 static int console_can_read(void *opaque
)
124 SemihostingConsole
*c
= opaque
;
126 g_assert(qemu_mutex_iothread_locked());
127 ret
= (int) fifo8_num_free(&c
->fifo
);
131 static void console_wake_up(gpointer data
, gpointer user_data
)
133 CPUState
*cs
= (CPUState
*) data
;
134 /* cpu_handle_halt won't know we have work so just unbung here */
139 static void console_read(void *opaque
, const uint8_t *buf
, int size
)
141 SemihostingConsole
*c
= opaque
;
142 g_assert(qemu_mutex_iothread_locked());
143 while (size
-- && !fifo8_is_full(&c
->fifo
)) {
144 fifo8_push(&c
->fifo
, *buf
++);
146 g_slist_foreach(c
->sleeping_cpus
, console_wake_up
, NULL
);
147 c
->sleeping_cpus
= NULL
;
150 target_ulong
qemu_semihosting_console_inc(CPUArchState
*env
)
153 SemihostingConsole
*c
= &console
;
154 g_assert(qemu_mutex_iothread_locked());
155 g_assert(current_cpu
);
156 if (fifo8_is_empty(&c
->fifo
)) {
157 c
->sleeping_cpus
= g_slist_prepend(c
->sleeping_cpus
, current_cpu
);
158 current_cpu
->halted
= 1;
159 current_cpu
->exception_index
= EXCP_HALTED
;
160 cpu_loop_exit(current_cpu
);
163 ch
= fifo8_pop(&c
->fifo
);
164 return (target_ulong
) ch
;
167 void qemu_semihosting_console_init(void)
169 Chardev
*chr
= semihosting_get_chardev();
172 fifo8_create(&console
.fifo
, FIFO_SIZE
);
173 qemu_chr_fe_init(&console
.backend
, chr
, &error_abort
);
174 qemu_chr_fe_set_handlers(&console
.backend
,
177 NULL
, NULL
, &console
,