| 
									
										
										
										
											2019-05-13 21:49:43 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Semihosting Console Support | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2015 Imagination Technologies | 
					
						
							|  |  |  |  * Copyright (c) 2019 Linaro Ltd | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This provides support for outputting to a semihosting console. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * While most semihosting implementations support reading and writing | 
					
						
							|  |  |  |  * to arbitrary file descriptors we treat the console as something | 
					
						
							|  |  |  |  * specifically for debugging interaction. This means messages can be | 
					
						
							|  |  |  |  * re-directed to gdb (if currently being used to debug) or even | 
					
						
							|  |  |  |  * re-directed elsewhere. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: GPL-2.0-or-later | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-05 13:54:49 +00:00
										 |  |  | #include "semihosting/semihost.h"
 | 
					
						
							|  |  |  | #include "semihosting/console.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-13 21:49:43 +01:00
										 |  |  | #include "exec/gdbstub.h"
 | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | #include "exec/exec-all.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-13 21:49:43 +01:00
										 |  |  | #include "qemu/log.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-14 15:30:14 +01:00
										 |  |  | #include "chardev/char.h"
 | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | #include "chardev/char-fe.h"
 | 
					
						
							|  |  |  | #include "qemu/main-loop.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							|  |  |  | #include "qemu/fifo8.h"
 | 
					
						
							| 
									
										
										
										
											2019-05-13 21:49:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 16:59:06 -07:00
										 |  |  | /* Access to this structure is protected by the BQL */ | 
					
						
							|  |  |  | typedef struct SemihostingConsole { | 
					
						
							|  |  |  |     CharBackend         backend; | 
					
						
							|  |  |  |     Chardev             *chr; | 
					
						
							|  |  |  |     GSList              *sleeping_cpus; | 
					
						
							|  |  |  |     bool                got; | 
					
						
							|  |  |  |     Fifo8               fifo; | 
					
						
							|  |  |  | } SemihostingConsole; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static SemihostingConsole console; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | #define FIFO_SIZE   1024
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int console_can_read(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SemihostingConsole *c = opaque; | 
					
						
							|  |  |  |     g_assert(qemu_mutex_iothread_locked()); | 
					
						
							| 
									
										
										
										
											2022-11-22 14:49:16 +01:00
										 |  |  |     return (int)fifo8_num_free(&c->fifo); | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void console_wake_up(gpointer data, gpointer user_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     CPUState *cs = (CPUState *) data; | 
					
						
							|  |  |  |     /* cpu_handle_halt won't know we have work so just unbung here */ | 
					
						
							|  |  |  |     cs->halted = 0; | 
					
						
							|  |  |  |     qemu_cpu_kick(cs); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void console_read(void *opaque, const uint8_t *buf, int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SemihostingConsole *c = opaque; | 
					
						
							|  |  |  |     g_assert(qemu_mutex_iothread_locked()); | 
					
						
							|  |  |  |     while (size-- && !fifo8_is_full(&c->fifo)) { | 
					
						
							|  |  |  |         fifo8_push(&c->fifo, *buf++); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     g_slist_foreach(c->sleeping_cpus, console_wake_up, NULL); | 
					
						
							|  |  |  |     c->sleeping_cpus = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 11:15:40 -07:00
										 |  |  | bool qemu_semihosting_console_ready(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SemihostingConsole *c = &console; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_assert(qemu_mutex_iothread_locked()); | 
					
						
							|  |  |  |     return !fifo8_is_empty(&c->fifo); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void qemu_semihosting_console_block_until_ready(CPUState *cs) | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | { | 
					
						
							|  |  |  |     SemihostingConsole *c = &console; | 
					
						
							| 
									
										
										
										
											2022-05-01 12:21:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  |     g_assert(qemu_mutex_iothread_locked()); | 
					
						
							| 
									
										
										
										
											2022-05-01 12:31:08 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Block if the fifo is completely empty. */ | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  |     if (fifo8_is_empty(&c->fifo)) { | 
					
						
							| 
									
										
										
										
											2022-05-01 12:21:19 -07:00
										 |  |  |         c->sleeping_cpus = g_slist_prepend(c->sleeping_cpus, cs); | 
					
						
							|  |  |  |         cs->halted = 1; | 
					
						
							|  |  |  |         cs->exception_index = EXCP_HALTED; | 
					
						
							|  |  |  |         cpu_loop_exit(cs); | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  |         /* never returns */ | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-05-02 11:15:40 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int qemu_semihosting_console_read(CPUState *cs, void *buf, int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     SemihostingConsole *c = &console; | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_semihosting_console_block_until_ready(cs); | 
					
						
							| 
									
										
										
										
											2022-05-01 12:31:08 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Read until buffer full or fifo exhausted. */ | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |         *(char *)(buf + ret) = fifo8_pop(&c->fifo); | 
					
						
							|  |  |  |         ret++; | 
					
						
							|  |  |  |     } while (ret < len && !fifo8_is_empty(&c->fifo)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 12:42:37 -07:00
										 |  |  | int qemu_semihosting_console_write(void *buf, int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (console.chr) { | 
					
						
							| 
									
										
										
										
											2022-07-25 15:05:13 +01:00
										 |  |  |         int r = qemu_chr_write_all(console.chr, (uint8_t *)buf, len); | 
					
						
							|  |  |  |         return r < 0 ? 0 : r; | 
					
						
							| 
									
										
										
										
											2022-05-01 12:42:37 -07:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         return fwrite(buf, 1, len, stderr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 16:59:06 -07:00
										 |  |  | void qemu_semihosting_console_init(Chardev *chr) | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-05-01 16:59:06 -07:00
										 |  |  |     console.chr = chr; | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  |     if  (chr) { | 
					
						
							|  |  |  |         fifo8_create(&console.fifo, FIFO_SIZE); | 
					
						
							|  |  |  |         qemu_chr_fe_init(&console.backend, chr, &error_abort); | 
					
						
							|  |  |  |         qemu_chr_fe_set_handlers(&console.backend, | 
					
						
							|  |  |  |                                  console_can_read, | 
					
						
							|  |  |  |                                  console_read, | 
					
						
							|  |  |  |                                  NULL, NULL, &console, | 
					
						
							|  |  |  |                                  NULL, true); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-05-01 17:21:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     qemu_semihosting_guestfd_init(); | 
					
						
							| 
									
										
										
										
											2019-11-04 12:42:30 -08:00
										 |  |  | } |