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