]>
Commit | Line | Data |
---|---|---|
a331c6d7 AB |
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" | |
6b5fe137 PMD |
19 | #include "semihosting/semihost.h" |
20 | #include "semihosting/console.h" | |
a331c6d7 | 21 | #include "exec/gdbstub.h" |
8de702cb | 22 | #include "exec/exec-all.h" |
a331c6d7 | 23 | #include "qemu/log.h" |
4e7f9032 | 24 | #include "chardev/char.h" |
8de702cb | 25 | #include "chardev/char-fe.h" |
8de702cb KP |
26 | #include "qemu/main-loop.h" |
27 | #include "qapi/error.h" | |
28 | #include "qemu/fifo8.h" | |
a331c6d7 | 29 | |
fb08790b RH |
30 | /* Access to this structure is protected by the BQL */ |
31 | typedef struct SemihostingConsole { | |
32 | CharBackend backend; | |
33 | Chardev *chr; | |
34 | GSList *sleeping_cpus; | |
35 | bool got; | |
36 | Fifo8 fifo; | |
37 | } SemihostingConsole; | |
38 | ||
39 | static SemihostingConsole console; | |
40 | ||
a331c6d7 AB |
41 | int qemu_semihosting_log_out(const char *s, int len) |
42 | { | |
fb08790b RH |
43 | if (console.chr) { |
44 | return qemu_chr_write_all(console.chr, (uint8_t *) s, len); | |
4e7f9032 AB |
45 | } else { |
46 | return write(STDERR_FILENO, s, len); | |
47 | } | |
a331c6d7 AB |
48 | } |
49 | ||
8de702cb KP |
50 | #define FIFO_SIZE 1024 |
51 | ||
8de702cb KP |
52 | static int console_can_read(void *opaque) |
53 | { | |
54 | SemihostingConsole *c = opaque; | |
55 | int ret; | |
56 | g_assert(qemu_mutex_iothread_locked()); | |
57 | ret = (int) fifo8_num_free(&c->fifo); | |
58 | return ret; | |
59 | } | |
60 | ||
61 | static void console_wake_up(gpointer data, gpointer user_data) | |
62 | { | |
63 | CPUState *cs = (CPUState *) data; | |
64 | /* cpu_handle_halt won't know we have work so just unbung here */ | |
65 | cs->halted = 0; | |
66 | qemu_cpu_kick(cs); | |
67 | } | |
68 | ||
69 | static void console_read(void *opaque, const uint8_t *buf, int size) | |
70 | { | |
71 | SemihostingConsole *c = opaque; | |
72 | g_assert(qemu_mutex_iothread_locked()); | |
73 | while (size-- && !fifo8_is_full(&c->fifo)) { | |
74 | fifo8_push(&c->fifo, *buf++); | |
75 | } | |
76 | g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); | |
77 | c->sleeping_cpus = NULL; | |
78 | } | |
79 | ||
1b9177f7 RH |
80 | bool qemu_semihosting_console_ready(void) |
81 | { | |
82 | SemihostingConsole *c = &console; | |
83 | ||
84 | g_assert(qemu_mutex_iothread_locked()); | |
85 | return !fifo8_is_empty(&c->fifo); | |
86 | } | |
87 | ||
88 | void qemu_semihosting_console_block_until_ready(CPUState *cs) | |
8de702cb | 89 | { |
8de702cb | 90 | SemihostingConsole *c = &console; |
3367d452 | 91 | |
8de702cb | 92 | g_assert(qemu_mutex_iothread_locked()); |
e7fb6f32 RH |
93 | |
94 | /* Block if the fifo is completely empty. */ | |
8de702cb | 95 | if (fifo8_is_empty(&c->fifo)) { |
3367d452 RH |
96 | c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); |
97 | cs->halted = 1; | |
98 | cs->exception_index = EXCP_HALTED; | |
99 | cpu_loop_exit(cs); | |
8de702cb KP |
100 | /* never returns */ |
101 | } | |
1b9177f7 RH |
102 | } |
103 | ||
104 | int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) | |
105 | { | |
106 | SemihostingConsole *c = &console; | |
107 | int ret = 0; | |
108 | ||
109 | qemu_semihosting_console_block_until_ready(cs); | |
e7fb6f32 RH |
110 | |
111 | /* Read until buffer full or fifo exhausted. */ | |
112 | do { | |
113 | *(char *)(buf + ret) = fifo8_pop(&c->fifo); | |
114 | ret++; | |
115 | } while (ret < len && !fifo8_is_empty(&c->fifo)); | |
116 | ||
117 | return ret; | |
8de702cb KP |
118 | } |
119 | ||
cd66f20f RH |
120 | int qemu_semihosting_console_write(void *buf, int len) |
121 | { | |
122 | if (console.chr) { | |
123 | return qemu_chr_write_all(console.chr, (uint8_t *)buf, len); | |
124 | } else { | |
125 | return fwrite(buf, 1, len, stderr); | |
126 | } | |
127 | } | |
128 | ||
fb08790b | 129 | void qemu_semihosting_console_init(Chardev *chr) |
8de702cb | 130 | { |
fb08790b | 131 | console.chr = chr; |
8de702cb KP |
132 | if (chr) { |
133 | fifo8_create(&console.fifo, FIFO_SIZE); | |
134 | qemu_chr_fe_init(&console.backend, chr, &error_abort); | |
135 | qemu_chr_fe_set_handlers(&console.backend, | |
136 | console_can_read, | |
137 | console_read, | |
138 | NULL, NULL, &console, | |
139 | NULL, true); | |
140 | } | |
e4a4aaa5 RH |
141 | |
142 | qemu_semihosting_guestfd_init(); | |
8de702cb | 143 | } |