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