[U-Boot, v2, 1/1] misc: fs_loader: Add request_firmware_into_buf_via_script() for flexible firmware loading via U-Boot script
Lucien.Jheng
lucienzx159 at gmail.com
Sun Jul 27 16:16:09 CEST 2025
This commit introduces a new API, request_firmware_into_buf_via_script(),
to the fs_loader framework.
This function allows firmware to be loaded into memory
using a user-defined U-Boot script,
providing greater flexibility for firmware loading scenarios that cannot be handled
by static file paths or device/partition selection alone.
Key features:
- The API runs a specified U-Boot script (by name), which is responsible
for loading the firmware into memory by any means (e.g., load from MMC, USB, network, etc.).
- The script must set two environment variables: 'fw_addr'
(the memory address where the firmware is loaded) and
'fw_size' (the size of the firmware in bytes).
- The function validates these variables, copies the firmware into a newly
allocated buffer (using memdup), and returns the pointer
via the provided double pointer argument.
- The maximum allowed firmware size is checked to prevent buffer overflows.
- The environment variables are cleared after use to avoid stale data.
- Detailed error messages are provided for all failure conditions to aid debugging.
Usage example:
1. Define a U-Boot script in the environment that loads the firmware
and sets the required variables:
=> env set my_fw_script 'load mmc 0:1 ${loadaddr} firmware.bin && env set fw_addr ${loadaddr} && env set fw_size ${filesize}'
2. In your code, call the new API:
void *fw_buf;
int ret = request_firmware_into_buf_via_script(&fw_buf, 0x24000, "my_fw_script");
if (ret < 0)
return ret;
This approach allows board integrators and users to customize the firmware
loading process without modifying the source code,
simply by changing the script in the U-Boot environment.
Signed-off-by: Lucien.Jheng <lucienzx159 at gmail.com>
---
Since the Airoha EN8811H requires MD32 firmware to be loaded via MDIO,
and U-Boot currently supports various FW loading methods (e.g., partition or rootfs for MMC),
I've followed Marek Vasut's advice [1] to implement this via U-Boot scripts.
[1] https://patchwork.ozlabs.org/project/uboot/patch/20250617081641.8385-5-marek.vasut+renesas@mailbox.org/
This commit expands the existing `fs_loader` concept by adding U-Boot script functionality.
This leverages U-Boot environment variables, providing significant flexibility
for FW loading without requiring CONFIG_* modifications or recompilations.
I've validated this API on the Banana Pi R3 Mini. Here are my test commands and logs:
1. `env set en8811h_load_firmware 'env set fw_addr 0x46000000 && env set fw_size 0x24000 && load mmc 0:3 0x46000000 /boot/EthMD32.dm.bin && load mmc 0:3 0x46004000 /boot/EthMD32.DSP.bin'`
````Test Log````
Net: 16384 bytes read in 1 ms (15.6 MiB/s)
131072 bytes read in 9 ms (13.9 MiB/s)
addr: 0x46000000, size: 0x24000
Found Airoha Firmware.
MD32 firmware version: 24112802
````End of Test Log````
2. `env set en8811h_load_firmware 'env set fw_addr 0x46000000 && env set fw_size 0x24000 && mmc partconf 0 1 2 2 && mmc read $fw_addr 0x0 0x120 && mmc partconf 0 1 1 0'`
````Test Log````
Net: MMC read: dev # 0, block # 0, count 288 ... 288 blocks read: OK
addr: 0x46000000, size: 0x24000
Found Airoha Firmware.
MD32 firmware version: 24112802
````End of Test Log````
Test Logs:
https://gist.github.com/bjxlucX123/15aa5f92493e1363ec81fd5e5b83ee6d
drivers/misc/fs_loader.c | 72 ++++++++++++++++++++++++++++++++++++++++
include/fs_loader.h | 24 ++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c
index 66803f4b997..202ee7c7bfb 100644
--- a/drivers/misc/fs_loader.c
+++ b/drivers/misc/fs_loader.c
@@ -228,6 +228,78 @@ int request_firmware_into_buf(struct udevice *dev,
return ret;
}
+/**
+ * request_firmware_into_buf_via_script() -
+ * Load firmware using a U-Boot script and copy to buffer
+ * @buf: Pointer to a void* that will receive a pointer to the copied firmware buffer.
+ * @max_size: Maximum allowed size for the firmware to be loaded.
+ * @script_name: Name of the U-Boot script to execute for firmware loading.
+ *
+ * Executes a U-Boot script (@script_name) that loads firmware into
+ * memory and sets the environment variables 'fw_addr' (address) and
+ * 'fw_size' (size in bytes). Allocates a buffer, copies the firmware
+ * from the given address, and returns the pointer via @buf.
+ *
+ * The script must set these environment variables:
+ * fw_addr - Address where firmware is loaded in memory
+ * fw_size - Size of the firmware in bytes
+ *
+ * The script should be defined in the U-Boot environment, for example:
+ * env set script_name 'load mmc 0:1 ${loadaddr} firmware.bin &&
+ * env set fw_addr ${loadaddr} && env set fw_size ${filesize}
+ * Return: 0 on success, negative value on error.
+ */
+int request_firmware_into_buf_via_script(void **buf, size_t max_size,
+ const char *script_name)
+{
+ ulong addr, size;
+ int ret;
+ char cmd[32];
+
+ if (!buf || !script_name || !max_size)
+ return -EINVAL;
+
+ /* Create command to run the firmware loading script */
+ snprintf(cmd, sizeof(cmd), "run %s", script_name);
+
+ /* Run the firmware loading script */
+ ret = run_command_list(cmd, -1, 0);
+ if (ret) {
+ log_err("Firmware loading script '%s' not defined or failed.\n",
+ script_name);
+ return -EINVAL;
+ }
+
+ /* Find out where the firmware got loaded and how long it is */
+ addr = env_get_hex("fw_addr", 0);
+ size = env_get_hex("fw_size", 0);
+
+ /* Clear the variables set by the firmware loading script */
+ env_set("fw_addr", NULL);
+ env_set("fw_size", NULL);
+
+ if (!addr || !size) {
+ log_err("Firmware address (0x%lx) or size (0x%lx) are invalid.\n",
+ addr, size);
+ return -EINVAL;
+ }
+
+ /* Validate the firmware size against the buffer size */
+ if (size > max_size) {
+ log_err("Firmware size (0x%lx) exceeds buffer size (0x%zx).\n",
+ size, max_size);
+ return -EINVAL;
+ }
+
+ *buf = (void *)memdup((void *)addr, size);
+ if (!*buf) {
+ log_err("Failed to allocate memory for firmware copy.\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static int fs_loader_of_to_plat(struct udevice *dev)
{
u32 phandlepart[2];
diff --git a/include/fs_loader.h b/include/fs_loader.h
index 5eb5b7ab4a1..97ed4cc2af0 100644
--- a/include/fs_loader.h
+++ b/include/fs_loader.h
@@ -64,4 +64,28 @@ int request_firmware_into_buf(struct udevice *dev,
* Return: 0 on success, negative value on error
*/
int get_fs_loader(struct udevice **dev);
+
+/**
+ * request_firmware_into_buf_via_script() -
+ * Load firmware using a U-Boot script and copy to buffer
+ * @buf: Pointer to a void* that will receive a pointer to the copied firmware buffer.
+ * @max_size: Maximum allowed size for the firmware to be loaded.
+ * @script_name: Name of the U-Boot script to execute for firmware loading.
+ *
+ * Executes a U-Boot script (@script_name) that loads firmware into
+ * memory and sets the environment variables 'fw_addr' (address) and
+ * 'fw_size' (size in bytes). Allocates a buffer, copies the firmware
+ * from the given address, and returns the pointer via @buf.
+ *
+ * The script must set these environment variables:
+ * fw_addr - Address where firmware is loaded in memory
+ * fw_size - Size of the firmware in bytes
+ *
+ * The script should be defined in the U-Boot environment, for example:
+ * env set script_name 'load mmc 0:1 ${loadaddr} firmware.bin &&
+ * env set fw_addr ${loadaddr} && env set fw_size ${filesize}
+ * Return: 0 on success, negative value on error.
+ */
+int request_firmware_into_buf_via_script(void **buf, size_t max_size,
+ const char *script_name);
#endif
--
2.34.1
More information about the U-Boot
mailing list