| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Hosted file support for semihosting syscalls. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2005, 2007 CodeSourcery. | 
					
						
							|  |  |  |  * Copyright (c) 2019 Linaro | 
					
						
							|  |  |  |  * Copyright © 2020 by Keith Packard <keithp@keithp.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: GPL-2.0-or-later | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2023-03-02 18:57:57 -08:00
										 |  |  | #include "gdbstub/syscalls.h"
 | 
					
						
							| 
									
										
										
										
											2022-05-01 17:21:00 -07:00
										 |  |  | #include "semihosting/semihost.h"
 | 
					
						
							| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  | #include "semihosting/guestfd.h"
 | 
					
						
							| 
									
										
										
										
											2023-12-06 16:12:12 +01:00
										 |  |  | #ifndef CONFIG_USER_ONLY
 | 
					
						
							| 
									
										
										
										
											2022-05-01 17:21:00 -07:00
										 |  |  | #include CONFIG_DEVICES
 | 
					
						
							| 
									
										
										
										
											2022-04-28 01:44:28 -07:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static GArray *guestfd_array; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-01 17:21:00 -07:00
										 |  |  | #ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING
 | 
					
						
							|  |  |  | GuestFD console_in_gf; | 
					
						
							|  |  |  | GuestFD console_out_gf; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void qemu_semihosting_guestfd_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* New entries zero-initialized, i.e. type GuestFDUnused */ | 
					
						
							|  |  |  |     guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING
 | 
					
						
							|  |  |  |     /* For ARM-compat, the console is in a separate namespace. */ | 
					
						
							|  |  |  |     if (use_gdb_syscalls()) { | 
					
						
							|  |  |  |         console_in_gf.type = GuestFDGDB; | 
					
						
							|  |  |  |         console_in_gf.hostfd = 0; | 
					
						
							|  |  |  |         console_out_gf.type = GuestFDGDB; | 
					
						
							|  |  |  |         console_out_gf.hostfd = 2; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         console_in_gf.type = GuestFDConsole; | 
					
						
							|  |  |  |         console_out_gf.type = GuestFDConsole; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     /* Otherwise, the stdio file descriptors apply. */ | 
					
						
							|  |  |  |     guestfd_array = g_array_set_size(guestfd_array, 3); | 
					
						
							|  |  |  | #ifndef CONFIG_USER_ONLY
 | 
					
						
							|  |  |  |     if (!use_gdb_syscalls()) { | 
					
						
							|  |  |  |         GuestFD *gf = &g_array_index(guestfd_array, GuestFD, 0); | 
					
						
							|  |  |  |         gf[0].type = GuestFDConsole; | 
					
						
							|  |  |  |         gf[1].type = GuestFDConsole; | 
					
						
							|  |  |  |         gf[2].type = GuestFDConsole; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     associate_guestfd(0, 0); | 
					
						
							|  |  |  |     associate_guestfd(1, 1); | 
					
						
							|  |  |  |     associate_guestfd(2, 2); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Allocate a new guest file descriptor and return it; if we | 
					
						
							|  |  |  |  * couldn't allocate a new fd then return -1. | 
					
						
							|  |  |  |  * This is a fairly simplistic implementation because we don't | 
					
						
							|  |  |  |  * expect that most semihosting guest programs will make very | 
					
						
							|  |  |  |  * heavy use of opening and closing fds. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int alloc_guestfd(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     guint i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ | 
					
						
							|  |  |  |     for (i = 1; i < guestfd_array->len; i++) { | 
					
						
							|  |  |  |         GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (gf->type == GuestFDUnused) { | 
					
						
							|  |  |  |             return i; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* All elements already in use: expand the array */ | 
					
						
							|  |  |  |     g_array_set_size(guestfd_array, i + 1); | 
					
						
							|  |  |  |     return i; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 09:22:18 -07:00
										 |  |  | static void do_dealloc_guestfd(GuestFD *gf) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     gf->type = GuestFDUnused; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Look up the guestfd in the data structure; return NULL | 
					
						
							|  |  |  |  * for out of bounds, but don't check whether the slot is unused. | 
					
						
							|  |  |  |  * This is used internally by the other guestfd functions. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static GuestFD *do_get_guestfd(int guestfd) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-05-01 17:21:00 -07:00
										 |  |  |     if (guestfd < 0 || guestfd >= guestfd_array->len) { | 
					
						
							| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return &g_array_index(guestfd_array, GuestFD, guestfd); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Given a guest file descriptor, get the associated struct. | 
					
						
							|  |  |  |  * If the fd is not valid, return NULL. This is the function | 
					
						
							|  |  |  |  * used by the various semihosting calls to validate a handle | 
					
						
							|  |  |  |  * from the guest. | 
					
						
							|  |  |  |  * Note: calling alloc_guestfd() or dealloc_guestfd() will | 
					
						
							|  |  |  |  * invalidate any GuestFD* obtained by calling this function. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | GuestFD *get_guestfd(int guestfd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GuestFD *gf = do_get_guestfd(guestfd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!gf || gf->type == GuestFDUnused) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return gf; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Associate the specified guest fd (which must have been | 
					
						
							|  |  |  |  * allocated via alloc_fd() and not previously used) with | 
					
						
							|  |  |  |  * the specified host/gdb fd. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void associate_guestfd(int guestfd, int hostfd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GuestFD *gf = do_get_guestfd(guestfd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(gf); | 
					
						
							|  |  |  |     gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; | 
					
						
							|  |  |  |     gf->hostfd = hostfd; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GuestFD *gf = do_get_guestfd(guestfd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(gf); | 
					
						
							|  |  |  |     gf->type = GuestFDStatic; | 
					
						
							|  |  |  |     gf->staticfile.data = data; | 
					
						
							|  |  |  |     gf->staticfile.len = len; | 
					
						
							|  |  |  |     gf->staticfile.off = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Deallocate the specified guest file descriptor. This doesn't | 
					
						
							|  |  |  |  * close the host fd, it merely undoes the work of alloc_fd(). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void dealloc_guestfd(int guestfd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     GuestFD *gf = do_get_guestfd(guestfd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(gf); | 
					
						
							| 
									
										
										
										
											2022-04-28 09:22:18 -07:00
										 |  |  |     do_dealloc_guestfd(gf); | 
					
						
							| 
									
										
										
										
											2022-04-27 21:38:02 -07:00
										 |  |  | } |