xref: /openbmc/qemu/semihosting/guestfd.c (revision 83fb360d)
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