semihosting: Split out guestfd.c

In arm-compat-semi.c, we have more advanced treatment of
guest file descriptors than we do in other implementations.
Split out GuestFD and related functions to a new file so
that they can be shared.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-04-27 21:38:02 -07:00
parent 3d5e2b4f26
commit 1c6ff7205b
10 changed files with 233 additions and 142 deletions

View File

@ -2,4 +2,5 @@ TARGET_ARCH=aarch64
TARGET_BASE_ARCH=arm TARGET_BASE_ARCH=arm
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml
TARGET_HAS_BFLT=y TARGET_HAS_BFLT=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y

View File

@ -3,4 +3,5 @@ TARGET_BASE_ARCH=arm
TARGET_BIG_ENDIAN=y TARGET_BIG_ENDIAN=y
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml
TARGET_HAS_BFLT=y TARGET_HAS_BFLT=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y

View File

@ -3,4 +3,5 @@ TARGET_SYSTBL_ABI=common,oabi
TARGET_SYSTBL=syscall.tbl TARGET_SYSTBL=syscall.tbl
TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml
TARGET_HAS_BFLT=y TARGET_HAS_BFLT=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y

View File

@ -4,4 +4,5 @@ TARGET_SYSTBL=syscall.tbl
TARGET_BIG_ENDIAN=y TARGET_BIG_ENDIAN=y
TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml
TARGET_HAS_BFLT=y TARGET_HAS_BFLT=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y

View File

@ -2,4 +2,5 @@ TARGET_ARCH=riscv32
TARGET_BASE_ARCH=riscv TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv TARGET_ABI_DIR=riscv
TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y

View File

@ -2,4 +2,5 @@ TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv TARGET_ABI_DIR=riscv
TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y

View File

@ -0,0 +1,83 @@
/*
* Hosted file support for semihosting syscalls.
*
* Copyright (c) 2005, 2007 CodeSourcery.
* Copyright (c) 2019 Linaro
* Copyright © 2020 by Keith Packard <keithp@keithp.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef SEMIHOSTING_GUESTFD_H
#define SEMIHOSTING_GUESTFD_H
typedef enum GuestFDType {
GuestFDUnused = 0,
GuestFDHost = 1,
GuestFDGDB = 2,
GuestFDStatic = 3,
} GuestFDType;
/*
* Guest file descriptors are integer indexes into an array of
* these structures (we will dynamically resize as necessary).
*/
typedef struct GuestFD {
GuestFDType type;
union {
int hostfd;
struct {
const uint8_t *data;
size_t len;
size_t off;
} staticfile;
};
} GuestFD;
/**
* alloc_guestfd:
*
* Allocate an unused GuestFD index. The associated guestfd index
* will still be GuestFDUnused until it is initialized.
*/
int alloc_guestfd(void);
/**
* dealloc_guestfd:
* @guestfd: GuestFD index
*
* Deallocate a GuestFD index. The associated GuestFD structure
* will be recycled for a subsequent allocation.
*/
void dealloc_guestfd(int guestfd);
/**
* get_guestfd:
* @guestfd: GuestFD index
*
* Return the GuestFD structure associated with an initialized @guestfd,
* or NULL if it has not been allocated, or hasn't been initialized.
*/
GuestFD *get_guestfd(int guestfd);
/**
* associate_guestfd:
* @guestfd: GuestFD index
* @hostfd: host file descriptor
*
* Initialize the GuestFD for @guestfd to GuestFDHost using @hostfd.
*/
void associate_guestfd(int guestfd, int hostfd);
/**
* staticfile_guestfd:
* @guestfd: GuestFD index
* @data: data to be read
* @len: length of @data
*
* Initialize the GuestFD for @guestfd to GuestFDStatic.
* The @len bytes at @data will be returned to the guest on reads.
*/
void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len);
#endif /* SEMIHOSTING_GUESTFD_H */

View File

@ -32,12 +32,13 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "semihosting/semihost.h" #include "semihosting/semihost.h"
#include "semihosting/console.h" #include "semihosting/console.h"
#include "semihosting/common-semi.h" #include "semihosting/common-semi.h"
#include "semihosting/guestfd.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "exec/gdbstub.h" #include "exec/gdbstub.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#include "qemu.h" #include "qemu.h"
@ -123,27 +124,6 @@ static int open_modeflags[12] = {
O_RDWR | O_CREAT | O_APPEND | O_BINARY O_RDWR | O_CREAT | O_APPEND | O_BINARY
}; };
typedef enum GuestFDType {
GuestFDUnused = 0,
GuestFDHost = 1,
GuestFDGDB = 2,
GuestFDFeatureFile = 3,
} GuestFDType;
/*
* Guest file descriptors are integer indexes into an array of
* these structures (we will dynamically resize as necessary).
*/
typedef struct GuestFD {
GuestFDType type;
union {
int hostfd;
target_ulong featurefile_offset;
};
} GuestFD;
static GArray *guestfd_array;
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
/** /**
@ -268,98 +248,6 @@ common_semi_sys_exit_extended(CPUState *cs, int nr)
#endif #endif
/*
* Allocate a new guest file descriptor and return it; if we
* couldn't allocate a new fd then return -1.
* This is a fairly simplistic implementation because we don't
* expect that most semihosting guest programs will make very
* heavy use of opening and closing fds.
*/
static int alloc_guestfd(void)
{
guint i;
if (!guestfd_array) {
/* New entries zero-initialized, i.e. type GuestFDUnused */
guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
}
/* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */
for (i = 1; i < guestfd_array->len; i++) {
GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i);
if (gf->type == GuestFDUnused) {
return i;
}
}
/* All elements already in use: expand the array */
g_array_set_size(guestfd_array, i + 1);
return i;
}
/*
* Look up the guestfd in the data structure; return NULL
* for out of bounds, but don't check whether the slot is unused.
* This is used internally by the other guestfd functions.
*/
static GuestFD *do_get_guestfd(int guestfd)
{
if (!guestfd_array) {
return NULL;
}
if (guestfd <= 0 || guestfd >= guestfd_array->len) {
return NULL;
}
return &g_array_index(guestfd_array, GuestFD, guestfd);
}
/*
* Associate the specified guest fd (which must have been
* allocated via alloc_fd() and not previously used) with
* the specified host/gdb fd.
*/
static void associate_guestfd(int guestfd, int hostfd)
{
GuestFD *gf = do_get_guestfd(guestfd);
assert(gf);
gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost;
gf->hostfd = hostfd;
}
/*
* Deallocate the specified guest file descriptor. This doesn't
* close the host fd, it merely undoes the work of alloc_fd().
*/
static void dealloc_guestfd(int guestfd)
{
GuestFD *gf = do_get_guestfd(guestfd);
assert(gf);
gf->type = GuestFDUnused;
}
/*
* Given a guest file descriptor, get the associated struct.
* If the fd is not valid, return NULL. This is the function
* used by the various semihosting calls to validate a handle
* from the guest.
* Note: calling alloc_guestfd() or dealloc_guestfd() will
* invalidate any GuestFD* obtained by calling this function.
*/
static GuestFD *get_guestfd(int guestfd)
{
GuestFD *gf = do_get_guestfd(guestfd);
if (!gf || gf->type == GuestFDUnused) {
return NULL;
}
return gf;
}
/* /*
* The semihosting API has no concept of its errno being thread-safe, * The semihosting API has no concept of its errno being thread-safe,
* as the API design predates SMP CPUs and was intended as a simple * as the API design predates SMP CPUs and was intended as a simple
@ -665,22 +553,13 @@ static const uint8_t featurefile_data[] = {
SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */ SH_EXT_EXIT_EXTENDED | SH_EXT_STDOUT_STDERR, /* Feature byte 0 */
}; };
static void init_featurefile_guestfd(int guestfd) static uint32_t staticfile_closefn(CPUState *cs, GuestFD *gf)
{
GuestFD *gf = do_get_guestfd(guestfd);
assert(gf);
gf->type = GuestFDFeatureFile;
gf->featurefile_offset = 0;
}
static uint32_t featurefile_closefn(CPUState *cs, GuestFD *gf)
{ {
/* Nothing to do */ /* Nothing to do */
return 0; return 0;
} }
static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf, static uint32_t staticfile_writefn(CPUState *cs, GuestFD *gf,
target_ulong buf, uint32_t len) target_ulong buf, uint32_t len)
{ {
/* This fd can never be open for writing */ /* This fd can never be open for writing */
@ -689,7 +568,7 @@ static uint32_t featurefile_writefn(CPUState *cs, GuestFD *gf,
return set_swi_errno(cs, -1); return set_swi_errno(cs, -1);
} }
static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf, static uint32_t staticfile_readfn(CPUState *cs, GuestFD *gf,
target_ulong buf, uint32_t len) target_ulong buf, uint32_t len)
{ {
CPUArchState *env = cs->env_ptr; CPUArchState *env = cs->env_ptr;
@ -703,11 +582,11 @@ static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf,
} }
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (gf->featurefile_offset >= sizeof(featurefile_data)) { if (gf->staticfile.off >= gf->staticfile.len) {
break; break;
} }
s[i] = featurefile_data[gf->featurefile_offset]; s[i] = gf->staticfile.data[gf->staticfile.off];
gf->featurefile_offset++; gf->staticfile.off++;
} }
unlock_user(s, buf, len); unlock_user(s, buf, len);
@ -716,21 +595,21 @@ static uint32_t featurefile_readfn(CPUState *cs, GuestFD *gf,
return len - i; return len - i;
} }
static uint32_t featurefile_isattyfn(CPUState *cs, GuestFD *gf) static uint32_t staticfile_isattyfn(CPUState *cs, GuestFD *gf)
{ {
return 0; return 0;
} }
static uint32_t featurefile_seekfn(CPUState *cs, GuestFD *gf, static uint32_t staticfile_seekfn(CPUState *cs, GuestFD *gf,
target_ulong offset) target_ulong offset)
{ {
gf->featurefile_offset = offset; gf->staticfile.off = offset;
return 0; return 0;
} }
static uint32_t featurefile_flenfn(CPUState *cs, GuestFD *gf) static uint32_t staticfile_flenfn(CPUState *cs, GuestFD *gf)
{ {
return sizeof(featurefile_data); return gf->staticfile.len;
} }
typedef struct GuestFDFunctions { typedef struct GuestFDFunctions {
@ -759,13 +638,13 @@ static const GuestFDFunctions guestfd_fns[] = {
.seekfn = gdb_seekfn, .seekfn = gdb_seekfn,
.flenfn = gdb_flenfn, .flenfn = gdb_flenfn,
}, },
[GuestFDFeatureFile] = { [GuestFDStatic] = {
.closefn = featurefile_closefn, .closefn = staticfile_closefn,
.writefn = featurefile_writefn, .writefn = staticfile_writefn,
.readfn = featurefile_readfn, .readfn = staticfile_readfn,
.isattyfn = featurefile_isattyfn, .isattyfn = staticfile_isattyfn,
.seekfn = featurefile_seekfn, .seekfn = staticfile_seekfn,
.flenfn = featurefile_flenfn, .flenfn = staticfile_flenfn,
}, },
}; };
@ -886,7 +765,8 @@ target_ulong do_common_semihosting(CPUState *cs)
errno = EACCES; errno = EACCES;
return set_swi_errno(cs, -1); return set_swi_errno(cs, -1);
} }
init_featurefile_guestfd(guestfd); staticfile_guestfd(guestfd, featurefile_data,
sizeof(featurefile_data));
return guestfd; return guestfd;
} }

118
semihosting/guestfd.c Normal file
View File

@ -0,0 +1,118 @@
/*
* Hosted file support for semihosting syscalls.
*
* Copyright (c) 2005, 2007 CodeSourcery.
* Copyright (c) 2019 Linaro
* Copyright © 2020 by Keith Packard <keithp@keithp.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "exec/gdbstub.h"
#include "semihosting/guestfd.h"
static GArray *guestfd_array;
/*
* Allocate a new guest file descriptor and return it; if we
* couldn't allocate a new fd then return -1.
* This is a fairly simplistic implementation because we don't
* expect that most semihosting guest programs will make very
* heavy use of opening and closing fds.
*/
int alloc_guestfd(void)
{
guint i;
if (!guestfd_array) {
/* New entries zero-initialized, i.e. type GuestFDUnused */
guestfd_array = g_array_new(FALSE, TRUE, sizeof(GuestFD));
}
/* SYS_OPEN should return nonzero handle on success. Start guestfd from 1 */
for (i = 1; i < guestfd_array->len; i++) {
GuestFD *gf = &g_array_index(guestfd_array, GuestFD, i);
if (gf->type == GuestFDUnused) {
return i;
}
}
/* All elements already in use: expand the array */
g_array_set_size(guestfd_array, i + 1);
return i;
}
/*
* Look up the guestfd in the data structure; return NULL
* for out of bounds, but don't check whether the slot is unused.
* This is used internally by the other guestfd functions.
*/
static GuestFD *do_get_guestfd(int guestfd)
{
if (!guestfd_array) {
return NULL;
}
if (guestfd <= 0 || guestfd >= guestfd_array->len) {
return NULL;
}
return &g_array_index(guestfd_array, GuestFD, guestfd);
}
/*
* Given a guest file descriptor, get the associated struct.
* If the fd is not valid, return NULL. This is the function
* used by the various semihosting calls to validate a handle
* from the guest.
* Note: calling alloc_guestfd() or dealloc_guestfd() will
* invalidate any GuestFD* obtained by calling this function.
*/
GuestFD *get_guestfd(int guestfd)
{
GuestFD *gf = do_get_guestfd(guestfd);
if (!gf || gf->type == GuestFDUnused) {
return NULL;
}
return gf;
}
/*
* Associate the specified guest fd (which must have been
* allocated via alloc_fd() and not previously used) with
* the specified host/gdb fd.
*/
void associate_guestfd(int guestfd, int hostfd)
{
GuestFD *gf = do_get_guestfd(guestfd);
assert(gf);
gf->type = use_gdb_syscalls() ? GuestFDGDB : GuestFDHost;
gf->hostfd = hostfd;
}
void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len)
{
GuestFD *gf = do_get_guestfd(guestfd);
assert(gf);
gf->type = GuestFDStatic;
gf->staticfile.data = data;
gf->staticfile.len = len;
gf->staticfile.off = 0;
}
/*
* Deallocate the specified guest file descriptor. This doesn't
* close the host fd, it merely undoes the work of alloc_fd().
*/
void dealloc_guestfd(int guestfd)
{
GuestFD *gf = do_get_guestfd(guestfd);
assert(gf);
gf->type = GuestFDUnused;
}

View File

@ -1,3 +1,7 @@
specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files(
'guestfd.c',
))
specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files(
'config.c', 'config.c',
'console.c', 'console.c',