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