Hi Roger, On 2/2/2024 4:49 PM, Roger Quadros wrote: > > > On 30/01/2024 08:33, MD Danish Anwar wrote: >> Add APIs to set a firmware_name to a rproc and boot the rproc with the >> same firmware. >> >> Clients can call rproc_set_firmware() API to set firmware_name for a rproc >> whereas rproc_boot() will load the firmware set by rproc_set_firmware() to >> a buffer by calling request_firmware_into_buf(). rproc_boot() will then >> load the firmware file to the remote processor and start the remote >> processor. >> >> Also include "fs-loader.h" and make remoteproc driver select FS_LOADER in >> Kconfig so that we can call request_firmware_into_buf() from remoteproc >> driver. >> >> Signed-off-by: MD Danish Anwar <danishan...@ti.com> >> --- >> Changes from v3 to v4: >> *) No functional change. Splitted the patch out of the series as suggested >> by Nishant. >> *) Droppped the RFC tag. >> >> v3: https://lore.kernel.org/all/20240124064930.1787929-4-danishan...@ti.com/ >> >> drivers/remoteproc/Kconfig | 1 + >> drivers/remoteproc/rproc-uclass.c | 85 +++++++++++++++++++++++++++++++ >> include/remoteproc.h | 35 +++++++++++++ >> 3 files changed, 121 insertions(+) >> >> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig >> index 781de530af..0fdf1b38ea 100644 >> --- a/drivers/remoteproc/Kconfig >> +++ b/drivers/remoteproc/Kconfig >> @@ -10,6 +10,7 @@ menu "Remote Processor drivers" >> # All users should depend on DM >> config REMOTEPROC >> bool >> + select FS_LOADER >> depends on DM >> >> # Please keep the configuration alphabetically sorted. >> diff --git a/drivers/remoteproc/rproc-uclass.c >> b/drivers/remoteproc/rproc-uclass.c >> index 28b362c887..76db4157f7 100644 >> --- a/drivers/remoteproc/rproc-uclass.c >> +++ b/drivers/remoteproc/rproc-uclass.c >> @@ -13,6 +13,7 @@ >> #include <log.h> >> #include <malloc.h> >> #include <virtio_ring.h> >> +#include <fs_loader.h> >> #include <remoteproc.h> >> #include <asm/io.h> >> #include <dm/device-internal.h> >> @@ -961,3 +962,87 @@ unsigned long rproc_parse_resource_table(struct udevice >> *dev, struct rproc *cfg) >> >> return 1; >> } >> + >> +int rproc_set_firmware(struct udevice *rproc_dev, const char *fw_name) >> +{ >> + struct dm_rproc_uclass_pdata *uc_pdata; >> + int len; >> + char *p; >> + >> + if (!rproc_dev || !fw_name) >> + return -EINVAL; >> + >> + uc_pdata = dev_get_uclass_plat(rproc_dev); > > This can return NULL and you shuould error out if it does. >
Sure. >> + >> + len = strcspn(fw_name, "\n"); >> + if (!len) { >> + debug("can't provide empty string for firmware name\n"); > > how about "invalid filename" ? > Sure. >> + return -EINVAL; >> + } >> + >> + p = strndup(fw_name, len); >> + if (!p) >> + return -ENOMEM; >> + >> + uc_pdata->fw_name = p; >> + >> + return 0; >> +} >> + >> +int rproc_boot(struct udevice *rproc_dev, size_t fw_size) >> +{ >> + struct dm_rproc_uclass_pdata *uc_pdata; >> + struct udevice *fs_loader; >> + void *addr = malloc(fw_size); > > I will suggest to do malloc just before you need the buffer. > You need to free up the buffer an all return paths after that. > That is correct. I will do malloc just before calling request_firmware_into_buf() API. >> + int core_id, ret = 0; >> + char *firmware; >> + ulong rproc_addr; > > do you really need rproc_addr? You could use addr itself. > Sure. >> + >> + if (!rproc_dev) >> + return -EINVAL; >> + >> + if (!addr) >> + return -ENOMEM; >> + >> + uc_pdata = dev_get_uclass_plat(rproc_dev); >> + core_id = dev_seq(rproc_dev); >> + firmware = uc_pdata->fw_name; >> + >> + if (!firmware) { >> + debug("No firmware set for rproc core %d\n", core_id); >> + return -EINVAL; >> + } >> + >> + /* Initialize all rproc cores */ >> + rproc_init(); > > if (!rproc_is_initialized()) { > ret = rproc_init() > if (ret) { > debug("rproc_init() failed: %d\n", ret); > return ret; > } > } > Sure. >> + >> + /* Loading firmware to a given address */ >> + ret = get_fs_loader(&fs_loader); >> + if (ret) { >> + debug("could not get fs loader: %d\n", ret); >> + return ret; >> + } >> + >> + ret = request_firmware_into_buf(fs_loader, firmware, addr, fw_size, 0); >> + if (ret < 0) { >> + debug("could not request %s: %d\n", firmware, ret); >> + return ret; >> + } >> + >> + rproc_addr = (ulong)addr; >> + >> + ret = rproc_load(core_id, rproc_addr, ret); > > ret = rproc_load(coare_id, (ulong)addr, ret); > >> + if (ret) { >> + debug("failed to load %s to rproc core %d from addr 0x%08lX err >> %d\n", >> + uc_pdata->fw_name, core_id, rproc_addr, ret); >> + return ret; >> + } >> + >> + ret = rproc_start(core_id); >> + if (ret) { >> + debug("failed to start rproc core %d\n", core_id); >> + return ret; >> + } >> + >> + return ret; > > return 0; > >> +} >> diff --git a/include/remoteproc.h b/include/remoteproc.h >> index 91a88791a4..e53f85ba51 100644 >> --- a/include/remoteproc.h >> +++ b/include/remoteproc.h >> @@ -403,6 +403,7 @@ enum rproc_mem_type { >> * @name: Platform-specific way of naming the Remote proc >> * @mem_type: one of 'enum rproc_mem_type' >> * @driver_plat_data: driver specific platform data that may be needed. >> + * @fw_name: firmware name >> * >> * This can be accessed with dev_get_uclass_plat() for any UCLASS_REMOTEPROC >> * device. >> @@ -412,6 +413,7 @@ struct dm_rproc_uclass_pdata { >> const char *name; >> enum rproc_mem_type mem_type; >> void *driver_plat_data; >> + char *fw_name; >> }; >> >> /** >> @@ -705,6 +707,35 @@ unsigned long rproc_parse_resource_table(struct udevice >> *dev, >> struct resource_table *rproc_find_resource_table(struct udevice *dev, >> unsigned int addr, >> int *tablesz); >> +/** >> + * rproc_set_firmware() - assign a new firmware > > firmware/firmware name. > >> + * @rproc_dev: device for wich new firmware is being assigned > > firmware/firmware name > wich/which > >> + * @fw_name: new firmware name to be assigned >> + * >> + * This function allows remoteproc drivers or clients to configure a custom >> + * firmware name. The function does not trigger a remote processor boot, >> + * only sets the firmware name used for a subsequent boot. >> + * >> + * This function sets the fw_name field in uclass pdata of the Remote proc >> + * >> + * Return: 0 on success or a negative value upon failure >> + */ >> +int rproc_set_firmware(struct udevice *rproc_dev, const char *fw_name); >> + >> +/** >> + * rproc_boot() - boot a remote processor >> + * @rproc_dev: rproc device to boot >> + * @fw_size: Size of the memory to allocate for firmeware > > firmeware/firmware > I'll fix all these typos. > How does caller know what firmware size to set to? > This should already be private to the rproc as it knows > how large is its program memory. > Caller is trying to boot the rproc with a firmware binary. Caller should know the size of binary that it wants to load to rproc core. Caller will specify the binary size to rproc_boot(). Based on the size provided by caller, rproc_boot() will then allocate that much memory and call request_firmware_into_buf() with the size and allocated buffer. If the caller doesn't provide minimum size rproc_load() will fail. rproc_load() calls respective driver ops, for example: pru_load(). pru_load() [1] API checks the required size of firmware to load by casting the buffer into Elf32_Ehdr and Elf32_Phdr and returns error if size provided by caller is less than this. if (offset + filesz > size) { dev_dbg(dev, "truncated fw: need 0x%x avail 0x%zx\n", offset + filesz, size); ret = -EINVAL; break; } >> + * >> + * Boot a remote processor (i.e. load its firmware, power it on, ...). >> + * >> + * This function first loads the firmware set in the uclass pdata of Remote >> + * processor to a buffer and then loads firmware to the remote processor >> + * using rproc_load(). >> + * >> + * Return: 0 on success, and an appropriate error value otherwise >> + */ >> +int rproc_boot(struct udevice *rproc_dev, size_t fw_size); > > Was wondering if you need separate API for rproc_set_firmware or we can just > pass firmware name as argument to rproc_boot()? > Technically we can. But when we discussed this approach first in v1, you had asked to keep the APIs similar to upstream linux. Upstream linux has these two APIs so I kept it that way. If you want I can drop the first API. Please let me know. >> #else >> static inline int rproc_init(void) { return -ENOSYS; } >> static inline int rproc_dev_init(int id) { return -ENOSYS; } >> @@ -744,6 +775,10 @@ static inline int rproc_elf_load_rsc_table(struct >> udevice *dev, ulong fw_addr, >> ulong fw_size, ulong *rsc_addr, >> ulong *rsc_size) >> { return -ENOSYS; } >> +static inline int rproc_set_firmware(struct udevice *rproc_dev, const char >> *fw_name) >> +{ return -ENOSYS; } >> +static inline int rproc_boot(struct udevice *rproc_dev, size_t fw_size) >> +{ return -ENOSYS; } >> #endif >> >> #endif /* _RPROC_H_ */ > [1] https://elixir.bootlin.com/u-boot/latest/source/drivers/remoteproc/pru_rproc.c#L324 -- Thanks and Regards, Md Danish Anwar