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