]>
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 | ||
8de702cb KP |
41 | #define FIFO_SIZE 1024 |
42 | ||
8de702cb KP |
43 | static int console_can_read(void *opaque) |
44 | { | |
45 | SemihostingConsole *c = opaque; | |
46 | int ret; | |
47 | g_assert(qemu_mutex_iothread_locked()); | |
48 | ret = (int) fifo8_num_free(&c->fifo); | |
49 | return ret; | |
50 | } | |
51 | ||
52 | static void console_wake_up(gpointer data, gpointer user_data) | |
53 | { | |
54 | CPUState *cs = (CPUState *) data; | |
55 | /* cpu_handle_halt won't know we have work so just unbung here */ | |
56 | cs->halted = 0; | |
57 | qemu_cpu_kick(cs); | |
58 | } | |
59 | ||
60 | static void console_read(void *opaque, const uint8_t *buf, int size) | |
61 | { | |
62 | SemihostingConsole *c = opaque; | |
63 | g_assert(qemu_mutex_iothread_locked()); | |
64 | while (size-- && !fifo8_is_full(&c->fifo)) { | |
65 | fifo8_push(&c->fifo, *buf++); | |
66 | } | |
67 | g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); | |
68 | c->sleeping_cpus = NULL; | |
69 | } | |
70 | ||
1b9177f7 RH |
71 | bool qemu_semihosting_console_ready(void) |
72 | { | |
73 | SemihostingConsole *c = &console; | |
74 | ||
75 | g_assert(qemu_mutex_iothread_locked()); | |
76 | return !fifo8_is_empty(&c->fifo); | |
77 | } | |
78 | ||
79 | void qemu_semihosting_console_block_until_ready(CPUState *cs) | |
8de702cb | 80 | { |
8de702cb | 81 | SemihostingConsole *c = &console; |
3367d452 | 82 | |
8de702cb | 83 | g_assert(qemu_mutex_iothread_locked()); |
e7fb6f32 RH |
84 | |
85 | /* Block if the fifo is completely empty. */ | |
8de702cb | 86 | if (fifo8_is_empty(&c->fifo)) { |
3367d452 RH |
87 | c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); |
88 | cs->halted = 1; | |
89 | cs->exception_index = EXCP_HALTED; | |
90 | cpu_loop_exit(cs); | |
8de702cb KP |
91 | /* never returns */ |
92 | } | |
1b9177f7 RH |
93 | } |
94 | ||
95 | int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) | |
96 | { | |
97 | SemihostingConsole *c = &console; | |
98 | int ret = 0; | |
99 | ||
100 | qemu_semihosting_console_block_until_ready(cs); | |
e7fb6f32 RH |
101 | |
102 | /* Read until buffer full or fifo exhausted. */ | |
103 | do { | |
104 | *(char *)(buf + ret) = fifo8_pop(&c->fifo); | |
105 | ret++; | |
106 | } while (ret < len && !fifo8_is_empty(&c->fifo)); | |
107 | ||
108 | return ret; | |
8de702cb KP |
109 | } |
110 | ||
cd66f20f RH |
111 | int qemu_semihosting_console_write(void *buf, int len) |
112 | { | |
113 | if (console.chr) { | |
aed04e63 PM |
114 | int r = qemu_chr_write_all(console.chr, (uint8_t *)buf, len); |
115 | return r < 0 ? 0 : r; | |
cd66f20f RH |
116 | } else { |
117 | return fwrite(buf, 1, len, stderr); | |
118 | } | |
119 | } | |
120 | ||
fb08790b | 121 | void qemu_semihosting_console_init(Chardev *chr) |
8de702cb | 122 | { |
fb08790b | 123 | console.chr = chr; |
8de702cb KP |
124 | if (chr) { |
125 | fifo8_create(&console.fifo, FIFO_SIZE); | |
126 | qemu_chr_fe_init(&console.backend, chr, &error_abort); | |
127 | qemu_chr_fe_set_handlers(&console.backend, | |
128 | console_can_read, | |
129 | console_read, | |
130 | NULL, NULL, &console, | |
131 | NULL, true); | |
132 | } | |
e4a4aaa5 RH |
133 | |
134 | qemu_semihosting_guestfd_init(); | |
8de702cb | 135 | } |