virtio: Plumb virtio-fs into the legacy filesystem code

Add compatibility function to support a single virtio-fs via the normal
'ls' and 'load' commands. Other commands are not supported for now.

Add a workaround for the fact that virtiofs does not use a block device.
The existing filesystem layer does not support non-block filesystems -
sandbox's hostfs mostly bypasses this code.

Make sure that sandbox does not try to mount a virtio-fs as this does
not work at present. It will require either a fake driver for virtio-fs
which connects to host files, or possibly something involving FUSE.

Series-to: concept
Cover-letter:
virtio: Support virtio-fs
This series introduces support for virtio-fs, a filesystem which
provides access to host files from within a QEMU guest OS.

A new filesystem driver is created with support for the three uclasses
(FS, DIR, FILE). A compatibility layer is added as well, so that the
existing cmdline work as expected.

Only listing directories and reading files are supported so far.

Since sandbox works by using a NULL blk_desc, a workaround is added for
now. Once we switch commands (and bootstd!) over to the new filesystem
approach, this will go away.

It is possible to test this using something like:

   ./scripts/build-qemu -a x86  -rs -D .

then within U-Boot:

   ls virtio 0
END

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass
2025-06-26 07:10:21 -06:00
parent ce5ca9b204
commit 5ab90a9307
7 changed files with 252 additions and 3 deletions

View File

@@ -482,7 +482,13 @@ int blk_get_device_part_str(const char *ifname, const char *dev_part_str,
return 0;
}
#endif
if (IS_ENABLED(CONFIG_VIRTIO_FS) && !strcmp(ifname, "virtiofs")) {
strcpy((char *)info->type, BOOT_PART_TYPE);
strcpy((char *)info->name, "Virtio filesystem");
info->fs_type = FS_TYPE_VIRTIO;
return 0;
}
#if IS_ENABLED(CONFIG_CMD_UBIFS) && !IS_ENABLED(CONFIG_XPL_BUILD)
/*
* Special-case ubi, ubi goes through a mtd, rather than through

View File

@@ -12,4 +12,4 @@ obj-$(CONFIG_VIRTIO_SANDBOX_EMUL) += sandbox_emul.o emul_blk.o
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o
obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o fs_compat.o

163
drivers/virtio/fs_compat.c Normal file
View File

@@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0
/*
* U-Boot Virtio-FS compatibility layer, to allow use with the legacy
* filesystem-layer
*
* Copyright 2025 Simon Glass <sjg@chromium.org>
*
*/
#define LOG_CATEGORY UCLASS_VIRTIO
#include <dir.h>
#include <dm.h>
#include <file.h>
#include <fs.h>
#include <malloc.h>
#include <virtio.h>
#include <virtio_fs.h>
#include <linux/fuse.h>
#include "fs_internal.h"
static struct udevice *fs_dev;
int virtio_fs_compat_opendir(const char *fname, struct fs_dir_stream **strmp)
{
struct udevice *dev;
int ret;
log_debug("starting fs_dev %p\n", fs_dev);
log_debug("lookup dev '%s' fname '%s'\n", fs_dev->name, fname);
ret = fs_lookup_dir(fs_dev, fname, &dev);
if (ret)
return log_msg_ret("vld", ret);
log_debug("open\n");
ret = dir_open(dev, strmp);
if (ret)
return log_msg_ret("vdo", ret);
return 0;
}
int virtio_fs_compat_readdir(struct fs_dir_stream *strm,
struct fs_dirent **dentp)
{
struct fs_dirent *dent;
int ret;
log_debug("read dev '%s'\n", fs_dev->name);
dent = malloc(sizeof(struct fs_dirent));
if (!dent)
return log_msg_ret("vrD", -ENOMEM);
ret = dir_read(strm->dev, strm, dent);
if (ret) {
free(dent);
return log_msg_ret("vrd", ret);
}
*dentp = dent;
log_debug("read done\n");
return 0;
}
void virtio_fs_compat_closedir(struct fs_dir_stream *strm)
{
int ret;
log_debug("close dev '%s'\n", fs_dev->name);
ret = dir_close(strm->dev, strm);
if (ret)
log_err("dir_close() failed: %dE\n", ret);
}
int virtio_fs_compat_probe(struct blk_desc *fs_dev_desc,
struct disk_partition *fs_partition)
{
struct udevice *dev;
int ret;
/*
* at present, sandbox cannot support virtiofs fully, so add a
* work-around here
*/
if (IS_ENABLED(CONFIG_SANDBOX))
return log_msg_ret("vfc", -ENOENT);
ret = uclass_first_device_err(UCLASS_FS, &dev);
if (ret) {
printf("No filesystem (err %dE)\n", ret);
return ret;
}
ret = fs_mount(dev);
if (ret && ret != -EISCONN) {
printf("Cannot mount filesystem (err %dE)\n", ret);
return ret;
}
fs_dev = dev;
log_debug("fs_dev %p\n", fs_dev);
return 0;
}
int virtio_fs_compat_size(const char *fname, loff_t *sizep)
{
struct fuse_entry_out out;
const char *leaf;
char *subdir;
int ret;
log_debug("filename '%s'\n", fname);
ret = fs_split_path(fname, &subdir, &leaf);
if (ret)
return log_msg_ret("vcp", ret);
log_debug("subdir '%s' leaf '%s'\n", subdir, leaf);
ret = virtio_fs_lookup_(fs_dev, virtio_fs_get_root(fs_dev), fname,
&out);
if (ret)
return log_msg_ret("vcl", ret);
log_debug("inode %llx size %llx\n", out.nodeid, out.attr.size);
*sizep = out.attr.size;
return 0;
}
int virtio_fs_compat_read(const char *fname, void *buf, loff_t offset,
loff_t len, loff_t *actread)
{
struct udevice *dir, *fil;
const char *leaf;
char *subdir;
long nread;
int ret;
log_debug("load '%s'\n", fname);
ret = fs_split_path(fname, &subdir, &leaf);
if (ret)
return log_msg_ret("fcr", ret);
log_debug("subdir '%s' leaf '%s'\n", subdir, leaf);
ret = fs_lookup_dir(fs_dev, subdir, &dir);
if (ret)
return log_msg_ret("fcl", ret);
log_debug("dir '%s'\n", dir->name);
ret = virtio_fs_setup_file(dir, leaf, DIR_O_RDONLY, &fil);
log_debug("virtio_fs_file_open returned %d\n", ret);
if (ret)
return log_msg_ret("fco", ret);
log_debug("reading file '%s'\n", fil->name);
nread = file_read_at(fil, buf, offset, len);
if (nread < 0)
return log_msg_ret("fco", nread);
*actread = nread;
return 0;
}

View File

@@ -13,6 +13,31 @@
#include <fs.h>
#include <dm/device-internal.h>
int fs_split_path(const char *fname, char **subdirp, const char **leafp)
{
char *subdir, *p;
if (!*fname)
return log_msg_ret("fsp", -EINVAL);
/* allocate space for the whole filename, for simplicity */
subdir = strdup(fname);
if (!subdir)
return log_msg_ret("fsp", -ENOMEM);
p = strrchr(subdir, '/');
if (p) {
*leafp = p + 1;
*p = '\0';
} else {
*leafp = fname;
strcpy(subdir, "/");
}
*subdirp = subdir;
return 0;
}
int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp)
{
struct fs_ops *ops = fs_get_ops(dev);

View File

@@ -35,6 +35,7 @@
#include <squashfs.h>
#include <erofs.h>
#include <exfat.h>
#include <virtio_fs.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -405,6 +406,28 @@ static struct fstype_info fstypes[] = {
.mkdir = exfat_fs_mkdir,
.rename = exfat_fs_rename,
},
#endif
#if CONFIG_IS_ENABLED(VIRTIO_FS)
{
.fstype = FS_TYPE_VIRTIO,
.name = "virtio",
.null_dev_desc_ok = true,
.probe = virtio_fs_compat_probe,
.opendir = virtio_fs_compat_opendir,
.readdir = virtio_fs_compat_readdir,
.ls = fs_ls_generic,
.read = virtio_fs_compat_read,
.size = virtio_fs_compat_size,
.close = fs_close_unsupported,
.closedir = virtio_fs_compat_closedir,
.exists = fs_exists_unsupported,
.uuid = fs_uuid_unsupported,
.write = fs_write_unsupported,
.ln = fs_ln_unsupported,
.unlink = fs_unlink_unsupported,
.mkdir = fs_mkdir_unsupported,
.rename = fs_rename_unsupported,
},
#endif
{
.fstype = FS_TYPE_ANY,
@@ -693,7 +716,15 @@ struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
struct fs_dirent *dirent;
int ret;
fs_set_blk_dev_with_part(dirs->desc, dirs->part);
/*
* If this is not a block device we can assume it is virtiofs, since we
* cannot reach this code otherwise. Sandbox uses its own 'ls' method.
*/
if (dirs->desc)
fs_set_blk_dev_with_part(dirs->desc, dirs->part);
else
fs_type = FS_TYPE_VIRTIO;
info = fs_get_info(fs_type);
ret = info->readdir(dirs, &dirent);
@@ -713,7 +744,15 @@ void fs_closedir(struct fs_dir_stream *dirs)
if (!dirs)
return;
fs_set_blk_dev_with_part(dirs->desc, dirs->part);
/*
* If this is not a block device we can assume it is virtiofs, since we
* cannot reach this code otherwise. Sandbox uses its own 'ls' method.
*/
if (dirs->desc)
fs_set_blk_dev_with_part(dirs->desc, dirs->part);
else
fs_type = FS_TYPE_VIRTIO;
info = fs_get_info(fs_type);
info->closedir(dirs);

View File

@@ -96,4 +96,16 @@ int fs_unmount(struct udevice *dev);
*/
int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp);
/**
* fs_split_path() - Get a list of subdirs in a filename
*
* For example, '/path/to/fred' returns an alist containing allocated strings
* 'path' and 'to', with \*leafp pointing to the 'f'
*
* @fname: Filename to parse
* @subdirp: Returns an allocating string containing the subdirs, or "/" if none
* @leafp: Returns a pointer to the leaf filename, within @fname
*/
int fs_split_path(const char *fname, char **subdirp, const char **leafp);
#endif

View File

@@ -7,6 +7,9 @@
#include <rtc.h>
/**
* @FS_TYPE_VIRTIO: virtio-fs for access to the host filesystem from QEMU
*/
enum fs_type_t {
FS_TYPE_ANY = 0,
FS_TYPE_FAT,
@@ -18,6 +21,7 @@ enum fs_type_t {
FS_TYPE_EROFS,
FS_TYPE_SEMIHOSTING,
FS_TYPE_EXFAT,
FS_TYPE_VIRTIO,
};
/*