fs: Add support for a filesystem

The existing filesystem support is fairly basic. It supports access to
only one filesystem at a time, does not allow caching, does not have an
equivalent of Linux's VFS and uses global variables to keep track of
which one is in use.

As a starting point to improving this, provide a filesystem uclass. For
now it only includes operations to mount and unmount.

Provide a bootdev so that it is possible to locate bootflows on a
filesystem.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass
2025-06-25 07:54:20 -06:00
parent c963c7c5d9
commit 48655e4aa9
8 changed files with 215 additions and 4 deletions

View File

@@ -1182,6 +1182,12 @@ F: net/fastboot_tcp.c
F: net/fastboot_udp.c
F: test/dm/fastboot.c
FILESYSTEM LAYER
M: Simon Glass <sjg@chromium.org>
S: Maintained
F: fs/fs-uclass.c
F: include/fs.h
FPGA
M: Michal Simek <michal.simek@amd.com>
S: Maintained

View File

@@ -4,6 +4,14 @@
menu "File systems"
config FS
bool "Support for filesystems"
default y if SANDBOX || VENDOR_EMULATION || ARCH_QEMU
depends on EXPERT
help
Provides an interface for filesystems, allowing them to be
persistently mounted. Filesystem can contain files and directory.
config FS_LEGACY
def_bool y
help

View File

@@ -5,6 +5,7 @@
# Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o
obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o
ifdef CONFIG_XPL_BUILD
obj-$(CONFIG_SPL_FS_FAT) += fat/

113
fs/fs-uclass.c Normal file
View File

@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Implementation of a filesystem, e.g. on a partition
*
* Copyright 2025 Simon Glass <sjg@chromium.org>
*/
#define LOG_DEBUG
#define LOG_CATEGORY UCLASS_FS
#include <bootdev.h>
#include <bootmeth.h>
#include <dm.h>
#include <fs.h>
#include <dm/device-internal.h>
int fs_mount(struct udevice *dev)
{
struct fs_ops *ops = fs_get_ops(dev);
int ret;
ret = ops->mount(dev);
if (ret)
return log_msg_ret("fsm", ret);
ret = bootdev_setup_for_dev(dev, "fs_bootdev");
if (ret)
return log_msg_ret("fss", ret);
return 0;
}
int fs_unmount(struct udevice *dev)
{
struct fs_ops *ops = fs_get_ops(dev);
return ops->unmount(dev);
}
static int fs_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
struct bootflow *bflow)
{
struct udevice *fsdev = dev_get_parent(dev);
int ret;
log_debug("get_bootflow fs '%s'\n", fsdev->name);
ret = bootmeth_check(bflow->method, iter);
if (ret)
return log_msg_ret("check", ret);
return 0;
}
static int fs_bootdev_bind(struct udevice *dev)
{
struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
/*
* we don't know what priority to give this, so pick something a little
* slow for now
*/
ucp->prio = BOOTDEVP_3_INTERNAL_SLOW;
return 0;
}
static int fs_bootdev_hunt(struct bootdev_hunter *info, bool show)
{
struct udevice *dev;
int ret;
/* mount all filesystems, which will create bootdevs for each */
uclass_foreach_dev_probe(UCLASS_FS, dev) {
ret = fs_mount(dev);
if (ret)
log_warning("Failed to mount filesystem '%s'\n",
dev->name);
}
return 0;
}
struct bootdev_ops fs_bootdev_ops = {
.get_bootflow = fs_get_bootflow,
};
static const struct udevice_id fs_bootdev_ids[] = {
{ .compatible = "u-boot,bootdev-fs" },
{ }
};
U_BOOT_DRIVER(fs_bootdev) = {
.name = "fs_bootdev",
.id = UCLASS_BOOTDEV,
.ops = &fs_bootdev_ops,
.bind = fs_bootdev_bind,
.of_match = fs_bootdev_ids,
};
BOOTDEV_HUNTER(fs_bootdev_hunter) = {
.prio = BOOTDEVP_3_INTERNAL_SLOW,
.uclass = UCLASS_FS,
.hunt = fs_bootdev_hunt,
.drv = DM_DRIVER_REF(fs_bootdev),
};
UCLASS_DRIVER(fs) = {
.name = "fs",
.id = UCLASS_FS,
.per_device_auto = sizeof(struct fs_priv),
.per_device_plat_auto = sizeof(struct fs_plat),
};

View File

@@ -68,6 +68,7 @@ enum uclass_id {
UCLASS_FIRMWARE, /* Firmware */
UCLASS_FPGA, /* FPGA device */
UCLASS_FUZZING_ENGINE, /* Fuzzing engine */
UCLASS_FS, /* Filesystem */
UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
UCLASS_FWU_MDATA, /* FWU Metadata Access */
UCLASS_GPIO, /* Bank of general-purpose I/O pins */

78
include/fs.h Normal file
View File

@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* U-Boot Filesystem layer
*
* Models a filesystem which can be mounted and unmounted. It also allows a
* directory to be looked up.
*
* Copyright 2025 Simon Glass <sjg@chromium.org>
*/
#ifndef __FS_H
#define __FS_H
#include <fs_common.h>
struct udevice;
enum {
/* Maximum length of the filesystem name */
FS_MAX_NAME_LEN = 128,
};
/**
* struct fs_plat - Filesystem information
*
* @name: Name of the filesystem, or empty if not available
*/
struct fs_plat {
char name[FS_MAX_NAME_LEN];
};
/**
* struct fs_priv - Private information for the FS devices
*
* @mounted: true if mounted
*/
struct fs_priv {
bool mounted;
};
struct fs_ops {
/**
* mount() - Mount the filesystem
*
* @dev: Filesystem device
* Return 0 if OK, -EISCONN if already mounted, other -ve on error
*/
int (*mount)(struct udevice *dev);
/**
* unmount() - Unmount the filesystem
*
* @dev: Filesystem device
* Return 0 if OK, -ENOTCONN if not mounted, other -ve on error
*/
int (*unmount)(struct udevice *dev);
};
/* Get access to a filesystem's operations */
#define fs_get_ops(dev) ((struct fs_ops *)(dev)->driver->ops)
/**
* fs_mount() - Mount the filesystem
*
* @dev: Filesystem device
* Return 0 if OK, -EISCONN if already mounted, other -ve on error
*/
int fs_mount(struct udevice *dev);
/**
* fs_unmount() - Unmount the filesystem
*
* @dev: Filesystem device
* Return 0 if OK, -ENOTCONN if not mounted, other -ve on error
*/
int fs_unmount(struct udevice *dev);
#endif

View File

@@ -390,6 +390,7 @@ static int bootdev_test_hunter(struct unit_test_state *uts)
ut_assert_nextlinen("----");
ut_assert_nextline(" 6 ethernet eth_bootdev");
ut_assert_nextline(" 1 simple_bus (none)");
ut_assert_nextline(" 3 fs fs_bootdev");
ut_assert_nextline(" 5 ide ide_bootdev");
ut_assert_nextline(" 2 mmc mmc_bootdev");
ut_assert_nextline(" 4 nvme nvme_bootdev");
@@ -447,6 +448,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
/* This is the extension feature which has no uclass at present */
ut_assert_nextline("Hunting with: simple_bus");
ut_assert_nextline("Found 2 extension board(s).");
ut_assert_nextline("Hunting with: fs");
ut_assert_nextline("Hunting with: ide");
/* mmc hunter has already been used so should not run again */
@@ -468,6 +470,7 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assert_nextlinen("----");
ut_assert_nextline(" 6 * ethernet eth_bootdev");
ut_assert_nextline(" 1 * simple_bus (none)");
ut_assert_nextline(" 3 * fs fs_bootdev");
ut_assert_nextline(" 5 * ide ide_bootdev");
ut_assert_nextline(" 2 * mmc mmc_bootdev");
ut_assert_nextline(" 4 * nvme nvme_bootdev");

View File

@@ -24,10 +24,11 @@
enum {
HUNTER_ETH = 0,
HUNTER_SIMPLE_BUS,
HUNTER_MMC = 3,
HUNTER_SCSI = 6,
HUNTER_USB = 8,
HUNTER_COUNT = 10,
HUNTER_FS = 3,
HUNTER_MMC, /* ID of MMC hunter */
HUNTER_SCSI = 7, /* ID of SCSI hunter */
HUNTER_USB = 9, /* ID of USB hunter */
HUNTER_COUNT = 11,
HUNTER_MAX = HUNTER_COUNT - 1,
};