]>
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" | |
19 | #include "cpu.h" | |
4e7f9032 | 20 | #include "hw/semihosting/semihost.h" |
a331c6d7 AB |
21 | #include "hw/semihosting/console.h" |
22 | #include "exec/gdbstub.h" | |
8de702cb | 23 | #include "exec/exec-all.h" |
a331c6d7 | 24 | #include "qemu/log.h" |
4e7f9032 | 25 | #include "chardev/char.h" |
8de702cb KP |
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" | |
a331c6d7 AB |
31 | |
32 | int qemu_semihosting_log_out(const char *s, int len) | |
33 | { | |
4e7f9032 AB |
34 | Chardev *chardev = semihosting_get_chardev(); |
35 | if (chardev) { | |
36 | return qemu_chr_write_all(chardev, (uint8_t *) s, len); | |
37 | } else { | |
38 | return write(STDERR_FILENO, s, len); | |
39 | } | |
a331c6d7 AB |
40 | } |
41 | ||
42 | /* | |
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 | |
78e24848 | 45 | * in time. Copy string until we find a 0 or address error. |
a331c6d7 | 46 | */ |
78e24848 | 47 | static GString *copy_user_string(CPUArchState *env, target_ulong addr) |
a331c6d7 | 48 | { |
29a0af61 | 49 | CPUState *cpu = env_cpu(env); |
78e24848 | 50 | GString *s = g_string_sized_new(128); |
a331c6d7 | 51 | uint8_t c; |
a331c6d7 AB |
52 | |
53 | do { | |
54 | if (cpu_memory_rw_debug(cpu, addr++, &c, 1, 0) == 0) { | |
7d2d6522 KF |
55 | if (c) { |
56 | s = g_string_append_c(s, c); | |
57 | } | |
a331c6d7 AB |
58 | } else { |
59 | qemu_log_mask(LOG_GUEST_ERROR, | |
60 | "%s: passed inaccessible address " TARGET_FMT_lx, | |
61 | __func__, addr); | |
78e24848 | 62 | break; |
a331c6d7 | 63 | } |
78e24848 | 64 | } while (c!=0); |
a331c6d7 AB |
65 | |
66 | return s; | |
67 | } | |
68 | ||
69 | static void semihosting_cb(CPUState *cs, target_ulong ret, target_ulong err) | |
70 | { | |
71 | if (ret == (target_ulong) -1) { | |
72 | qemu_log("%s: gdb console output failed ("TARGET_FMT_ld")", | |
73 | __func__, err); | |
74 | } | |
75 | } | |
76 | ||
78e24848 | 77 | int qemu_semihosting_console_outs(CPUArchState *env, target_ulong addr) |
a331c6d7 | 78 | { |
78e24848 | 79 | GString *s = copy_user_string(env, addr); |
a331c6d7 AB |
80 | int out = s->len; |
81 | ||
82 | if (use_gdb_syscalls()) { | |
83 | gdb_do_syscall(semihosting_cb, "write,2,%x,%x", addr, s->len); | |
84 | } else { | |
85 | out = qemu_semihosting_log_out(s->str, s->len); | |
86 | } | |
87 | ||
88 | g_string_free(s, true); | |
89 | return out; | |
90 | } | |
78e24848 AB |
91 | |
92 | void qemu_semihosting_console_outc(CPUArchState *env, target_ulong addr) | |
93 | { | |
94 | CPUState *cpu = env_cpu(env); | |
95 | uint8_t c; | |
96 | ||
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); | |
100 | } else { | |
101 | qemu_semihosting_log_out((const char *) &c, 1); | |
102 | } | |
103 | } else { | |
104 | qemu_log_mask(LOG_GUEST_ERROR, | |
105 | "%s: passed inaccessible address " TARGET_FMT_lx, | |
106 | __func__, addr); | |
107 | } | |
108 | } | |
8de702cb KP |
109 | |
110 | #define FIFO_SIZE 1024 | |
111 | ||
112 | /* Access to this structure is protected by the BQL */ | |
113 | typedef struct SemihostingConsole { | |
114 | CharBackend backend; | |
115 | GSList *sleeping_cpus; | |
116 | bool got; | |
117 | Fifo8 fifo; | |
118 | } SemihostingConsole; | |
119 | ||
120 | static SemihostingConsole console; | |
121 | ||
122 | static int console_can_read(void *opaque) | |
123 | { | |
124 | SemihostingConsole *c = opaque; | |
125 | int ret; | |
126 | g_assert(qemu_mutex_iothread_locked()); | |
127 | ret = (int) fifo8_num_free(&c->fifo); | |
128 | return ret; | |
129 | } | |
130 | ||
131 | static void console_wake_up(gpointer data, gpointer user_data) | |
132 | { | |
133 | CPUState *cs = (CPUState *) data; | |
134 | /* cpu_handle_halt won't know we have work so just unbung here */ | |
135 | cs->halted = 0; | |
136 | qemu_cpu_kick(cs); | |
137 | } | |
138 | ||
139 | static void console_read(void *opaque, const uint8_t *buf, int size) | |
140 | { | |
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++); | |
145 | } | |
146 | g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); | |
147 | c->sleeping_cpus = NULL; | |
148 | } | |
149 | ||
150 | target_ulong qemu_semihosting_console_inc(CPUArchState *env) | |
151 | { | |
152 | uint8_t ch; | |
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); | |
161 | /* never returns */ | |
162 | } | |
163 | ch = fifo8_pop(&c->fifo); | |
164 | return (target_ulong) ch; | |
165 | } | |
166 | ||
167 | void qemu_semihosting_console_init(void) | |
168 | { | |
169 | Chardev *chr = semihosting_get_chardev(); | |
170 | ||
171 | if (chr) { | |
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, | |
175 | console_can_read, | |
176 | console_read, | |
177 | NULL, NULL, &console, | |
178 | NULL, true); | |
179 | } | |
180 | } |