From patchwork Fri Apr 3 14:04:29 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2092 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225163; bh=nJ0gLPipfkAp7qFifmriMpNkt27g9GhW9VaokzazqUE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=D6w/VZPMMWFg6NA0/HMFbgXNHL7XFSe+h/PhUdNwGc8Ee1fa8mF5pmWw3QyCLcCXG tbIzvkginwGXKDTLYryQABGuXoSnwLqi8CQcUaGKxxk8QSBxuGDGwWqkLnBIP2SqEs mek64VXNukA9IZL/XETvl/7i7nyAmfUOIGTr9viLgdb9626zQ6tTjewdolMENCrSAr W6pzP3VtNxNTNZxh2REEKLIoStffB/AUggfD1gRJrCABbeXETOtyqfsfERjxW5Rlu1 Pc0i/5MJtSjoj4k9pbO0HWDxRt5fXhoquDroins4CQV4XgFRPCuLLeYqm5HIAC09Hv RnSFQQ9+0enlw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id CBE8B6A34F for ; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id tKyAlpAT_86e for ; Fri, 3 Apr 2026 08:06:03 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225159; bh=nJ0gLPipfkAp7qFifmriMpNkt27g9GhW9VaokzazqUE=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=PnwD91YcPSxfZJ+2T342IG0i7zhyIecaqn9mxtKkaqwAoiqP5/b/Nl2jGy2vD3N3x yHlGuDbG8pRbO1afqR3uOPP9xAFqITqzvsFODd9JTdG3NrkPEsMQvrmhg8VG6JkPc8 2E/GafDfC4PxBJf4Xg3mZvBSSVlEIPZpyL0x4qyWg0oeur/ncpnly0A/8DLZToe97K 6jXtYPfbWdZuK35pofQByDZ9i5/Tt+OtHB1keb9n+ppti97r2HmQf5wMXiwLKoo5c4 ZsaALz3smacyWtGOu21I7getXOY+PKd6Rv0zBaB9h4XFrNFQggUrR0dqeSH5UCjunO wNt/5SJdpri2w== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id BFE246A370 for ; Fri, 3 Apr 2026 08:05:59 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225157; bh=B0BLKCvYfHui3CEYa34GrEtcbziJJYld7T6aJOdhUP8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PYsOCYUTn4PApvcFfTvEuA1TxMXfyXRyh0M/JEAfM2VUNWV4iy9nAdOfbd43POzDb zyGXN9+xRdaqKKOPK1AidLtFqxZxfZbtMPr/TAF6ArNLYA2lWpZETNMvgo/BvAfYlV ZP6PkPxxZVgqhHi6CtGsyhOPiz9DL4bxqXnLOuaffl957LA0A8xJoFG1atbnDsDyk+ Wa5xky53SKtey2MPJdO84UvcOVkOxu1FuVIoxCWUib/U71DBfREJlm1GAMmeOw3F9a yWjwYbsLxhkxj5oJfF5iPeFPLc1/30JkmMGeiuhbYQOytU4qmy/+oa24yV8mjJHpiq t54WzEJV8ds1Q== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 976FB6A350; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 3cTBOk0UBevS; Fri, 3 Apr 2026 08:05:57 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225155; bh=LJEiQHHZh4u28I13+2B2FzU54qMjb6W17ZkttXzSu3w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Paai3yiQtK4Kf/sx6FSxPTQIVtuDvQmQVn9HXgI9gGbIEGw4jLB5Sa6GtfYgn9VxS 5cy6unAr44PKmeHt437QifBaMYWPzy/GxFZ7vhO0uVKEPH0gp9r/OoMTlXwh528cv0 JfAHs9Mw2oIqr6LTg5MLylJWzbaAEsABRb1lccHZmCMqZ/K9fiEJvD3iMCJFvWnm5J Z97vaffQE+ULB1YWb5vqRyBYFwDrkH5PN4rngoxDe0YnpOLjkvcBE9oKd3F59xmlkv /hcT4r9tnvNEyfEIogpMBLw/LH8bzyOzijjAJ+mNJ3vIlcb2Qz79Tr+YvA9lQKf4l7 F2UTWlSk3XNVA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 69B7B6A34F; Fri, 3 Apr 2026 08:05:55 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:29 -0600 Message-ID: <20260403140523.1998228-8-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 6BEZODSTB2STQYV363MF3S7V2J3XWJIJ X-Message-ID-Hash: 6BEZODSTB2STQYV363MF3S7V2J3XWJIJ X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 07/34] vfs: Add VFS root and init List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add the VFS root filesystem driver and initialisation. The rootfs is a minimal UCLASS_FS device that provides an empty root directory and creates mount-point directories on demand. The VFS root directory driver (in vfs_dir.c) lists mount-point directories as entries. It is initialised automatically via EVT_LAST_STAGE_INIT. Signed-off-by: Simon Glass --- fs/Kconfig | 1 + fs/Makefile | 2 +- fs/vfs.c | 124 +++++++++++++++++++++++++++++++ fs/vfs_dir.c | 88 ++++++++++++++++++++++ fs/vfs_internal.h | 22 ++++++ include/vfs.h | 18 +++++ test/dm/fs.c | 31 ++++++++ test/py/tests/test_event_dump.py | 1 + 8 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 fs/vfs.c create mode 100644 fs/vfs_dir.c create mode 100644 fs/vfs_internal.h diff --git a/fs/Kconfig b/fs/Kconfig index 6a55bd71adc..b173b5814ed 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -32,6 +32,7 @@ config FILE config VFS bool "Virtual filesystem support" depends on FS && DIR + select EVENT help Provides a virtual filesystem layer with a mount table and unified path namespace. Includes a root filesystem at "/". diff --git a/fs/Makefile b/fs/Makefile index ffb6bbce737..704ac6e4866 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o obj-$(CONFIG_$(PHASE_)FILE) += file-uclass.o -obj-$(CONFIG_$(PHASE_)VFS) += mount-uclass.o +obj-$(CONFIG_$(PHASE_)VFS) += mount-uclass.o vfs.o vfs_dir.o ifdef CONFIG_XPL_BUILD obj-$(CONFIG_SPL_FS_FAT) += fat/ diff --git a/fs/vfs.c b/fs/vfs.c new file mode 100644 index 00000000000..439c9760829 --- /dev/null +++ b/fs/vfs.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtual Filesystem layer + * + * Manages a mount tree using UCLASS_MOUNT devices as children of + * UCLASS_DIR devices, providing unified path resolution across mounted + * filesystems. Inspired by the Linux VFS. + * + * Copyright 2026 Simon Glass + */ + +#define LOG_CATEGORY UCLASS_MOUNT + +#include +#include +#include +#include +#include +#include "vfs_internal.h" +#include +#include +#include +#include + +/* VFS root filesystem - provides an empty root directory */ + +static int vfs_rootfs_mount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + + if (uc_priv->mounted) + return log_msg_ret("rfm", -EISCONN); + + uc_priv->mounted = true; + + return 0; +} + +static int vfs_rootfs_unmount(struct udevice *dev) +{ + return log_msg_ret("rfu", -EBUSY); +} + +static int vfs_rootfs_lookup_dir(struct udevice *dev, const char *path, + struct udevice **dirp) +{ + struct dir_uc_priv *uc_priv; + struct udevice *child, *dir; + int ret; + + /* Check for an existing dir with this path */ + device_foreach_child(child, dev) { + if (device_get_uclass_id(child) == UCLASS_DIR) { + uc_priv = dev_get_uclass_priv(child); + if (uc_priv->path && !strcmp(uc_priv->path, path)) { + *dirp = child; + return 0; + } + } + } + + /* Create a new dir */ + ret = dir_add_probe(dev, DM_DRIVER_GET(vfs_rootfs_dir), path, &dir); + if (ret) + return log_msg_ret("rfD", ret); + + *dirp = dir; + + return 0; +} + +/* Exported functions */ + +struct udevice *vfs_root(void) +{ + struct udevice *dev; + + if (uclass_find_device_by_name(UCLASS_FS, "vfs_rootfs", &dev)) + return NULL; + if (!device_active(dev)) + return NULL; + + return dev; +} + +int vfs_init(void) +{ + struct udevice *dev; + int ret; + + /* Already initialised? */ + dev = vfs_root(); + if (dev) + return 0; + + ret = device_bind_driver(dm_root(), "vfs_rootfs", "vfs_rootfs", &dev); + if (ret) + return log_msg_ret("vib", ret); + + ret = device_probe(dev); + if (ret) + return log_msg_ret("vip", ret); + + ret = fs_mount(dev); + if (ret) + return log_msg_ret("vim", ret); + + return 0; +} + +static const struct fs_ops vfs_rootfs_ops = { + .mount = vfs_rootfs_mount, + .unmount = vfs_rootfs_unmount, + .lookup_dir = vfs_rootfs_lookup_dir, +}; + +U_BOOT_DRIVER(vfs_rootfs) = { + .name = "vfs_rootfs", + .id = UCLASS_FS, + .ops = &vfs_rootfs_ops, + .priv_auto = sizeof(struct vfs_priv), +}; + +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, vfs_init); diff --git a/fs/vfs_dir.c b/fs/vfs_dir.c new file mode 100644 index 00000000000..87a4193516e --- /dev/null +++ b/fs/vfs_dir.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VFS root directory driver + * + * Provides the directory driver for the VFS rootfs. The root directory + * (path "") lists all mount-point directories as entries. Mount-point + * directories themselves have no entries since access crosses into the + * mounted filesystem. + * + * Copyright 2026 Simon Glass + */ + +#include +#include +#include +#include + +static int vfs_rootfs_dir_open(struct udevice *dev, + struct fs_dir_stream *strm) +{ + strm->offset = 0; + + return 0; +} + +/** + * vfs_rootfs_dir_read() - Read the next directory entry + * + * For the root directory (path ""), iterates through sibling DIR devices + * that have non-empty paths, returning each as a directory entry. For + * mount-point directories, returns -ENOENT immediately since their + * contents come from the mounted filesystem. + */ +static int vfs_rootfs_dir_read(struct udevice *dev, + struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct dir_uc_priv *dir_priv = dev_get_uclass_priv(dev); + struct udevice *parent, *child; + int idx = 0; + + /* Only the root dir has entries to list */ + if (*dir_priv->path) + return -ENOENT; + + /* Walk children of the FS device, skipping to the current offset */ + parent = dev_get_parent(dev); + device_foreach_child(child, parent) { + struct dir_uc_priv *uc_priv; + + if (device_get_uclass_id(child) != UCLASS_DIR) + continue; + + uc_priv = dev_get_uclass_priv(child); + if (!uc_priv->path || !*uc_priv->path) + continue; + + if (idx++ < strm->offset) + continue; + + strlcpy(dent->name, uc_priv->path, sizeof(dent->name)); + dent->type = FS_DT_DIR; + dent->size = 0; + strm->offset++; + + return 0; + } + + return -ENOENT; +} + +static int vfs_rootfs_dir_close(struct udevice *dev, + struct fs_dir_stream *strm) +{ + return 0; +} + +static struct dir_ops vfs_rootfs_dir_ops = { + .open = vfs_rootfs_dir_open, + .read = vfs_rootfs_dir_read, + .close = vfs_rootfs_dir_close, +}; + +U_BOOT_DRIVER(vfs_rootfs_dir) = { + .name = "vfs_rootfs_dir", + .id = UCLASS_DIR, + .ops = &vfs_rootfs_dir_ops, +}; diff --git a/fs/vfs_internal.h b/fs/vfs_internal.h new file mode 100644 index 00000000000..b7e6b37d55d --- /dev/null +++ b/fs/vfs_internal.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Internal VFS helpers - not part of the public API + * + * Copyright 2026 Simon Glass + */ + +#ifndef __VFS_INTERNAL_H +#define __VFS_INTERNAL_H + +struct udevice; + +/** + * struct vfs_priv - VFS root FS device driver-private data + * + * @mount_count: Counter for unique mount device names + */ +struct vfs_priv { + int mount_count; +}; + +#endif diff --git a/include/vfs.h b/include/vfs.h index 7c31caace25..1bf1762ab52 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -28,4 +28,22 @@ struct vfsmount { struct udevice *target; }; +/** + * vfs_init() - Initialise the VFS + * + * Creates the VFS root directory device. Normally called automatically + * via EVT_LAST_STAGE_INIT during boot. May also be called directly in + * tests after a DM tree reset. + * + * Return: 0 if OK, -ve on error + */ +int vfs_init(void); + +/** + * vfs_root() - Get the VFS root FS device + * + * Return: VFS root FS device, or NULL if not initialised + */ +struct udevice *vfs_root(void); + #endif diff --git a/test/dm/fs.c b/test/dm/fs.c index 6cb5f74db35..1d395031dee 100644 --- a/test/dm/fs.c +++ b/test/dm/fs.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -104,3 +105,33 @@ static int dm_test_fs_file(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_fs_file, UTF_SCAN_FDT); + +#if IS_ENABLED(CONFIG_VFS) +/* Test VFS init and root directory operations */ +static int dm_test_vfs_init(struct unit_test_state *uts) +{ + struct udevice *vfs, *dir; + struct fs_dir_stream *strm; + struct fs_dirent dent; + + ut_assertok(vfs_init()); + + vfs = vfs_root(); + ut_assertnonnull(vfs); + + /* Look up the root directory */ + ut_assertok(fs_lookup_dir(vfs, "", &dir)); + ut_assertnonnull(dir); + + /* open should succeed, read should return -ENOENT (root is empty) */ + ut_assertok(dir_open(dir, &strm)); + ut_asserteq(-ENOENT, dir_read(dir, strm, &dent)); + ut_assertok(dir_close(dir, strm)); + + /* rootfs cannot be unmounted */ + ut_asserteq(-EBUSY, fs_unmount(vfs)); + + return 0; +} +DM_TEST(dm_test_vfs_init, UTF_SCAN_FDT); +#endif diff --git a/test/py/tests/test_event_dump.py b/test/py/tests/test_event_dump.py index ff0da82196b..dad0132eeed 100644 --- a/test/py/tests/test_event_dump.py +++ b/test/py/tests/test_event_dump.py @@ -21,6 +21,7 @@ EVT_LAST_STAGE_INIT alloc_write_acpi_tables .*lib/acpi/acpi_table.c:.* EVT_LAST_STAGE_INIT efi_block_device_create .*lib/efi_driver/efi_block_device.c:.* EVT_LAST_STAGE_INIT install_smbios_table .*lib/efi_loader/efi_smbios.c:.* EVT_LAST_STAGE_INIT last_stage_init .*arch/sandbox/cpu/start.c:.* +EVT_LAST_STAGE_INIT vfs_init .*fs/vfs.c:.* EVT_MISC_INIT_F sandbox_early_getopt_check .*arch/sandbox/cpu/start.c:.* EVT_SETTINGS_R sandbox_settings .*board/sandbox/sandbox.c:.* EVT_TEST h_adder_simple .*test/common/event.c:'''