11c6ff720SRichard Henderson /*
21c6ff720SRichard Henderson * Hosted file support for semihosting syscalls.
31c6ff720SRichard Henderson *
41c6ff720SRichard Henderson * Copyright (c) 2005, 2007 CodeSourcery.
51c6ff720SRichard Henderson * Copyright (c) 2019 Linaro
61c6ff720SRichard Henderson * Copyright © 2020 by Keith Packard <keithp@keithp.com>
71c6ff720SRichard Henderson *
81c6ff720SRichard Henderson * SPDX-License-Identifier: GPL-2.0-or-later
91c6ff720SRichard Henderson */
101c6ff720SRichard Henderson
111c6ff720SRichard Henderson #include "qemu/osdep.h"
12c566080cSAlex Bennée #include "gdbstub/syscalls.h"
13e4a4aaa5SRichard Henderson #include "semihosting/semihost.h"
141c6ff720SRichard Henderson #include "semihosting/guestfd.h"
15*83fb360dSPhilippe Mathieu-Daudé #ifndef CONFIG_USER_ONLY
16e4a4aaa5SRichard Henderson #include CONFIG_DEVICES
175b3f39cbSRichard Henderson #endif
181c6ff720SRichard Henderson
191c6ff720SRichard Henderson static GArray *guestfd_array;
201c6ff720SRichard Henderson
21e4a4aaa5SRichard Henderson #ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING
22e4a4aaa5SRichard Henderson GuestFD console_in_gf;
23e4a4aaa5SRichard Henderson GuestFD console_out_gf;
24e4a4aaa5SRichard Henderson #endif
25e4a4aaa5SRichard Henderson
qemu_semihosting_guestfd_init(void)26e4a4aaa5SRichard Henderson void qemu_semihosting_guestfd_init(void)
27e4a4aaa5SRichard Henderson {
28e4a4aaa5SRichard Henderson /* New entries zero-initialized, i.e. type GuestFDUnused */
29e4a4aaa5SRichard Henderson guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
30e4a4aaa5SRichard Henderson
31e4a4aaa5SRichard Henderson #ifdef CONFIG_ARM_COMPATIBLE_SEMIHOSTING
32e4a4aaa5SRichard Henderson /* For ARM-compat, the console is in a separate namespace. */
33e4a4aaa5SRichard Henderson if (use_gdb_syscalls()) {
34e4a4aaa5SRichard Henderson console_in_gf.type = GuestFDGDB;
35e4a4aaa5SRichard Henderson console_in_gf.hostfd = 0;
36e4a4aaa5SRichard Henderson console_out_gf.type = GuestFDGDB;
37e4a4aaa5SRichard Henderson console_out_gf.hostfd = 2;
38e4a4aaa5SRichard Henderson } else {
39e4a4aaa5SRichard Henderson console_in_gf.type = GuestFDConsole;
40e4a4aaa5SRichard Henderson console_out_gf.type = GuestFDConsole;
41e4a4aaa5SRichard Henderson }
42e4a4aaa5SRichard Henderson #else
43e4a4aaa5SRichard Henderson /* Otherwise, the stdio file descriptors apply. */
44e4a4aaa5SRichard Henderson guestfd_array = g_array_set_size(guestfd_array, 3);
45e4a4aaa5SRichard Henderson #ifndef CONFIG_USER_ONLY
46e4a4aaa5SRichard Henderson if (!use_gdb_syscalls()) {
47e4a4aaa5SRichard Henderson GuestFD *gf = &g_array_index(guestfd_array, GuestFD, 0);
48e4a4aaa5SRichard Henderson gf[0].type = GuestFDConsole;
49e4a4aaa5SRichard Henderson gf[1].type = GuestFDConsole;
50e4a4aaa5SRichard Henderson gf[2].type = GuestFDConsole;
51e4a4aaa5SRichard Henderson return;
52e4a4aaa5SRichard Henderson }
53e4a4aaa5SRichard Henderson #endif
54e4a4aaa5SRichard Henderson associate_guestfd(0, 0);
55e4a4aaa5SRichard Henderson associate_guestfd(1, 1);
56e4a4aaa5SRichard Henderson associate_guestfd(2, 2);
57e4a4aaa5SRichard Henderson #endif
58e4a4aaa5SRichard Henderson }
59e4a4aaa5SRichard Henderson
601c6ff720SRichard Henderson /*
611c6ff720SRichard Henderson * Allocate a new guest file descriptor and return it; if we
621c6ff720SRichard Henderson * couldn't allocate a new fd then return -1.
631c6ff720SRichard Henderson * This is a fairly simplistic implementation because we don't
641c6ff720SRichard Henderson * expect that most semihosting guest programs will make very
651c6ff720SRichard Henderson * heavy use of opening and closing fds.
661c6ff720SRichard Henderson */
alloc_guestfd(void)671c6ff720SRichard Henderson int alloc_guestfd(void)
681c6ff720SRichard Henderson {
691c6ff720SRichard Henderson guint i;
701c6ff720SRichard Henderson
711c6ff720SRichard Henderson /* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */
721c6ff720SRichard Henderson for (i = 1; i < guestfd_array->len; i++) {
731c6ff720SRichard Henderson GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i);
741c6ff720SRichard Henderson
751c6ff720SRichard Henderson if (gf->type == GuestFDUnused) {
761c6ff720SRichard Henderson return i;
771c6ff720SRichard Henderson }
781c6ff720SRichard Henderson }
791c6ff720SRichard Henderson
801c6ff720SRichard Henderson /* All elements already in use: expand the array */
811c6ff720SRichard Henderson g_array_set_size(guestfd_array, i + 1);
821c6ff720SRichard Henderson return i;
831c6ff720SRichard Henderson }
841c6ff720SRichard Henderson
do_dealloc_guestfd(GuestFD * gf)855eadbbfcSRichard Henderson static void do_dealloc_guestfd(GuestFD *gf)
865eadbbfcSRichard Henderson {
875eadbbfcSRichard Henderson gf->type = GuestFDUnused;
885eadbbfcSRichard Henderson }
895eadbbfcSRichard Henderson
901c6ff720SRichard Henderson /*
911c6ff720SRichard Henderson * Look up the guestfd in the data structure; return NULL
921c6ff720SRichard Henderson * for out of bounds, but don't check whether the slot is unused.
931c6ff720SRichard Henderson * This is used internally by the other guestfd functions.
941c6ff720SRichard Henderson */
do_get_guestfd(int guestfd)951c6ff720SRichard Henderson static GuestFD *do_get_guestfd(int guestfd)
961c6ff720SRichard Henderson {
97e4a4aaa5SRichard Henderson if (guestfd < 0 || guestfd >= guestfd_array->len) {
981c6ff720SRichard Henderson return NULL;
991c6ff720SRichard Henderson }
1001c6ff720SRichard Henderson
1011c6ff720SRichard Henderson return &g_array_index(guestfd_array, GuestFD, guestfd);
1021c6ff720SRichard Henderson }
1031c6ff720SRichard Henderson
1041c6ff720SRichard Henderson /*
1051c6ff720SRichard Henderson * Given a guest file descriptor, get the associated struct.
1061c6ff720SRichard Henderson * If the fd is not valid, return NULL. This is the function
1071c6ff720SRichard Henderson * used by the various semihosting calls to validate a handle
1081c6ff720SRichard Henderson * from the guest.
1091c6ff720SRichard Henderson * Note: calling alloc_guestfd() or dealloc_guestfd() will
1101c6ff720SRichard Henderson * invalidate any GuestFD* obtained by calling this function.
1111c6ff720SRichard Henderson */
get_guestfd(int guestfd)1121c6ff720SRichard Henderson GuestFD *get_guestfd(int guestfd)
1131c6ff720SRichard Henderson {
1141c6ff720SRichard Henderson GuestFD *gf = do_get_guestfd(guestfd);
1151c6ff720SRichard Henderson
1161c6ff720SRichard Henderson if (!gf || gf->type == GuestFDUnused) {
1171c6ff720SRichard Henderson return NULL;
1181c6ff720SRichard Henderson }
1191c6ff720SRichard Henderson return gf;
1201c6ff720SRichard Henderson }
1211c6ff720SRichard Henderson
1221c6ff720SRichard Henderson /*
1231c6ff720SRichard Henderson * Associate the specified guest fd (which must have been
1241c6ff720SRichard Henderson * allocated via alloc_fd() and not previously used) with
1251c6ff720SRichard Henderson * the specified host/gdb fd.
1261c6ff720SRichard Henderson */
associate_guestfd(int guestfd,int hostfd)1271c6ff720SRichard Henderson void associate_guestfd(int guestfd, int hostfd)
1281c6ff720SRichard Henderson {
1291c6ff720SRichard Henderson GuestFD *gf = do_get_guestfd(guestfd);
1301c6ff720SRichard Henderson
1311c6ff720SRichard Henderson assert(gf);
1321c6ff720SRichard Henderson gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost;
1331c6ff720SRichard Henderson gf->hostfd = hostfd;
1341c6ff720SRichard Henderson }
1351c6ff720SRichard Henderson
staticfile_guestfd(int guestfd,const uint8_t * data,size_t len)1361c6ff720SRichard Henderson void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len)
1371c6ff720SRichard Henderson {
1381c6ff720SRichard Henderson GuestFD *gf = do_get_guestfd(guestfd);
1391c6ff720SRichard Henderson
1401c6ff720SRichard Henderson assert(gf);
1411c6ff720SRichard Henderson gf->type = GuestFDStatic;
1421c6ff720SRichard Henderson gf->staticfile.data = data;
1431c6ff720SRichard Henderson gf->staticfile.len = len;
1441c6ff720SRichard Henderson gf->staticfile.off = 0;
1451c6ff720SRichard Henderson }
1461c6ff720SRichard Henderson
1471c6ff720SRichard Henderson /*
1481c6ff720SRichard Henderson * Deallocate the specified guest file descriptor. This doesn't
1491c6ff720SRichard Henderson * close the host fd, it merely undoes the work of alloc_fd().
1501c6ff720SRichard Henderson */
dealloc_guestfd(int guestfd)1511c6ff720SRichard Henderson void dealloc_guestfd(int guestfd)
1521c6ff720SRichard Henderson {
1531c6ff720SRichard Henderson GuestFD *gf = do_get_guestfd(guestfd);
1541c6ff720SRichard Henderson
1551c6ff720SRichard Henderson assert(gf);
1565eadbbfcSRichard Henderson do_dealloc_guestfd(gf);
1571c6ff720SRichard Henderson }
158