* [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader
@ 2026-03-03 13:29 Christian Marangi
2026-03-03 13:29 ` [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error Christian Marangi
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:29 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Casey Connolly, Christian Marangi,
Peng Fan, Quentin Schulz, Harsha Vardhan V M, Neha Malcom Francis,
Chen-Yu Tsai, Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang,
Weijie Gao, Marek Vasut, Lucien.Jheng, Sky Huang,
Alif Zakuan Yuslaimi, u-boot
This series mainly rework the FS loader to permit reading firmware from
container that are not exactly a readable filesystem.
One scenario is when a firmware is shipped in a FIP container as
a generic blob with an UUID.
FIP are mostly used on ARM in the context of ATF.
In such context U-Boot is loaded as BL31 and the PHY firmware
can't be stored in the FIT image for the kernel as U-Boot should
not depend on the presence of the kernel to correctly enable PHY
for Ethernet port.
To handle such case the PHY firmware is stored in FIP with a
predictable UUID.
One example is with Airoha 8811H firmware where the FIP can
have a blob with UUID "d39d2cf2-9bd0-3ca7-93e9-e71b4f9250b2".
(generated from command "uuidgen -n @dns --md5 --name en8811h.bin")
With these example DTS entry:
fs_loader0: fip-loader {
bootph-all;
compatible = "u-boot,fip-loader";
phandlepart = <&mmc0 0>;
partoffset = <0x100>;
};
mdio {
en8811: ethernet-phy@f {
reg = <0xf>;
firmware-name = "d39d2cf2-9bd0-3ca7-93e9-e71b4f9250b2";
firmware-loader = <&fs_loader0>;
};
};
And PHY driver using the get_fw_loader_from_node() and the
common request_firmware_into_buf() it's possible to
load the PHY firmware transparently by just declaring
the required entry in the DTS.
get_fw_loader_from_node() is implemented to actual get the
loader from DT. This was something that was already in mind
from when the FS loader was implemented but then it was
never implemented in favor of a single loader per device.
The first patch is a minor fixup for something that probably
won't ever happen.
The second one is a good reworking of the FS and FW loader
moving the internal struct to a dedicated header. (this is
really to enforce what drivers should use and what
driver should not mess with)
Then there is the request_firmware_size() new OP to get only
the size of the firmware. Useful for case where the firmware
size is not always the same and change across different version.
(the patter might be get size -> alloc buffer -> get firmware).
Then the FIP loader as a basic parser of FIP. This only
read the FIP header, loop all the entry and search for a
matching UUID. If nothing is found then no firmware blob.
Very simple implementation.
This is being CI tested on [0]
[0] https://github.com/u-boot/u-boot/pull/884
Christian Marangi (5):
misc: fs_loader: fix ubifs not unmounted on dev_get_priv error
misc: fs_loader: reorganize and split to FS and FW loader
misc: fw_loader: implement generic get_fw_loader_from_node()
misc: fw_loader: introduce FIP loader driver
misc: fw_loader: implement request_firmware_size() OP
drivers/misc/Kconfig | 16 +
drivers/misc/Makefile | 2 +-
drivers/misc/fw_loader/Makefile | 5 +
drivers/misc/fw_loader/fip_loader.c | 610 +++++++++++++++++++++++
drivers/misc/{ => fw_loader}/fs_loader.c | 180 +++----
drivers/misc/fw_loader/fw_loader.c | 184 +++++++
drivers/misc/fw_loader/internal.h | 63 +++
include/dm/uclass-id.h | 3 +-
include/fs_loader.h | 47 +-
include/fw_loader.h | 41 ++
10 files changed, 980 insertions(+), 171 deletions(-)
create mode 100644 drivers/misc/fw_loader/Makefile
create mode 100644 drivers/misc/fw_loader/fip_loader.c
rename drivers/misc/{ => fw_loader}/fs_loader.c (62%)
create mode 100644 drivers/misc/fw_loader/fw_loader.c
create mode 100644 drivers/misc/fw_loader/internal.h
--
2.51.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error
2026-03-03 13:29 [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader Christian Marangi
@ 2026-03-03 13:29 ` Christian Marangi
2026-03-03 13:38 ` Daniel Golle
2026-03-03 13:29 ` [PATCH 2/5] misc: fs_loader: reorganize and split to FS and FW loader Christian Marangi
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:29 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Casey Connolly, Christian Marangi,
Peng Fan, Quentin Schulz, Harsha Vardhan V M, Neha Malcom Francis,
Chen-Yu Tsai, Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang,
Weijie Gao, Marek Vasut, Lucien.Jheng, Sky Huang,
Alif Zakuan Yuslaimi, u-boot
When dev_get_priv errors out, the ubifs is not unbounted (if used)
Correctly handle this handle condition and while at it also return -EINVAL
instead of -ENOMEM as a better error since no memory is allocated but is
actually an invalid scenario for fw_get_filesystem_firmware().
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/misc/fs_loader.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c
index 2928cf75f89e..7e432a7ebd62 100644
--- a/drivers/misc/fs_loader.c
+++ b/drivers/misc/fs_loader.c
@@ -178,8 +178,10 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
struct firmware *firmwarep = dev_get_priv(dev);
- if (!firmwarep)
- return -ENOMEM;
+ if (!firmwarep) {
+ ret = -EINVAL;
+ goto out;
+ }
ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
firmwarep->offset, firmwarep->size, &actread);
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/5] misc: fs_loader: reorganize and split to FS and FW loader
2026-03-03 13:29 [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader Christian Marangi
2026-03-03 13:29 ` [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error Christian Marangi
@ 2026-03-03 13:29 ` Christian Marangi
2026-03-03 13:29 ` [PATCH 3/5] misc: fw_loader: implement generic get_fw_loader_from_node() Christian Marangi
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:29 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Casey Connolly, Christian Marangi,
Peng Fan, Quentin Schulz, Harsha Vardhan V M, Neha Malcom Francis,
Chen-Yu Tsai, Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang,
Weijie Gao, Marek Vasut, Lucien.Jheng, Sky Huang,
Alif Zakuan Yuslaimi, u-boot
In preparation to the introduction of variant of the FS loader,
reorganize and split the driver to generic fw_loader function and
specific fs_loader function. Create a dedicated directory for the loader
and move the internal structs and functions to a dedicated header file.
This will permit to reuse all the property and logic of FS loader
with container that are not exactly a readable filesystem.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/misc/Kconfig | 5 +
drivers/misc/Makefile | 2 +-
drivers/misc/fw_loader/Makefile | 4 +
drivers/misc/{ => fw_loader}/fs_loader.c | 134 ++---------------------
drivers/misc/fw_loader/fw_loader.c | 121 ++++++++++++++++++++
drivers/misc/fw_loader/internal.h | 60 ++++++++++
include/fs_loader.h | 47 +-------
include/fw_loader.h | 19 ++++
8 files changed, 220 insertions(+), 172 deletions(-)
create mode 100644 drivers/misc/fw_loader/Makefile
rename drivers/misc/{ => fw_loader}/fs_loader.c (60%)
create mode 100644 drivers/misc/fw_loader/fw_loader.c
create mode 100644 drivers/misc/fw_loader/internal.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a0aa290480eb..9d332230b1f9 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -610,8 +610,12 @@ config MPC83XX_SERDES
help
Support for serdes found on MPC83xx SoCs.
+config FW_LOADER
+ bool
+
config FS_LOADER
bool "Enable loader driver for file system"
+ select FW_LOADER
help
This is file system generic loader which can be used to load
the file image from the storage into target such as memory.
@@ -621,6 +625,7 @@ config FS_LOADER
config SPL_FS_LOADER
bool "Enable loader driver for file system in SPL"
+ select FW_LOADER
depends on SPL
help
This is file system generic loader which can be used to load
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 1d950f7a0ab2..c1b83d576bbb 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -36,7 +36,7 @@ obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_FSL_IIM) += fsl_iim.o
obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
-obj-$(CONFIG_$(PHASE_)FS_LOADER) += fs_loader.o
+obj-$(CONFIG_FW_LOADER) += fw_loader/
obj-$(CONFIG_GATEWORKS_SC) += gsc.o
obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
diff --git a/drivers/misc/fw_loader/Makefile b/drivers/misc/fw_loader/Makefile
new file mode 100644
index 000000000000..96baebede788
--- /dev/null
+++ b/drivers/misc/fw_loader/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y += fw_loader.o
+obj-$(CONFIG_$(PHASE_)FS_LOADER) += fs_loader.o
diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fw_loader/fs_loader.c
similarity index 60%
rename from drivers/misc/fs_loader.c
rename to drivers/misc/fw_loader/fs_loader.c
index 7e432a7ebd62..4ae01b9c8f0d 100644
--- a/drivers/misc/fs_loader.c
+++ b/drivers/misc/fw_loader/fs_loader.c
@@ -25,25 +25,9 @@
#include <ubi_uboot.h>
#endif
-DECLARE_GLOBAL_DATA_PTR;
+#include "internal.h"
-/**
- * struct firmware - A place for storing firmware and its attribute data.
- *
- * This holds information about a firmware and its content.
- *
- * @size: Size of a file
- * @data: Buffer for file
- * @priv: Firmware loader private fields
- * @name: Filename
- * @offset: Offset of reading a file
- */
-struct firmware {
- size_t size;
- const u8 *data;
- const char *name;
- u32 offset;
-};
+DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_CMD_UBIFS
static int mount_ubifs(char *mtdpart, char *ubivol)
@@ -109,37 +93,6 @@ static int select_fs_dev(struct device_plat *plat)
return ret;
}
-/**
- * _request_firmware_prepare - Prepare firmware struct.
- *
- * @dev: An instance of a driver.
- * @name: Name of firmware file.
- * @dbuf: Address of buffer to load firmware into.
- * @size: Size of buffer.
- * @offset: Offset of a file for start reading into buffer.
- *
- * Return: Negative value if fail, 0 for successful.
- */
-static int _request_firmware_prepare(struct udevice *dev,
- const char *name, void *dbuf,
- size_t size, u32 offset)
-{
- if (!name || name[0] == '\0')
- return -EINVAL;
-
- struct firmware *firmwarep = dev_get_priv(dev);
-
- if (!firmwarep)
- return -ENOMEM;
-
- firmwarep->name = name;
- firmwarep->offset = offset;
- firmwarep->data = dbuf;
- firmwarep->size = size;
-
- return 0;
-}
-
/**
* fw_get_filesystem_firmware - load firmware into an allocated buffer.
* @dev: An instance of a driver.
@@ -200,87 +153,16 @@ out:
return ret;
}
-/**
- * request_firmware_into_buf - Load firmware into a previously allocated buffer.
- * @dev: An instance of a driver.
- * @name: Name of firmware file.
- * @buf: Address of buffer to load firmware into.
- * @size: Size of buffer.
- * @offset: Offset of a file for start reading into buffer.
- *
- * The firmware is loaded directly into the buffer pointed to by @buf.
- *
- * Return: Size of total read, negative value when error.
- */
-int request_firmware_into_buf(struct udevice *dev,
- const char *name,
- void *buf, size_t size, u32 offset)
-{
- int ret;
-
- if (!dev)
- return -EINVAL;
-
- ret = _request_firmware_prepare(dev, name, buf, size, offset);
- if (ret < 0) /* error */
- return ret;
-
- ret = fw_get_filesystem_firmware(dev);
-
- return ret;
-}
-
-static int fs_loader_of_to_plat(struct udevice *dev)
-{
- u32 phandlepart[2];
-
- ofnode fs_loader_node = dev_ofnode(dev);
-
- if (ofnode_valid(fs_loader_node)) {
- struct device_plat *plat;
-
- plat = dev_get_plat(dev);
- if (!ofnode_read_u32_array(fs_loader_node,
- "phandlepart",
- phandlepart, 2)) {
- plat->phandlepart.phandle = phandlepart[0];
- plat->phandlepart.partition = phandlepart[1];
- }
-
- plat->mtdpart = (char *)ofnode_read_string(
- fs_loader_node, "mtdpart");
-
- plat->ubivol = (char *)ofnode_read_string(
- fs_loader_node, "ubivol");
- }
-
- return 0;
-}
-
static int fs_loader_probe(struct udevice *dev)
{
-#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
- int ret;
struct device_plat *plat = dev_get_plat(dev);
+ int ret;
- if (plat->phandlepart.phandle) {
- ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle);
- struct udevice *parent_dev = NULL;
-
- ret = device_get_global_by_ofnode(node, &parent_dev);
- if (!ret) {
- struct udevice *dev;
-
- ret = blk_get_from_parent(parent_dev, &dev);
- if (ret) {
- debug("fs_loader: No block device: %d\n",
- ret);
+ ret = generic_fw_loader_probe(dev);
+ if (ret)
+ return ret;
- return ret;
- }
- }
- }
-#endif
+ plat->get_firmware = fw_get_filesystem_firmware;
return 0;
};
@@ -295,7 +177,7 @@ U_BOOT_DRIVER(fs_loader) = {
.id = UCLASS_FS_FIRMWARE_LOADER,
.of_match = fs_loader_ids,
.probe = fs_loader_probe,
- .of_to_plat = fs_loader_of_to_plat,
+ .of_to_plat = generic_fw_loader_of_to_plat,
.plat_auto = sizeof(struct device_plat),
.priv_auto = sizeof(struct firmware),
};
diff --git a/drivers/misc/fw_loader/fw_loader.c b/drivers/misc/fw_loader/fw_loader.c
new file mode 100644
index 000000000000..6459ba23a15b
--- /dev/null
+++ b/drivers/misc/fw_loader/fw_loader.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2019 Intel Corporation <www.intel.com>
+ *
+ */
+
+#include <errno.h>
+#include <blk.h>
+#include <linux/types.h>
+#include <dm/device.h>
+#include <fw_loader.h>
+
+#include "internal.h"
+
+int generic_fw_loader_of_to_plat(struct udevice *dev)
+{
+ u32 phandlepart[2];
+
+ ofnode fw_loader_node = dev_ofnode(dev);
+
+ if (ofnode_valid(fw_loader_node)) {
+ struct device_plat *plat;
+
+ plat = dev_get_plat(dev);
+ if (!ofnode_read_u32_array(fw_loader_node,
+ "phandlepart",
+ phandlepart, 2)) {
+ plat->phandlepart.phandle = phandlepart[0];
+ plat->phandlepart.partition = phandlepart[1];
+ }
+
+ plat->mtdpart = (char *)ofnode_read_string(fw_loader_node,
+ "mtdpart");
+
+ plat->ubivol = (char *)ofnode_read_string(fw_loader_node,
+ "ubivol");
+ }
+
+ return 0;
+}
+
+int generic_fw_loader_probe(struct udevice *dev)
+{
+#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
+ int ret;
+ struct device_plat *plat = dev_get_plat(dev);
+
+ if (plat->phandlepart.phandle) {
+ ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle);
+ struct udevice *parent_dev = NULL;
+
+ ret = device_get_global_by_ofnode(node, &parent_dev);
+ if (!ret) {
+ struct udevice *dev;
+
+ ret = blk_get_from_parent(parent_dev, &dev);
+ if (ret) {
+ debug("fw_loader: No block device: %d\n",
+ ret);
+
+ return ret;
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * _request_firmware_prepare - Prepare firmware struct.
+ *
+ * @dev: An instance of a driver.
+ * @name: Name of firmware file.
+ * @dbuf: Address of buffer to load firmware into.
+ * @size: Size of buffer.
+ * @offset: Offset of a file for start reading into buffer.
+ *
+ * Return: Negative value if fail, 0 for successful.
+ */
+static int _request_firmware_prepare(struct udevice *dev,
+ const char *name, void *dbuf,
+ size_t size, u32 offset)
+{
+ if (!name || name[0] == '\0')
+ return -EINVAL;
+
+ struct firmware *firmwarep = dev_get_priv(dev);
+
+ if (!firmwarep)
+ return -ENOMEM;
+
+ firmwarep->name = name;
+ firmwarep->offset = offset;
+ firmwarep->data = dbuf;
+ firmwarep->size = size;
+
+ return 0;
+}
+
+int request_firmware_into_buf(struct udevice *dev,
+ const char *name,
+ void *buf, size_t size, u32 offset)
+{
+ struct device_plat *plat;
+ int ret;
+
+ if (!dev)
+ return -EINVAL;
+
+ ret = _request_firmware_prepare(dev, name, buf, size, offset);
+ if (ret < 0) /* error */
+ return ret;
+
+ plat = dev_get_plat(dev);
+
+ if (!plat->get_firmware)
+ return -EOPNOTSUPP;
+
+ return plat->get_firmware(dev);
+}
diff --git a/drivers/misc/fw_loader/internal.h b/drivers/misc/fw_loader/internal.h
new file mode 100644
index 000000000000..fc78a4add59d
--- /dev/null
+++ b/drivers/misc/fw_loader/internal.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018-2019 Intel Corporation <www.intel.com>
+ */
+#ifndef _FW_LOADER_INTERNAL_H_
+#define _FW_LOADER_INTERNAL_H_
+
+/**
+ * struct phandle_part - A place for storing phandle of node and its partition
+ *
+ * This holds information about a phandle of the block device, and its
+ * partition where the firmware would be loaded from.
+ *
+ * @phandle: Phandle of storage device node
+ * @partition: Partition of block device
+ */
+struct phandle_part {
+ u32 phandle;
+ u32 partition;
+};
+
+/**
+ * struct phandle_part - A place for storing all supported storage devices
+ *
+ * This holds information about all supported storage devices for driver use.
+ *
+ * @phandlepart: Attribute data for block device.
+ * @mtdpart: MTD partition for ubi partition.
+ * @ubivol: UBI volume-name for ubifsmount.
+ */
+struct device_plat {
+ struct phandle_part phandlepart;
+ char *mtdpart;
+ char *ubivol;
+
+ int (*get_firmware)(struct udevice *dev);
+};
+
+/**
+ * struct firmware - A place for storing firmware and its attribute data.
+ *
+ * This holds information about a firmware and its content.
+ *
+ * @size: Size of a file
+ * @data: Buffer for file
+ * @priv: Firmware loader private fields
+ * @name: Filename
+ * @offset: Offset of reading a file
+ */
+struct firmware {
+ size_t size;
+ const u8 *data;
+ const char *name;
+ u32 offset;
+};
+
+int generic_fw_loader_probe(struct udevice *dev);
+int generic_fw_loader_of_to_plat(struct udevice *dev);
+
+#endif
diff --git a/include/fs_loader.h b/include/fs_loader.h
index 7e16e0f70309..3c64efe1b439 100644
--- a/include/fs_loader.h
+++ b/include/fs_loader.h
@@ -6,52 +6,9 @@
#ifndef _FS_LOADER_H_
#define _FS_LOADER_H_
-struct udevice;
-
-/**
- * struct phandle_part - A place for storing phandle of node and its partition
- *
- * This holds information about a phandle of the block device, and its
- * partition where the firmware would be loaded from.
- *
- * @phandle: Phandle of storage device node
- * @partition: Partition of block device
- */
-struct phandle_part {
- u32 phandle;
- u32 partition;
-};
-
-/**
- * struct phandle_part - A place for storing all supported storage devices
- *
- * This holds information about all supported storage devices for driver use.
- *
- * @phandlepart: Attribute data for block device.
- * @mtdpart: MTD partition for ubi partition.
- * @ubivol: UBI volume-name for ubifsmount.
- */
-struct device_plat {
- struct phandle_part phandlepart;
- char *mtdpart;
- char *ubivol;
-};
+#include <fw_loader.h>
-/**
- * request_firmware_into_buf - Load firmware into a previously allocated buffer.
- * @dev: An instance of a driver.
- * @name: Name of firmware file.
- * @buf: Address of buffer to load firmware into.
- * @size: Size of buffer.
- * @offset: Offset of a file for start reading into buffer.
- *
- * The firmware is loaded directly into the buffer pointed to by @buf.
- *
- * Return: Size of total read, negative value when error.
- */
-int request_firmware_into_buf(struct udevice *dev,
- const char *name,
- void *buf, size_t size, u32 offset);
+struct udevice;
/**
* get_fs_loader() - Get the chosen filesystem loader
diff --git a/include/fw_loader.h b/include/fw_loader.h
index 35574482b2b9..56f5e3be6195 100644
--- a/include/fw_loader.h
+++ b/include/fw_loader.h
@@ -1,10 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
+ * Copyright (C) 2018-2019 Intel Corporation <www.intel.com>
* Copyright (C) 2025 Lucien Jheng <lucienzx159@gmail.com>
*/
#ifndef _FW_LOADER_H_
#define _FW_LOADER_H_
+struct udevice;
+
+/**
+ * request_firmware_into_buf - Load firmware into a previously allocated buffer.
+ * @dev: An instance of a driver.
+ * @name: Name of firmware file.
+ * @buf: Address of buffer to load firmware into.
+ * @size: Size of buffer.
+ * @offset: Offset of a file for start reading into buffer.
+ *
+ * The firmware is loaded directly into the buffer pointed to by @buf.
+ *
+ * Return: Size of total read, negative value when error.
+ */
+int request_firmware_into_buf(struct udevice *dev,
+ const char *name,
+ void *buf, size_t size, u32 offset);
+
/**
* request_firmware_into_buf_via_script() -
* Load firmware using a U-Boot script and copy to buffer
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/5] misc: fw_loader: implement generic get_fw_loader_from_node()
2026-03-03 13:29 [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader Christian Marangi
2026-03-03 13:29 ` [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error Christian Marangi
2026-03-03 13:29 ` [PATCH 2/5] misc: fs_loader: reorganize and split to FS and FW loader Christian Marangi
@ 2026-03-03 13:29 ` Christian Marangi
2026-03-03 13:29 ` [PATCH 4/5] misc: fw_loader: introduce FIP loader driver Christian Marangi
2026-03-03 13:29 ` [PATCH 5/5] misc: fw_loader: implement request_firmware_size() OP Christian Marangi
4 siblings, 0 replies; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:29 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Casey Connolly, Christian Marangi,
Peng Fan, Quentin Schulz, Harsha Vardhan V M, Neha Malcom Francis,
Chen-Yu Tsai, Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang,
Weijie Gao, Marek Vasut, Lucien.Jheng, Sky Huang,
Alif Zakuan Yuslaimi, u-boot
Implement a generic function to get a FW loader dev from a node.
There is currently only get_fs_loader() but that is limited to chosen
node and is limited only to FS loader.
Introduce get_fw_loader_from_node() that will parse the
"firmware-loader" from a specified node and will loop over all the
available FW loader until one is found (as it will be probed by the
dedicated compatible)
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/misc/fw_loader/fw_loader.c | 40 ++++++++++++++++++++++++++++++
include/fw_loader.h | 13 ++++++++++
2 files changed, 53 insertions(+)
diff --git a/drivers/misc/fw_loader/fw_loader.c b/drivers/misc/fw_loader/fw_loader.c
index 6459ba23a15b..e477a631fae3 100644
--- a/drivers/misc/fw_loader/fw_loader.c
+++ b/drivers/misc/fw_loader/fw_loader.c
@@ -8,6 +8,7 @@
#include <blk.h>
#include <linux/types.h>
#include <dm/device.h>
+#include <dm/uclass.h>
#include <fw_loader.h>
#include "internal.h"
@@ -67,6 +68,45 @@ int generic_fw_loader_probe(struct udevice *dev)
return 0;
}
+static int fw_loaders[] = {
+#if CONFIG_IS_ENABLED(FS_LOADER)
+ UCLASS_FS_FIRMWARE_LOADER,
+#endif
+};
+
+/**
+ * get_fw_loader_from_node - Get FW loader dev from @node.
+ *
+ * @node: ofnode where "firmware-loader" phandle is stored.
+ * @dev: pointer where to store the FW loader dev.
+ *
+ * Loop over all the supported FW loader and find a matching
+ * one.
+ *
+ * Return: Negative value if fail, 0 for successful.
+ */
+int get_fw_loader_from_node(ofnode node, struct udevice **dev)
+{
+ int i, ret;
+
+ node = ofnode_parse_phandle(node, "firmware-loader", 0);
+ if (!ofnode_valid(node))
+ return -ENODEV;
+
+ /*
+ * Loop over all the available FW loaders and stop when
+ * found one.
+ */
+ for (i = 0; i < ARRAY_SIZE(fw_loaders); i++) {
+ ret = uclass_get_device_by_ofnode(fw_loaders[i],
+ node, dev);
+ if (!ret)
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
/**
* _request_firmware_prepare - Prepare firmware struct.
*
diff --git a/include/fw_loader.h b/include/fw_loader.h
index 56f5e3be6195..d07306c0674f 100644
--- a/include/fw_loader.h
+++ b/include/fw_loader.h
@@ -8,6 +8,19 @@
struct udevice;
+/**
+ * get_fw_loader_from_node - Get FW loader dev from @node.
+ *
+ * @node: ofnode where "firmware-loader" phandle is stored.
+ * @dev: pointer where to store the FW loader dev.
+ *
+ * Loop over all the supported FW loader and find a matching
+ * one.
+ *
+ * Return: Negative value if fail, 0 for successful.
+ */
+int get_fw_loader_from_node(ofnode node, struct udevice **dev);
+
/**
* request_firmware_into_buf - Load firmware into a previously allocated buffer.
* @dev: An instance of a driver.
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/5] misc: fw_loader: introduce FIP loader driver
2026-03-03 13:29 [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader Christian Marangi
` (2 preceding siblings ...)
2026-03-03 13:29 ` [PATCH 3/5] misc: fw_loader: implement generic get_fw_loader_from_node() Christian Marangi
@ 2026-03-03 13:29 ` Christian Marangi
2026-03-03 13:29 ` [PATCH 5/5] misc: fw_loader: implement request_firmware_size() OP Christian Marangi
4 siblings, 0 replies; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:29 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Casey Connolly, Christian Marangi,
Peng Fan, Quentin Schulz, Harsha Vardhan V M, Neha Malcom Francis,
Chen-Yu Tsai, Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang,
Weijie Gao, Marek Vasut, Lucien.Jheng, Sky Huang,
Alif Zakuan Yuslaimi, u-boot
Introduce a variant of the FS loader driver to extract images from FIP
image. These image can contain additional binary used to init Network
accellerator or PHY firmware blob.
The way FIP handle image type is with the usage of UUID.
This FIP loader driver implement a simple FIP image parser that check
every entry for a matching UUID.
Similar to FS loader, this driver also support both UBI and Block
devices.
Also an additional property is added to handle special case with eMMC
that doesn't have a GPT partition and require a global offset to
reference the FIP partition.
An example usage of this driver is the following:
Entry in DTS:
fs_loader0: fip-loader {
bootph-all;
compatible = "u-boot,fip-loader";
phandlepart = <&mmc0 0>;
partoffset = <0x100>;
};
ethernet@1fb50000 {
firmware-loader = <&fs_loader0>;
}
FIP loader user:
/* get the FW loader from the ethernet node */
get_fw_loader_from_node(dev_ofnode(dev), &fw_loader);
/* read the blob identified by "58704aef-389f-3e52-b475-e0bf2234a6a2" UUID */
request_firmware_into_buf(fw_loader, "58704aef-389f-3e52-b475-e0bf2234a6a2",
buf, 261400, 0);
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/misc/Kconfig | 11 +
drivers/misc/fw_loader/Makefile | 1 +
drivers/misc/fw_loader/fip_loader.c | 575 ++++++++++++++++++++++++++++
drivers/misc/fw_loader/fw_loader.c | 3 +
drivers/misc/fw_loader/internal.h | 2 +
include/dm/uclass-id.h | 3 +-
6 files changed, 594 insertions(+), 1 deletion(-)
create mode 100644 drivers/misc/fw_loader/fip_loader.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 9d332230b1f9..8d2f11de0fe7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -613,6 +613,17 @@ config MPC83XX_SERDES
config FW_LOADER
bool
+config FIP_LOADER
+ bool "Enable loader driver from FIP partition"
+ select LIB_UUID
+ select FW_LOADER
+ help
+ This is FIP partition generic loader which can be used to load
+ the file image from the FIP image into target such as memory.
+
+ The consumer driver would then use this loader to program whatever,
+ ie. the FPGA device/PHY firmware.
+
config FS_LOADER
bool "Enable loader driver for file system"
select FW_LOADER
diff --git a/drivers/misc/fw_loader/Makefile b/drivers/misc/fw_loader/Makefile
index 96baebede788..7854b64148e6 100644
--- a/drivers/misc/fw_loader/Makefile
+++ b/drivers/misc/fw_loader/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
obj-y += fw_loader.o
+obj-$(CONFIG_FIP_LOADER) += fip_loader.o
obj-$(CONFIG_$(PHASE_)FS_LOADER) += fs_loader.o
diff --git a/drivers/misc/fw_loader/fip_loader.c b/drivers/misc/fw_loader/fip_loader.c
new file mode 100644
index 000000000000..376e1d2a59d8
--- /dev/null
+++ b/drivers/misc/fw_loader/fip_loader.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Christian Marangi <ansuelsmth@gmail.com>
+ *
+ */
+
+#define LOG_CATEGORY UCLASS_FIP_FIRMWARE_LOADER
+
+#include <dm.h>
+#include <div64.h>
+#include <env.h>
+#include <errno.h>
+#include <blk.h>
+#include <fs.h>
+#include <fs_loader.h>
+#include <log.h>
+#include <mapmem.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <u-boot/uuid.h>
+
+#ifdef CONFIG_CMD_UBIFS
+#include <ubi_uboot.h>
+#endif
+
+#include "internal.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define TOC_HEADER_NAME 0xaa640001
+
+struct fip_toc_header {
+ u32 name;
+ u32 serial_number;
+ u64 flags;
+};
+
+struct fip_toc_entry {
+ struct uuid uuid;
+ u64 offset_address;
+ u64 size;
+ u64 flags;
+};
+
+enum fip_storage_interface {
+ FIP_STORAGE_INTERFACE_BLK,
+ FIP_STORAGE_INTERFACE_UBI,
+};
+
+struct fip_storage_info {
+ enum fip_storage_interface storage_interface;
+
+ /* BLK info */
+ struct disk_partition part_info;
+ struct blk_desc *desc;
+ unsigned int part_offset;
+
+ /* UBI info */
+ char *ubi_volume;
+};
+
+#ifdef CONFIG_CMD_UBIFS
+static int mount_ubifs(char *mtdpart, char *ubivol)
+{
+ int ret;
+
+ ret = ubi_part(mtdpart, NULL);
+ if (ret)
+ log_err("Cannot find mtd partition %s\n", mtdpart);
+
+ return ret;
+}
+
+static void umount_ubifs(void)
+{
+ cmd_ubifs_umount();
+}
+#else
+static int mount_ubifs(char *mtdpart, char *ubivol)
+{
+ log_err("Cannot load image: no UBIFS support.\n");
+ return -ENOSYS;
+}
+
+static void umount_ubifs(void)
+{
+}
+#endif
+
+static bool validate_fip_toc_header(struct fip_toc_header *hdr)
+{
+ if (hdr->name != TOC_HEADER_NAME) {
+ log_err("Invalid FIP header\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int firmware_name_to_uuid(struct firmware *firmwarep,
+ struct uuid *uuid)
+{
+ const char *uuid_str = firmwarep->name;
+ int ret;
+
+ ret = uuid_str_to_bin(uuid_str, (unsigned char *)uuid,
+ UUID_STR_FORMAT_STD);
+ if (ret)
+ log_err("Invalid UUID str: %s\n", uuid_str);
+
+ return ret;
+}
+
+static int check_fip_toc_entry(struct fip_toc_entry *ent,
+ struct uuid *uuid,
+ struct fip_toc_entry *dent)
+{
+ struct uuid uuid_null = { };
+
+ /* NULL uuid. We parsed every entry */
+ if (!memcmp(&ent->uuid, &uuid_null, sizeof(uuid_null)))
+ return -ENOENT;
+
+ /* We found the related uuid */
+ if (!memcmp(&ent->uuid, uuid, sizeof(*uuid))) {
+ log_debug("Found matching FIP entry. offset: 0x%llx size: %lld\n",
+ ent->offset_address, ent->size);
+ memcpy(dent, ent, sizeof(*ent));
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
+static int blk_read_fip_toc_header(struct blk_desc *desc, u32 offset,
+ char *buf, struct fip_toc_header *hdr)
+{
+ unsigned int blkcnt = BLOCK_CNT(sizeof(*hdr), desc);
+ size_t read = 0;
+ int i;
+
+ for (i = 0; i < blkcnt && read < sizeof(*hdr); i++) {
+ unsigned int to_read = MIN(desc->blksz,
+ sizeof(*hdr) - read);
+
+ blk_dread(desc, offset + i, 1, buf);
+
+ memcpy((u8 *)hdr + read, buf, to_read);
+ read += to_read;
+ }
+
+ return read;
+}
+
+static int blk_read_fip_toc_entry(struct blk_desc *desc, u32 offset,
+ int pos, char *buf,
+ struct fip_toc_entry *ent)
+{
+ unsigned int left, consumed, to_read, read = 0;
+ unsigned int blkstart, blkcnt;
+ int i;
+
+ consumed = pos % desc->blksz;
+ left = desc->blksz - consumed;
+ to_read = MIN(left, sizeof(*ent));
+
+ blkstart = BLOCK_CNT(pos, desc);
+ blkcnt = BLOCK_CNT(sizeof(*ent) - to_read, desc);
+
+ /* Read data from previous cached block if present */
+ if (left) {
+ memcpy(ent, buf + consumed, to_read);
+ read += to_read;
+ }
+
+ for (i = 0; i < blkcnt && read < sizeof(*ent); i++) {
+ to_read = MIN(desc->blksz, sizeof(*ent) - read);
+
+ blk_dread(desc, offset + blkstart + i, 1, buf);
+
+ memcpy((u8 *)ent + read, buf, to_read);
+ read += to_read;
+ }
+
+ return read;
+}
+
+static int blk_parse_fip_firmware(struct firmware *firmwarep,
+ struct blk_desc *desc,
+ struct disk_partition *part_info,
+ unsigned int part_offset,
+ struct fip_toc_entry *dent)
+{
+ unsigned int offset = part_info->start + part_offset;
+ struct fip_toc_header hdr;
+ struct fip_toc_entry ent;
+ struct uuid uuid;
+ unsigned int pos;
+ char *read_buf;
+ int ret;
+
+ /* Allocate a Scratch Buffer for FIP parsing */
+ read_buf = malloc(desc->blksz);
+ if (!read_buf)
+ return -ENOMEM;
+
+ pos = blk_read_fip_toc_header(desc, offset, read_buf, &hdr);
+
+ if (!validate_fip_toc_header(&hdr)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = firmware_name_to_uuid(firmwarep, &uuid);
+ if (ret)
+ goto out;
+
+ /* Loop for every FIP entry searching for uuid */
+ while (true) {
+ pos += blk_read_fip_toc_entry(desc, offset, pos,
+ read_buf, &ent);
+
+ ret = check_fip_toc_entry(&ent, &uuid, dent);
+ if (ret != -EAGAIN)
+ break;
+ }
+
+out:
+ free(read_buf);
+ return ret;
+}
+
+#ifdef CONFIG_CMD_UBIFS
+static int ubi_parse_fip_firmware(struct firmware *firmwarep,
+ char *ubi_vol,
+ struct fip_toc_entry *dent)
+{
+ struct fip_toc_header hdr;
+ struct fip_toc_entry ent;
+ struct uuid uuid;
+ unsigned int pos;
+ int ret;
+
+ ret = ubi_volume_read(ubi_vol, (char *)&hdr, 0, sizeof(hdr));
+ if (ret)
+ return ret;
+
+ pos = sizeof(hdr);
+
+ if (!validate_fip_toc_header(&hdr))
+ return -EINVAL;
+
+ ret = firmware_name_to_uuid(firmwarep, &uuid);
+ if (ret)
+ return ret;
+
+ /* Loop for every FIP entry searching for uuid */
+ while (true) {
+ ret = ubi_volume_read(ubi_vol, (char *)&ent, pos,
+ sizeof(ent));
+ if (ret)
+ return ret;
+
+ ret = check_fip_toc_entry(&ent, &uuid, dent);
+ if (ret != -EAGAIN)
+ break;
+
+ pos += sizeof(ent);
+ }
+
+ return ret;
+}
+#endif
+
+static int parse_fip_firmware(struct firmware *firmwarep,
+ struct fip_storage_info *info,
+ struct fip_toc_entry *dent)
+{
+ switch (info->storage_interface) {
+ case FIP_STORAGE_INTERFACE_BLK:
+ return blk_parse_fip_firmware(firmwarep, info->desc,
+ &info->part_info,
+ info->part_offset,
+ dent);
+#ifdef CONFIG_CMD_UBIFS
+ case FIP_STORAGE_INTERFACE_UBI:
+ return ubi_parse_fip_firmware(firmwarep,
+ info->ubi_volume,
+ dent);
+#endif
+ default:
+ return -EINVAL;
+ }
+}
+
+static int blk_read_fip_firmware(struct firmware *firmwarep,
+ struct blk_desc *desc,
+ struct disk_partition *part_info,
+ unsigned int part_offset,
+ const struct fip_toc_entry *ent)
+{
+ unsigned int offset = part_info->start + part_offset;
+ unsigned int pos, to_read, read = 0;
+ unsigned long long blkstart;
+ size_t size = ent->size;
+ unsigned int blkcnt;
+ char *read_buf;
+ int i, ret;
+
+ read_buf = malloc(desc->blksz);
+ if (!read_buf)
+ return -ENOMEM;
+
+ blkcnt = BLOCK_CNT(size + firmwarep->offset, desc);
+ blkstart = ent->offset_address + firmwarep->offset;
+ pos = do_div(blkstart, desc->blksz);
+
+ /* Read data in the middle of a block */
+ if (pos) {
+ to_read = MIN(desc->blksz - pos, size);
+ blk_dread(desc, offset + blkstart, 1, read_buf);
+
+ memcpy((u8 *)firmwarep->data, read_buf + pos, to_read);
+ read += to_read;
+ blkstart++;
+ }
+
+ /* Consume all the remaining block */
+ for (i = 0; i < blkcnt && read < size; i++) {
+ to_read = MIN(desc->blksz, size - read);
+ blk_dread(desc, offset + blkstart + i, 1, read_buf);
+
+ memcpy((u8 *)firmwarep->data + read, read_buf, to_read);
+ read += to_read;
+ }
+
+ ret = read;
+
+ free(read_buf);
+ return ret;
+}
+
+#ifdef CONFIG_CMD_UBIFS
+static int ubi_read_fip_firmware(struct firmware *firmwarep,
+ char *ubi_vol,
+ const struct fip_toc_entry *ent)
+{
+ unsigned int offset = firmwarep->offset;
+ size_t size = ent->size;
+ int ret;
+
+ ret = ubi_volume_read(ubi_vol,
+ (u8 *)firmwarep->data,
+ ent->offset_address + offset,
+ size - offset);
+ if (ret)
+ return ret;
+
+ return size - firmwarep->offset;
+}
+#endif
+
+static int read_fip_firmware(struct firmware *firmwarep,
+ struct fip_storage_info *info,
+ const struct fip_toc_entry *dent)
+{
+ switch (info->storage_interface) {
+ case FIP_STORAGE_INTERFACE_BLK:
+ return blk_read_fip_firmware(firmwarep, info->desc,
+ &info->part_info,
+ info->part_offset,
+ dent);
+#ifdef CONFIG_CMD_UBIFS
+ case FIP_STORAGE_INTERFACE_UBI:
+ return ubi_read_fip_firmware(firmwarep,
+ info->ubi_volume,
+ dent);
+#endif
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fw_parse_storage_info(struct udevice *dev,
+ struct fip_storage_info *info)
+{
+ char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
+ struct device_plat *plat = dev_get_plat(dev);
+ int ret;
+
+ storage_interface = env_get("storage_interface");
+ dev_part = env_get("fw_dev_part");
+ ubi_mtdpart = env_get("fw_ubi_mtdpart");
+ ubi_volume = env_get("fw_ubi_volume");
+ info->part_offset = env_get_hex("fw_partoffset", 0);
+
+ if (storage_interface && dev_part) {
+ int part;
+
+ part = part_get_info_by_dev_and_name_or_num(storage_interface,
+ dev_part,
+ &info->desc,
+ &info->part_info, 1);
+ if (part < 0)
+ return part;
+
+ info->storage_interface = FIP_STORAGE_INTERFACE_BLK;
+
+ return 0;
+ }
+
+ if (storage_interface && ubi_mtdpart && ubi_volume) {
+ if (strcmp("ubi", storage_interface))
+ return -ENODEV;
+
+ ret = mount_ubifs(ubi_mtdpart, ubi_volume);
+ if (ret)
+ return ret;
+
+ info->ubi_volume = ubi_volume;
+ info->storage_interface = FIP_STORAGE_INTERFACE_UBI;
+
+ return 0;
+ }
+
+ info->part_offset = plat->partoffset;
+
+ if (plat->phandlepart.phandle) {
+ struct udevice *disk_dev;
+ ofnode node;
+ int part;
+
+ node = ofnode_get_by_phandle(plat->phandlepart.phandle);
+
+ ret = device_get_global_by_ofnode(node, &disk_dev);
+ if (ret)
+ return ret;
+
+ info->desc = blk_get_by_device(disk_dev);
+ if (!info->desc)
+ return -ENODEV;
+
+ part = plat->phandlepart.partition;
+ if (part >= 1)
+ ret = part_get_info(info->desc, part,
+ &info->part_info);
+ else
+ ret = part_get_info_whole_disk(info->desc,
+ &info->part_info);
+
+ info->storage_interface = FIP_STORAGE_INTERFACE_BLK;
+
+ return ret;
+ }
+
+ if (plat->mtdpart && plat->ubivol) {
+ ret = mount_ubifs(plat->mtdpart, plat->ubivol);
+ if (ret)
+ return ret;
+
+ info->ubi_volume = plat->ubivol;
+ info->storage_interface = FIP_STORAGE_INTERFACE_UBI;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void fw_storage_info_release(struct udevice *dev,
+ const struct fip_storage_info *info)
+{
+ switch (info->storage_interface) {
+ case FIP_STORAGE_INTERFACE_UBI:
+ umount_ubifs();
+ return;
+ default:
+ return;
+ }
+}
+
+/**
+ * fw_get_fip_firmware - load firmware into an allocated buffer.
+ * @dev: An instance of a driver.
+ *
+ * Return: Size of total read, negative value when error.
+ */
+static int fw_get_fip_firmware(struct udevice *dev)
+{
+ struct fip_toc_entry ent;
+ struct fip_storage_info info = { };
+ int ret;
+
+ ret = fw_parse_storage_info(dev, &info);
+ if (ret)
+ goto out;
+
+ struct firmware *firmwarep = dev_get_priv(dev);
+
+ if (!firmwarep) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = parse_fip_firmware(firmwarep, &info, &ent);
+ if (ret)
+ goto out;
+
+ if (ent.size + firmwarep->offset > firmwarep->size) {
+ log_err("Not enough space to read firmware\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = read_fip_firmware(firmwarep, &info, &ent);
+ if (ret < 0)
+ log_err("Failed to read %s from FIP: %d.\n",
+ firmwarep->name, ret);
+
+out:
+ fw_storage_info_release(dev, &info);
+ return ret;
+}
+
+static int fip_loader_probe(struct udevice *dev)
+{
+ struct device_plat *plat = dev_get_plat(dev);
+ int ret;
+
+ ret = generic_fw_loader_probe(dev);
+ if (ret)
+ return ret;
+
+ plat->get_firmware = fw_get_fip_firmware;
+
+ return 0;
+};
+
+static int fip_loader_of_to_plat(struct udevice *dev)
+{
+ struct device_plat *plat = dev_get_plat(dev);
+ ofnode fip_loader_node = dev_ofnode(dev);
+ int ret;
+
+ ret = generic_fw_loader_of_to_plat(dev);
+ if (ret)
+ return ret;
+
+ /* Node validation is already done by the generic function */
+ ofnode_read_u32(fip_loader_node, "partoffset",
+ &plat->partoffset);
+
+ return 0;
+}
+
+static const struct udevice_id fip_loader_ids[] = {
+ { .compatible = "u-boot,fip-loader"},
+ { }
+};
+
+U_BOOT_DRIVER(fip_loader) = {
+ .name = "fip-loader",
+ .id = UCLASS_FIP_FIRMWARE_LOADER,
+ .of_match = fip_loader_ids,
+ .probe = fip_loader_probe,
+ .of_to_plat = fip_loader_of_to_plat,
+ .plat_auto = sizeof(struct device_plat),
+ .priv_auto = sizeof(struct firmware),
+};
+
+UCLASS_DRIVER(fip_loader) = {
+ .id = UCLASS_FIP_FIRMWARE_LOADER,
+ .name = "fip-loader",
+};
diff --git a/drivers/misc/fw_loader/fw_loader.c b/drivers/misc/fw_loader/fw_loader.c
index e477a631fae3..d2446fdc07da 100644
--- a/drivers/misc/fw_loader/fw_loader.c
+++ b/drivers/misc/fw_loader/fw_loader.c
@@ -72,6 +72,9 @@ static int fw_loaders[] = {
#if CONFIG_IS_ENABLED(FS_LOADER)
UCLASS_FS_FIRMWARE_LOADER,
#endif
+#if CONFIG_IS_ENABLED(FIP_LOADER)
+ UCLASS_FIP_FIRMWARE_LOADER,
+#endif
};
/**
diff --git a/drivers/misc/fw_loader/internal.h b/drivers/misc/fw_loader/internal.h
index fc78a4add59d..5513e043925f 100644
--- a/drivers/misc/fw_loader/internal.h
+++ b/drivers/misc/fw_loader/internal.h
@@ -25,11 +25,13 @@ struct phandle_part {
* This holds information about all supported storage devices for driver use.
*
* @phandlepart: Attribute data for block device.
+ * @partoffset: Global offset for BLK partition.
* @mtdpart: MTD partition for ubi partition.
* @ubivol: UBI volume-name for ubifsmount.
*/
struct device_plat {
struct phandle_part phandlepart;
+ int partoffset;
char *mtdpart;
char *ubivol;
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 36b5d87c304f..641d8e1e42fa 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -69,7 +69,8 @@ enum uclass_id {
UCLASS_FIRMWARE, /* Firmware */
UCLASS_FPGA, /* FPGA device */
UCLASS_FUZZING_ENGINE, /* Fuzzing engine */
- UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
+ UCLASS_FIP_FIRMWARE_LOADER, /* FIP image loader */
+ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
UCLASS_FWU_MDATA, /* FWU Metadata Access */
UCLASS_GPIO, /* Bank of general-purpose I/O pins */
UCLASS_HASH, /* Hash device */
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/5] misc: fw_loader: implement request_firmware_size() OP
2026-03-03 13:29 [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader Christian Marangi
` (3 preceding siblings ...)
2026-03-03 13:29 ` [PATCH 4/5] misc: fw_loader: introduce FIP loader driver Christian Marangi
@ 2026-03-03 13:29 ` Christian Marangi
4 siblings, 0 replies; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:29 UTC (permalink / raw)
To: Tom Rini, Simon Glass, Casey Connolly, Christian Marangi,
Peng Fan, Quentin Schulz, Harsha Vardhan V M, Neha Malcom Francis,
Chen-Yu Tsai, Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang,
Weijie Gao, Marek Vasut, Lucien.Jheng, Sky Huang,
Alif Zakuan Yuslaimi, u-boot
It might be useful to get the firmware size before reading it to allocate the
correct size. This is needed for case where the firmware size change across
different firmware version and is not always the same. In such case the only way
to handle this scenario is to allocate a big-enough buffer to read the firmware.
To better handle this, introduce the request_firmware_size() OP to get the
firmware size before reading it. This way proper buffer can be allocated and
dynamic firmware size can be better supported.
Both FS and FIP lodaer are updated to handle the new .get_size() handler.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/misc/fw_loader/fip_loader.c | 35 +++++++++++++++
drivers/misc/fw_loader/fs_loader.c | 70 ++++++++++++++++++++++++-----
drivers/misc/fw_loader/fw_loader.c | 20 +++++++++
drivers/misc/fw_loader/internal.h | 1 +
include/fw_loader.h | 9 ++++
5 files changed, 124 insertions(+), 11 deletions(-)
diff --git a/drivers/misc/fw_loader/fip_loader.c b/drivers/misc/fw_loader/fip_loader.c
index 376e1d2a59d8..a285f785db37 100644
--- a/drivers/misc/fw_loader/fip_loader.c
+++ b/drivers/misc/fw_loader/fip_loader.c
@@ -523,6 +523,40 @@ out:
return ret;
}
+/**
+ * fw_get_fip_firmware_size - get firmware size.
+ * @dev: An instance of a driver.
+ *
+ * Return: Size of firmware, negative value when error.
+ */
+static int fw_get_fip_firmware_size(struct udevice *dev)
+{
+ struct fip_toc_entry ent;
+ struct fip_storage_info info = { };
+ int ret;
+
+ ret = fw_parse_storage_info(dev, &info);
+ if (ret)
+ goto out;
+
+ struct firmware *firmwarep = dev_get_priv(dev);
+
+ if (!firmwarep) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = parse_fip_firmware(firmwarep, &info, &ent);
+ if (ret)
+ goto out;
+
+ ret = ent.size;
+
+out:
+ fw_storage_info_release(dev, &info);
+ return ret;
+}
+
static int fip_loader_probe(struct udevice *dev)
{
struct device_plat *plat = dev_get_plat(dev);
@@ -533,6 +567,7 @@ static int fip_loader_probe(struct udevice *dev)
return ret;
plat->get_firmware = fw_get_fip_firmware;
+ plat->get_size = fw_get_fip_firmware_size;
return 0;
};
diff --git a/drivers/misc/fw_loader/fs_loader.c b/drivers/misc/fw_loader/fs_loader.c
index 4ae01b9c8f0d..d6b9f4998815 100644
--- a/drivers/misc/fw_loader/fs_loader.c
+++ b/drivers/misc/fw_loader/fs_loader.c
@@ -93,15 +93,8 @@ static int select_fs_dev(struct device_plat *plat)
return ret;
}
-/**
- * fw_get_filesystem_firmware - load firmware into an allocated buffer.
- * @dev: An instance of a driver.
- *
- * Return: Size of total read, negative value when error.
- */
-static int fw_get_filesystem_firmware(struct udevice *dev)
+static int __fw_get_filesystem_firmware_prepare(struct udevice *dev)
{
- loff_t actread = 0;
char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
int ret;
@@ -126,6 +119,28 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
ret = select_fs_dev(dev_get_plat(dev));
}
+ return ret;
+}
+
+static void __fw_get_filesystem_firmware_release(struct udevice *dev)
+{
+#ifdef CONFIG_CMD_UBIFS
+ umount_ubifs();
+#endif
+}
+
+/**
+ * fw_get_filesystem_firmware - load firmware into an allocated buffer.
+ * @dev: An instance of a driver.
+ *
+ * Return: Size of total read, negative value when error.
+ */
+static int fw_get_filesystem_firmware(struct udevice *dev)
+{
+ loff_t actread = 0;
+ int ret;
+
+ ret = __fw_get_filesystem_firmware_prepare(dev);
if (ret)
goto out;
@@ -147,9 +162,41 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
}
out:
-#ifdef CONFIG_CMD_UBIFS
- umount_ubifs();
-#endif
+ __fw_get_filesystem_firmware_release(dev);
+ return ret;
+}
+
+/**
+ * fw_get_filesystem_firmware_size - get firmware size.
+ * @dev: An instance of a driver.
+ *
+ * Return: Size of firmware, negative value when error.
+ */
+static int fw_get_filesystem_firmware_size(struct udevice *dev)
+{
+ loff_t size = 0;
+ int ret;
+
+ ret = __fw_get_filesystem_firmware_prepare(dev);
+ if (ret)
+ goto out;
+
+ struct firmware *firmwarep = dev_get_priv(dev);
+
+ if (!firmwarep)
+ return -ENOMEM;
+
+ ret = fs_size(firmwarep->name, &size);
+
+ if (ret) {
+ debug("Error: %d Failed to get size for %s.\n",
+ ret, firmwarep->name);
+ } else {
+ ret = size;
+ }
+
+out:
+ __fw_get_filesystem_firmware_release(dev);
return ret;
}
@@ -163,6 +210,7 @@ static int fs_loader_probe(struct udevice *dev)
return ret;
plat->get_firmware = fw_get_filesystem_firmware;
+ plat->get_size = fw_get_filesystem_firmware_size;
return 0;
};
diff --git a/drivers/misc/fw_loader/fw_loader.c b/drivers/misc/fw_loader/fw_loader.c
index d2446fdc07da..d5973fce4b9c 100644
--- a/drivers/misc/fw_loader/fw_loader.c
+++ b/drivers/misc/fw_loader/fw_loader.c
@@ -162,3 +162,23 @@ int request_firmware_into_buf(struct udevice *dev,
return plat->get_firmware(dev);
}
+
+int request_firmware_size(struct udevice *dev, const char *name)
+{
+ struct device_plat *plat;
+ int ret;
+
+ if (!dev)
+ return -EINVAL;
+
+ ret = _request_firmware_prepare(dev, name, NULL, 0, 0);
+ if (ret < 0) /* error */
+ return ret;
+
+ plat = dev_get_plat(dev);
+
+ if (!plat->get_size)
+ return -EOPNOTSUPP;
+
+ return plat->get_size(dev);
+}
diff --git a/drivers/misc/fw_loader/internal.h b/drivers/misc/fw_loader/internal.h
index 5513e043925f..4701b494bf3f 100644
--- a/drivers/misc/fw_loader/internal.h
+++ b/drivers/misc/fw_loader/internal.h
@@ -36,6 +36,7 @@ struct device_plat {
char *ubivol;
int (*get_firmware)(struct udevice *dev);
+ int (*get_size)(struct udevice *dev);
};
/**
diff --git a/include/fw_loader.h b/include/fw_loader.h
index d07306c0674f..a3fa980c768d 100644
--- a/include/fw_loader.h
+++ b/include/fw_loader.h
@@ -37,6 +37,15 @@ int request_firmware_into_buf(struct udevice *dev,
const char *name,
void *buf, size_t size, u32 offset);
+/**
+ * request_firmware_size - Get firmware size.
+ * @dev: An instance of a driver.
+ * @name: Name of firmware file.
+ *
+ * Return: Size of firmware, negative value when error.
+ */
+int request_firmware_size(struct udevice *dev, const char *name);
+
/**
* request_firmware_into_buf_via_script() -
* Load firmware using a U-Boot script and copy to buffer
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error
2026-03-03 13:29 ` [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error Christian Marangi
@ 2026-03-03 13:38 ` Daniel Golle
2026-03-03 13:43 ` Christian Marangi
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Golle @ 2026-03-03 13:38 UTC (permalink / raw)
To: Christian Marangi
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Quentin Schulz,
Harsha Vardhan V M, Neha Malcom Francis, Chen-Yu Tsai,
Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang, Weijie Gao,
Marek Vasut, Lucien.Jheng, Sky Huang, Alif Zakuan Yuslaimi,
u-boot
On Tue, Mar 03, 2026 at 02:29:08PM +0100, Christian Marangi wrote:
> When dev_get_priv errors out, the ubifs is not unbounted (if used)
I don't understand why or where UBIFS is being used here.
Did you mean to say "UBI is not attached"?
UBI != UBIFS.
>
> Correctly handle this handle condition and while at it also return -EINVAL
> instead of -ENOMEM as a better error since no memory is allocated but is
> actually an invalid scenario for fw_get_filesystem_firmware().
>
> Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> ---
> drivers/misc/fs_loader.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c
> index 2928cf75f89e..7e432a7ebd62 100644
> --- a/drivers/misc/fs_loader.c
> +++ b/drivers/misc/fs_loader.c
> @@ -178,8 +178,10 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
>
> struct firmware *firmwarep = dev_get_priv(dev);
>
> - if (!firmwarep)
> - return -ENOMEM;
> + if (!firmwarep) {
> + ret = -EINVAL;
> + goto out;
> + }
>
> ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
> firmwarep->offset, firmwarep->size, &actread);
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error
2026-03-03 13:38 ` Daniel Golle
@ 2026-03-03 13:43 ` Christian Marangi
2026-03-03 13:53 ` Daniel Golle
0 siblings, 1 reply; 9+ messages in thread
From: Christian Marangi @ 2026-03-03 13:43 UTC (permalink / raw)
To: Daniel Golle
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Quentin Schulz,
Harsha Vardhan V M, Neha Malcom Francis, Chen-Yu Tsai,
Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang, Weijie Gao,
Marek Vasut, Lucien.Jheng, Sky Huang, Alif Zakuan Yuslaimi,
u-boot
On Tue, Mar 03, 2026 at 01:38:03PM +0000, Daniel Golle wrote:
> On Tue, Mar 03, 2026 at 02:29:08PM +0100, Christian Marangi wrote:
> > When dev_get_priv errors out, the ubifs is not unbounted (if used)
>
> I don't understand why or where UBIFS is being used here.
> Did you mean to say "UBI is not attached"?
> UBI != UBIFS.
>
Function that is not called is umount_ubifs. That calls cmd_ubifs_umount.
unbounted is a typo for unmounted.
Probably the confusion is present already in the FS loader driver where ubi
needed to be used instead of ubifs?
There are mount_ubifs and umount_ubifs but they should have been mount_ubi and
umount_ubi ? But then they use ubifs cmd OPs. I'm a bit confused here.
> >
> > Correctly handle this handle condition and while at it also return -EINVAL
> > instead of -ENOMEM as a better error since no memory is allocated but is
> > actually an invalid scenario for fw_get_filesystem_firmware().
> >
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > ---
> > drivers/misc/fs_loader.c | 6 ++++--
> > 1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c
> > index 2928cf75f89e..7e432a7ebd62 100644
> > --- a/drivers/misc/fs_loader.c
> > +++ b/drivers/misc/fs_loader.c
> > @@ -178,8 +178,10 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
> >
> > struct firmware *firmwarep = dev_get_priv(dev);
> >
> > - if (!firmwarep)
> > - return -ENOMEM;
> > + if (!firmwarep) {
> > + ret = -EINVAL;
> > + goto out;
> > + }
> >
> > ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
> > firmwarep->offset, firmwarep->size, &actread);
> > --
> > 2.51.0
> >
--
Ansuel
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error
2026-03-03 13:43 ` Christian Marangi
@ 2026-03-03 13:53 ` Daniel Golle
0 siblings, 0 replies; 9+ messages in thread
From: Daniel Golle @ 2026-03-03 13:53 UTC (permalink / raw)
To: Christian Marangi
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Quentin Schulz,
Harsha Vardhan V M, Neha Malcom Francis, Chen-Yu Tsai,
Jamie Gibbons, Justin Klaassen, Leo Yu-Chi Liang, Weijie Gao,
Marek Vasut, Lucien.Jheng, Sky Huang, Alif Zakuan Yuslaimi,
u-boot
On Tue, Mar 03, 2026 at 02:43:29PM +0100, Christian Marangi wrote:
> On Tue, Mar 03, 2026 at 01:38:03PM +0000, Daniel Golle wrote:
> > On Tue, Mar 03, 2026 at 02:29:08PM +0100, Christian Marangi wrote:
> > > When dev_get_priv errors out, the ubifs is not unbounted (if used)
> >
> > I don't understand why or where UBIFS is being used here.
> > Did you mean to say "UBI is not attached"?
> > UBI != UBIFS.
> >
>
> Function that is not called is umount_ubifs. That calls cmd_ubifs_umount.
> unbounted is a typo for unmounted.
Ok, so it *is* UBIFS, because you cannot mount UBI. You can only
"attach" an MTD partition to serve as UBI device (`ubi part ...` command
in U-Boot).
>
> Probably the confusion is present already in the FS loader driver where ubi
> needed to be used instead of ubifs?
If you are loading a file from a directory and rely on full POSIX-like
permissions, file ownership, ... then you need a proper filesystem, like
UBIFS, or SquashFS, ...
If all you need is to load raw data early at boot, using a static UBI
volume to hold the raw data is probably better (see below).
>
> There are mount_ubifs and umount_ubifs but they should have been mount_ubi and
> umount_ubi ? But then they use ubifs cmd OPs. I'm a bit confused here.
UBIFS is a read/write filesystem used on top of UBI.
UBI is a bit like a light version of LVM2, you can have multiple named
volumes, dynamically create them, rename or resize them.
Volumes can be static (ie. write-once, CRC32 over the whole volume
which is validated on access) or dynamic (no CRC validation, content
may change).
Inside dynamic volumes you may have either a filesystem or just raw
data. Any read-only filesystem can be used on top of UBI with the help
of ubiblock devices (eg. squashfs, erofs, ...).
In static volumes you typically store raw data, like it is the case
for U-Boot SPL loading U-Boot proper from UBI requires u-boot.bin
stored in a raw static volume, **not** inside a filesystem).
Imho using static volumes also makes a lot of sense for storing
those PHY or Ethernet firmware blobs, potentially even storing them
redundantly (ie. two static volumes, if one got broken CRC, move on
to the next one).
>
> > >
> > > Correctly handle this handle condition and while at it also return -EINVAL
> > > instead of -ENOMEM as a better error since no memory is allocated but is
> > > actually an invalid scenario for fw_get_filesystem_firmware().
> > >
> > > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > > ---
> > > drivers/misc/fs_loader.c | 6 ++++--
> > > 1 file changed, 4 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c
> > > index 2928cf75f89e..7e432a7ebd62 100644
> > > --- a/drivers/misc/fs_loader.c
> > > +++ b/drivers/misc/fs_loader.c
> > > @@ -178,8 +178,10 @@ static int fw_get_filesystem_firmware(struct udevice *dev)
> > >
> > > struct firmware *firmwarep = dev_get_priv(dev);
> > >
> > > - if (!firmwarep)
> > > - return -ENOMEM;
> > > + if (!firmwarep) {
> > > + ret = -EINVAL;
> > > + goto out;
> > > + }
> > >
> > > ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
> > > firmwarep->offset, firmwarep->size, &actread);
> > > --
> > > 2.51.0
> > >
>
> --
> Ansuel
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-03-03 13:53 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-03 13:29 [PATCH 0/5] misc: fs_loader: reorg and split to FS and FW loader + FIP loader Christian Marangi
2026-03-03 13:29 ` [PATCH 1/5] misc: fs_loader: fix ubifs not unmounted on dev_get_priv error Christian Marangi
2026-03-03 13:38 ` Daniel Golle
2026-03-03 13:43 ` Christian Marangi
2026-03-03 13:53 ` Daniel Golle
2026-03-03 13:29 ` [PATCH 2/5] misc: fs_loader: reorganize and split to FS and FW loader Christian Marangi
2026-03-03 13:29 ` [PATCH 3/5] misc: fw_loader: implement generic get_fw_loader_from_node() Christian Marangi
2026-03-03 13:29 ` [PATCH 4/5] misc: fw_loader: introduce FIP loader driver Christian Marangi
2026-03-03 13:29 ` [PATCH 5/5] misc: fw_loader: implement request_firmware_size() OP Christian Marangi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox