1 /* 2 * Hosted file support for semihosting syscalls. 3 * 4 * Copyright (c) 2005, 2007 CodeSourcery. 5 * Copyright (c) 2019 Linaro 6 * Copyright © 2020 by Keith Packard <keithp@keithp.com> 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include "qemu/osdep.h" 12 #include "gdbstub/syscalls.h" 13 #include "semihosting/semihost.h" 14 #include "semihosting/guestfd.h" 15 #ifdef CONFIG_USER_ONLY 16 #include "qemu.h" 17 #else 18 #include "semihosting/softmmu-uaccess.h" 19 #include CONFIG_DEVICES 20 #endif 21 22 static GArray *guestfd_array; 23 24 #ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING 25 GuestFD console_in_gf; 26 GuestFD console_out_gf; 27 #endif 28 29 void qemu_semihosting_guestfd_init(void) 30 { 31 /* New entries zero-initialized, i.e. type GuestFDUnused */ 32 guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD)); 33 34 #ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING 35 /* For ARM-compat, the console is in a separate namespace. */ 36 if (use_gdb_syscalls()) { 37 console_in_gf.type = GuestFDGDB; 38 console_in_gf.hostfd = 0; 39 console_out_gf.type = GuestFDGDB; 40 console_out_gf.hostfd = 2; 41 } else { 42 console_in_gf.type = GuestFDConsole; 43 console_out_gf.type = GuestFDConsole; 44 } 45 #else 46 /* Otherwise, the stdio file descriptors apply. */ 47 guestfd_array = g_array_set_size(guestfd_array, 3); 48 #ifndef CONFIG_USER_ONLY 49 if (!use_gdb_syscalls()) { 50 GuestFD *gf = &g_array_index(guestfd_array, GuestFD, 0); 51 gf[0].type = GuestFDConsole; 52 gf[1].type = GuestFDConsole; 53 gf[2].type = GuestFDConsole; 54 return; 55 } 56 #endif 57 associate_guestfd(0, 0); 58 associate_guestfd(1, 1); 59 associate_guestfd(2, 2); 60 #endif 61 } 62 63 /* 64 * Allocate a new guest file descriptor and return it; if we 65 * couldn't allocate a new fd then return -1. 66 * This is a fairly simplistic implementation because we don't 67 * expect that most semihosting guest programs will make very 68 * heavy use of opening and closing fds. 69 */ 70 int alloc_guestfd(void) 71 { 72 guint i; 73 74 /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */ 75 for (i = 1; i < guestfd_array->len; i++) { 76 GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i); 77 78 if (gf->type == GuestFDUnused) { 79 return i; 80 } 81 } 82 83 /* All elements already in use: expand the array */ 84 g_array_set_size(guestfd_array, i + 1); 85 return i; 86 } 87 88 static void do_dealloc_guestfd(GuestFD *gf) 89 { 90 gf->type = GuestFDUnused; 91 } 92 93 /* 94 * Look up the guestfd in the data structure; return NULL 95 * for out of bounds, but don't check whether the slot is unused. 96 * This is used internally by the other guestfd functions. 97 */ 98 static GuestFD *do_get_guestfd(int guestfd) 99 { 100 if (guestfd < 0 || guestfd >= guestfd_array->len) { 101 return NULL; 102 } 103 104 return &g_array_index(guestfd_array, GuestFD, guestfd); 105 } 106 107 /* 108 * Given a guest file descriptor, get the associated struct. 109 * If the fd is not valid, return NULL. This is the function 110 * used by the various semihosting calls to validate a handle 111 * from the guest. 112 * Note: calling alloc_guestfd() or dealloc_guestfd() will 113 * invalidate any GuestFD* obtained by calling this function. 114 */ 115 GuestFD *get_guestfd(int guestfd) 116 { 117 GuestFD *gf = do_get_guestfd(guestfd); 118 119 if (!gf || gf->type == GuestFDUnused) { 120 return NULL; 121 } 122 return gf; 123 } 124 125 /* 126 * Associate the specified guest fd (which must have been 127 * allocated via alloc_fd() and not previously used) with 128 * the specified host/gdb fd. 129 */ 130 void associate_guestfd(int guestfd, int hostfd) 131 { 132 GuestFD *gf = do_get_guestfd(guestfd); 133 134 assert(gf); 135 gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost; 136 gf->hostfd = hostfd; 137 } 138 139 void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len) 140 { 141 GuestFD *gf = do_get_guestfd(guestfd); 142 143 assert(gf); 144 gf->type = GuestFDStatic; 145 gf->staticfile.data = data; 146 gf->staticfile.len = len; 147 gf->staticfile.off = 0; 148 } 149 150 /* 151 * Deallocate the specified guest file descriptor. This doesn't 152 * close the host fd, it merely undoes the work of alloc_fd(). 153 */ 154 void dealloc_guestfd(int guestfd) 155 { 156 GuestFD *gf = do_get_guestfd(guestfd); 157 158 assert(gf); 159 do_dealloc_guestfd(gf); 160 } 161