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 <lucienzx...@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+rene...@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