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