From df433338a3cd88c61d07135bd8504eb23b7e8cc8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 30 Jan 2025 12:30:53 +0100 Subject: [PATCH 01/11] Update Zephyr Update Zephyr to fetch commits: 037cb87276bb ("llext: export spinlock debugging symbols to extensions") a2a587ae20b4 ("llext: fix symbol exporting for ET_REL") and the new LLEXT inspection API. Signed-off-by: Guennadi Liakhovetski --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 5e57d91b742b..7be1028f0985 100644 --- a/west.yml +++ b/west.yml @@ -43,7 +43,7 @@ manifest: - name: zephyr repo-path: zephyr - revision: aaa119d75729410ae70fc8ba188634c23214f804 + revision: fe29c40a9366b5ffdcdd2eac26023ce4502413b1 remote: zephyrproject # Import some projects listed in zephyr/west.yml@revision From 75c21e11f27401bdd4385a1a2939d613ab47b4f8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Jul 2024 12:51:39 +0200 Subject: [PATCH 02/11] llext: add auxiliary library type Add a dedicated type for auxiliary LLEXT objects, not implementing the Module Adapter API. Signed-off-by: Guennadi Liakhovetski --- src/include/module/module/llext.h | 12 ++++++++++++ src/include/sof/llext_manager.h | 3 ++- tools/rimage/src/include/rimage/sof/user/manifest.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/include/module/module/llext.h b/src/include/module/module/llext.h index e1d294694280..7b5d00feeb30 100644 --- a/src/include/module/module/llext.h +++ b/src/include/module/module/llext.h @@ -21,6 +21,18 @@ } \ } +#define SOF_LLEXT_AUX_MANIFEST(manifest_name, entry, mod_uuid) \ +{ \ + .module = { \ + .name = manifest_name, \ + .uuid = mod_uuid, \ + .entry_point = (uint32_t)(entry), \ + .type = { \ + .load_type = SOF_MAN_MOD_TYPE_LLEXT_AUX, \ + }, \ + } \ +} + #define SOF_LLEXT_MOD_ENTRY(name, interface) \ static const struct module_interface *name##_llext_entry(void *mod_cfg, \ void *parent_ppl, void **mod_ptr) \ diff --git a/src/include/sof/llext_manager.h b/src/include/sof/llext_manager.h index c963ef4adfd6..3192fdf7eeea 100644 --- a/src/include/sof/llext_manager.h +++ b/src/include/sof/llext_manager.h @@ -18,7 +18,8 @@ struct comp_ipc_config; static inline bool module_is_llext(const struct sof_man_module *mod) { - return mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT; + return mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT || + mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX; } uintptr_t llext_manager_allocate_module(struct processing_module *proc, diff --git a/tools/rimage/src/include/rimage/sof/user/manifest.h b/tools/rimage/src/include/rimage/sof/user/manifest.h index c206fe7a5055..773ed11f6433 100644 --- a/tools/rimage/src/include/rimage/sof/user/manifest.h +++ b/tools/rimage/src/include/rimage/sof/user/manifest.h @@ -26,6 +26,7 @@ #define SOF_MAN_MOD_TYPE_BUILTIN 0 #define SOF_MAN_MOD_TYPE_MODULE 1 #define SOF_MAN_MOD_TYPE_LLEXT 2 /* Zephyr LLEXT-style dynamically linked */ +#define SOF_MAN_MOD_TYPE_LLEXT_AUX 3 /* Zephyr LLEXT-style dynamically linked auxiliary */ /* module init config */ #define SOF_MAN_MOD_INIT_CONFIG_BASE_CFG 0 /* Base config only */ From f1763d41889018e10f7e721a8a7086e6e96bb6db Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 16 Jul 2024 12:33:09 +0200 Subject: [PATCH 03/11] llext: move the llext pointer to struct lib_manager_module The LLEXT context is currently bound to the Module Adapter API which shouldn't be the case because we also need generic LLEXT-based loadable SOF objects. This patch moves the context to struct lib_manager_module which is safe, because the library manager is always needed when dynamically loading modules. Signed-off-by: Guennadi Liakhovetski --- src/audio/module_adapter/module/modules.c | 2 +- src/include/module/module/base.h | 3 --- src/include/sof/lib_manager.h | 6 +++-- src/include/sof/llext_manager.h | 9 ++++---- src/library_manager/lib_manager.c | 28 ++++++++--------------- src/library_manager/llext_manager.c | 20 +++++++++++----- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/audio/module_adapter/module/modules.c b/src/audio/module_adapter/module/modules.c index 690f111fb0bc..68050089f037 100644 --- a/src/audio/module_adapter/module/modules.c +++ b/src/audio/module_adapter/module/modules.c @@ -62,7 +62,7 @@ static int modules_init(struct processing_module *mod) void *adapter; int ret; - uintptr_t module_entry_point = lib_manager_allocate_module(mod, config, src_cfg); + uintptr_t module_entry_point = lib_manager_allocate_module(config, src_cfg); if (module_entry_point == 0) { comp_err(dev, "modules_init(), lib_manager_allocate_module() failed!"); diff --git a/src/include/module/module/base.h b/src/include/module/module/base.h index d3f912957791..83b165f176f2 100644 --- a/src/include/module/module/base.h +++ b/src/include/module/module/base.h @@ -37,8 +37,6 @@ struct module_config { #endif }; -struct llext; - /* * A structure containing a module's private data, intended for its exclusive use. * @@ -60,7 +58,6 @@ struct module_data { void *runtime_params; struct module_memory memory; /**< memory allocated by module */ struct module_processing_data mpd; /**< shared data comp <-> module */ - struct llext *llext; /**< Zephyr loadable extension context */ #endif /* SOF_MODULE_PRIVATE */ }; diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 96915cb639e8..dc3bd0a0808c 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -95,10 +95,13 @@ struct lib_manager_segment_desc { size_t size; }; +struct llext; + struct lib_manager_module { unsigned int start_idx; /* Index of the first driver from this module in * the library-global driver list */ const struct sof_man_module_manifest *mod_manifest; + struct llext *llext; /* Zephyr loadable extension context */ struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS]; }; @@ -189,8 +192,7 @@ struct processing_module; * Function is responsible to allocate module in available free memory and assigning proper address. * (WIP) These feature will contain module validation and proper memory management. */ -uintptr_t lib_manager_allocate_module(struct processing_module *proc, - const struct comp_ipc_config *ipc_config, +uintptr_t lib_manager_allocate_module(const struct comp_ipc_config *ipc_config, const void *ipc_specific_config); /* diff --git a/src/include/sof/llext_manager.h b/src/include/sof/llext_manager.h index 3192fdf7eeea..3e74bde8cefd 100644 --- a/src/include/sof/llext_manager.h +++ b/src/include/sof/llext_manager.h @@ -22,18 +22,19 @@ static inline bool module_is_llext(const struct sof_man_module *mod) mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX; } -uintptr_t llext_manager_allocate_module(struct processing_module *proc, - const struct comp_ipc_config *ipc_config, +uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config, const void *ipc_specific_config); int llext_manager_free_module(const uint32_t component_id); +int llext_manager_unload(uint32_t module_id); + bool comp_is_llext(struct comp_dev *comp); #else #define module_is_llext(mod) false -#define llext_manager_allocate_module(proc, ipc_config, ipc_specific_config) 0 +#define llext_manager_allocate_module(ipc_config, ipc_specific_config) 0 #define llext_manager_free_module(component_id) 0 -#define llext_unload(ext) 0 +#define llext_manager_unload(module_id) 0 #define comp_is_llext(comp) false #endif diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index c208fde81988..a7dabb2a6fd7 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -338,8 +338,7 @@ static int lib_manager_free_module_instance(uint32_t module_id, uint32_t instanc return sys_mm_drv_unmap_region((__sparse_force void *)va_base, bss_size); } -uintptr_t lib_manager_allocate_module(struct processing_module *proc, - const struct comp_ipc_config *ipc_config, +uintptr_t lib_manager_allocate_module(const struct comp_ipc_config *ipc_config, const void *ipc_specific_config) { const struct sof_man_module *mod; @@ -356,7 +355,7 @@ uintptr_t lib_manager_allocate_module(struct processing_module *proc, } if (module_is_llext(mod)) - return llext_manager_allocate_module(proc, ipc_config, ipc_specific_config); + return llext_manager_allocate_module(ipc_config, ipc_specific_config); ret = lib_manager_load_module(module_id, mod); if (ret < 0) @@ -419,8 +418,7 @@ int lib_manager_free_module(const uint32_t component_id) #define PAGE_SZ 4096 /* equals to MAN_PAGE_SIZE used by rimage */ -uintptr_t lib_manager_allocate_module(struct processing_module *proc, - const struct comp_ipc_config *ipc_config, +uintptr_t lib_manager_allocate_module(const struct comp_ipc_config *ipc_config, const void *ipc_specific_config, const void **buildinfo) { tr_err(&lib_manager_tr, "Dynamic module allocation is not supported"); @@ -510,12 +508,10 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, * Variable used by llext_manager to temporary store llext handle before creation * a instance of processing_module. */ - struct processing_module tmp_proc; struct comp_dev *dev; /* At this point module resources are allocated and it is moved to L2 memory. */ - tmp_proc.priv.llext = NULL; - const uint32_t module_entry_point = lib_manager_allocate_module(&tmp_proc, config, + const uint32_t module_entry_point = lib_manager_allocate_module(config, args->data); if (!module_entry_point) { @@ -540,30 +536,26 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, } dev = module_adapter_new(drv, config, spec); - if (dev) { - struct processing_module *mod = comp_mod(dev); - - mod->priv.llext = tmp_proc.priv.llext; - } else { + if (!dev) lib_manager_free_module(module_id); - } + return dev; } static void lib_manager_module_free(struct comp_dev *dev) { struct processing_module *mod = comp_mod(dev); - struct llext *llext = mod->priv.llext; const struct comp_ipc_config *const config = &mod->dev->ipc_config; - const uint32_t module_id = config->id; + const uint32_t module_id = IPC4_MOD_ID(config->id); + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); int ret; /* This call invalidates dev, mod and config pointers! */ module_adapter_free(dev); - if (!llext || !llext_unload(&llext)) { + if (!ctx->mod || !llext_manager_unload(module_id)) { /* Free module resources allocated in L2 memory. */ - ret = lib_manager_free_module(module_id); + ret = lib_manager_free_module(config->id); if (ret < 0) comp_err(dev, "lib_manager_free_module() failed!"); } diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index c2cd5254ed76..51a4bd57fbd2 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -313,6 +313,7 @@ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != offs) { offs = mod_array[i].segment[LIB_MANAGER_TEXT].file_offset; ctx->mod[n_mod].segment[LIB_MANAGER_TEXT].size = 0; + ctx->mod[n_mod].llext = NULL; ctx->mod[n_mod++].start_idx = i; } @@ -330,8 +331,7 @@ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx return i - 1; } -uintptr_t llext_manager_allocate_module(struct processing_module *proc, - const struct comp_ipc_config *ipc_config, +uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config, const void *ipc_specific_config) { uint32_t module_id = IPC4_MOD_ID(ipc_config->id); @@ -349,7 +349,6 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, size_t mod_offset = mod_array[entry_index].segment[LIB_MANAGER_TEXT].file_offset; const struct sof_man_module_manifest *mod_manifest; const struct sof_module_api_build_info *buildinfo; - struct module_data *md = &proc->priv; size_t mod_size; int i, inst_idx; int ret; @@ -408,8 +407,8 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, struct llext_buf_loader ebl = LLEXT_BUF_LOADER((uint8_t *)dram_base + mod_offset, mod_size); /* LLEXT linking is only needed once for all the drivers in each module */ - ret = llext_manager_link(&ebl, mod_array[entry_index - inst_idx].name, mctx, &md->llext, - (const void **)&buildinfo, &mod_manifest); + ret = llext_manager_link(&ebl, mod_array[entry_index - inst_idx].name, mctx, + &mctx->llext, (const void **)&buildinfo, &mod_manifest); if (ret < 0) { tr_err(&lib_manager_tr, "linking failed: %d", ret); return 0; @@ -424,7 +423,7 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, } /* Map executable code and data */ - ret = llext_manager_load_module(md->llext, &ebl, mctx); + ret = llext_manager_load_module(mctx->llext, &ebl, mctx); if (ret < 0) return 0; @@ -442,6 +441,15 @@ uintptr_t llext_manager_allocate_module(struct processing_module *proc, return mctx->mod_manifest[inst_idx].module.entry_point; } +int llext_manager_unload(uint32_t module_id) +{ + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); + uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + + return llext_unload(&ctx->mod[mod_idx].llext); +} + int llext_manager_free_module(const uint32_t component_id) { const uint32_t module_id = IPC4_MOD_ID(component_id); From 068d6323ea8ef793cf9643852b26467b7b686e0d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 17 Jan 2025 17:06:08 +0100 Subject: [PATCH 04/11] llext: extract linking of a single module into a function Extract localisation and linking of a single LLEXT module into a separate function, it will be re-used for auxiliary modules. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 122 ++++++++++++++++++---------- 1 file changed, 80 insertions(+), 42 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 51a4bd57fbd2..d98904b51858 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -230,10 +230,10 @@ static bool llext_manager_section_detached(const elf_shdr_t *shdr) } static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, - struct lib_manager_module *mctx, struct llext **llext, - const void **buildinfo, + struct lib_manager_module *mctx, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) { + struct llext **llext = &mctx->llext; /* Identify if this is the first time loading this module */ struct llext_load_param ldr_parm = { .relocate_local = !mctx->segment[LIB_MANAGER_TEXT].size, @@ -283,6 +283,7 @@ static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, return binfo_o >= 0 && mod_o >= 0 ? 0 : -EPROTO; } +/* Count "module files" in the library, allocate and initialize memory for their descriptors */ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, const struct sof_man_fw_desc *desc) { @@ -320,6 +321,7 @@ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, return 0; } +/* Find a module context, containing the driver with the supplied index */ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx, unsigned int idx) { unsigned int i; @@ -331,29 +333,18 @@ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx return i - 1; } -uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config, - const void *ipc_specific_config) +static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw_desc *desc, + struct lib_manager_mod_ctx *ctx, struct llext_buf_loader *ebl, + const void **buildinfo, + const struct sof_man_module_manifest **mod_manifest) { - uint32_t module_id = IPC4_MOD_ID(ipc_config->id); - struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *)lib_manager_get_library_manifest(module_id); - struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); - - if (!ctx || !desc) { - tr_err(&lib_manager_tr, "failed to get module descriptor"); - return 0; - } - - struct sof_man_module *mod_array = (struct sof_man_module *)((char *)desc + + struct sof_man_module *mod_array = (struct sof_man_module *)((uint8_t *)desc + SOF_MAN_MODULE_OFFSET(0)); uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); size_t mod_offset = mod_array[entry_index].segment[LIB_MANAGER_TEXT].file_offset; - const struct sof_man_module_manifest *mod_manifest; - const struct sof_module_api_build_info *buildinfo; - size_t mod_size; - int i, inst_idx; int ret; - tr_dbg(&lib_manager_tr, "mod_id: %#x", ipc_config->id); + tr_dbg(&lib_manager_tr, "mod_id: %u", module_id); if (!ctx->mod) llext_manager_mod_init(ctx, desc); @@ -361,11 +352,13 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config if (entry_index >= desc->header.num_module_entries) { tr_err(&lib_manager_tr, "Invalid driver index %u exceeds %d", entry_index, desc->header.num_module_entries - 1); - return 0; + return -EINVAL; } - unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); - struct lib_manager_module *mctx = ctx->mod + mod_idx; + unsigned int mod_ctx_idx = llext_manager_mod_find(ctx, entry_index); + struct lib_manager_module *mctx = ctx->mod + mod_ctx_idx; + size_t mod_size; + int i, inst_idx; /* * We don't know the number of ELF files that this library is built of. @@ -381,9 +374,9 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config * we need to find the matching manifest in ".module" because only it * contains the entry point. For safety we calculate the ELF driver * index and then also check the driver name. - * We also need the driver size. For this we search the manifest array - * for the next ELF file, then the difference between offsets gives us - * the driver size. + * We also need a module size. For this we search the manifest array for + * the next ELF file, then the difference between offsets gives us the + * module size. */ for (i = entry_index - 1; i >= 0; i--) if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != mod_offset) @@ -404,17 +397,71 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config PAGE_SZ); uintptr_t dram_base = (uintptr_t)desc - SOF_MAN_ELF_TEXT_OFFSET; - struct llext_buf_loader ebl = LLEXT_BUF_LOADER((uint8_t *)dram_base + mod_offset, mod_size); - /* LLEXT linking is only needed once for all the drivers in each module */ - ret = llext_manager_link(&ebl, mod_array[entry_index - inst_idx].name, mctx, - &mctx->llext, (const void **)&buildinfo, &mod_manifest); + *ebl = (struct llext_buf_loader)LLEXT_BUF_LOADER((uint8_t *)dram_base + mod_offset, + mod_size); + + /* + * LLEXT linking is only needed once for all the "drivers" in the + * module. This calls llext_load(), which also takes references to any + * dependencies, sets up sections and retrieves buildinfo and + * mod_manifest + */ + ret = llext_manager_link(ebl, mod_array[entry_index - inst_idx].name, mctx, + buildinfo, mod_manifest); if (ret < 0) { tr_err(&lib_manager_tr, "linking failed: %d", ret); + return ret; + } + + /* if ret > 0, then the "driver" is already loaded */ + if (!ret) + /* mctx->mod_manifest points to a const array of module manifests */ + mctx->mod_manifest = *mod_manifest; + + /* Return the manifest, related to the specific instance */ + *mod_manifest = mctx->mod_manifest + inst_idx; + + if (strncmp(mod_array[entry_index].name, (*mod_manifest)->module.name, + sizeof(mod_array[0].name))) { + tr_err(&lib_manager_tr, "Name mismatch %s vs. %s", + mod_array[entry_index].name, (*mod_manifest)->module.name); + return -ENOEXEC; + } + + return mod_ctx_idx; +} + +uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config, + const void *ipc_specific_config) +{ + uint32_t module_id = IPC4_MOD_ID(ipc_config->id); + /* Library manifest */ + struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *) + lib_manager_get_library_manifest(module_id); + /* Library context */ + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); + + if (!ctx || !desc) { + tr_err(&lib_manager_tr, "failed to get module descriptor"); return 0; } - if (!ret) { + /* Array of all "module drivers" (manifests) in the library */ + const struct sof_man_module_manifest *mod_manifest; + const struct sof_module_api_build_info *buildinfo = NULL; + struct llext_buf_loader ebl; + + /* "module file" index in the ctx->mod array */ + int mod_ctx_idx = llext_manager_link_single(module_id, desc, ctx, &ebl, + (const void **)&buildinfo, &mod_manifest); + + if (mod_ctx_idx < 0) + return 0; + + struct lib_manager_module *mctx = ctx->mod + mod_ctx_idx; + + if (buildinfo) { /* First instance: check that the module is native */ if (buildinfo->format != SOF_MODULE_API_BUILD_INFO_FORMAT || buildinfo->api_version_number.full != SOF_MODULE_API_CURRENT_VERSION) { @@ -423,22 +470,13 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config } /* Map executable code and data */ - ret = llext_manager_load_module(mctx->llext, &ebl, mctx); + int ret = llext_manager_load_module(mctx->llext, &ebl, mctx); + if (ret < 0) return 0; - - /* mctx->mod_manifest points to a const array of module manifests */ - mctx->mod_manifest = mod_manifest; - } - - if (strncmp(mod_array[entry_index].name, mctx->mod_manifest[inst_idx].module.name, - sizeof(mod_array[0].name))) { - tr_err(&lib_manager_tr, "Name mismatch %s vs. %s", - mod_array[entry_index].name, mctx->mod_manifest[inst_idx].module.name); - return 0; } - return mctx->mod_manifest[inst_idx].module.entry_point; + return mod_manifest->module.entry_point; } int llext_manager_unload(uint32_t module_id) From d76aadaf309749ef1ab7e29a2b07b93c634a16e8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 23 Aug 2024 11:52:17 +0200 Subject: [PATCH 05/11] llext: call llext_unload() internally Currently lib_manager_module_free() first checks, if an LLEXT module is being freed. If so, it first calls llext_unload() and then - if the use-count is reported as zero, calls llext_manager_free_module(). Simplify this process by just calling llext_manager_free_module() and letting it handle llext_unload() internally. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/llext_manager.h | 3 --- src/library_manager/lib_manager.c | 12 ++++-------- src/library_manager/llext_manager.c | 18 +++++++++--------- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/include/sof/llext_manager.h b/src/include/sof/llext_manager.h index 3e74bde8cefd..a865d2d824a0 100644 --- a/src/include/sof/llext_manager.h +++ b/src/include/sof/llext_manager.h @@ -27,14 +27,11 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config int llext_manager_free_module(const uint32_t component_id); -int llext_manager_unload(uint32_t module_id); - bool comp_is_llext(struct comp_dev *comp); #else #define module_is_llext(mod) false #define llext_manager_allocate_module(ipc_config, ipc_specific_config) 0 #define llext_manager_free_module(component_id) 0 -#define llext_manager_unload(module_id) 0 #define comp_is_llext(comp) false #endif diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index a7dabb2a6fd7..1ff8ab3efae8 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -546,19 +546,15 @@ static void lib_manager_module_free(struct comp_dev *dev) { struct processing_module *mod = comp_mod(dev); const struct comp_ipc_config *const config = &mod->dev->ipc_config; - const uint32_t module_id = IPC4_MOD_ID(config->id); - struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); int ret; /* This call invalidates dev, mod and config pointers! */ module_adapter_free(dev); - if (!ctx->mod || !llext_manager_unload(module_id)) { - /* Free module resources allocated in L2 memory. */ - ret = lib_manager_free_module(config->id); - if (ret < 0) - comp_err(dev, "lib_manager_free_module() failed!"); - } + /* Free module resources allocated in L2 memory. */ + ret = lib_manager_free_module(config->id); + if (ret < 0) + comp_err(dev, "lib_manager_free_module() failed!"); } static void lib_manager_prepare_module_adapter(struct comp_driver *drv, const struct sof_uuid *uuid) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index d98904b51858..fbdc96d40037 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -479,15 +479,6 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config return mod_manifest->module.entry_point; } -int llext_manager_unload(uint32_t module_id) -{ - struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); - uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); - unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); - - return llext_unload(&ctx->mod[mod_idx].llext); -} - int llext_manager_free_module(const uint32_t component_id) { const uint32_t module_id = IPC4_MOD_ID(component_id); @@ -501,9 +492,18 @@ int llext_manager_free_module(const uint32_t component_id) return -ENOENT; } + if (!ctx->mod) { + tr_err(&lib_manager_tr, "NULL module array: ID %#x ctx %p", component_id, ctx); + return -ENOENT; + } + unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); struct lib_manager_module *mctx = ctx->mod + mod_idx; + if (llext_unload(&mctx->llext)) + /* More users are active */ + return 0; + tr_dbg(&lib_manager_tr, "mod_id: %#x", component_id); return llext_manager_unload_module(mctx); From 40e6862148618ef93202af0430791382ce985019 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 17 Jan 2025 16:08:10 +0100 Subject: [PATCH 06/11] Revert "llext: remove LIB_MANAGER_BSS" This reverts commit cf31c9c5fa8ecc3389fbd41ae95adb75df000768. We do now need to store BSS information for auxiliary LLEXT management. When instantiating such a module for the second time, we don't have access to Zephyr LLEXT information any more, so we need to use our cached information for .bss allocation. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib_manager.h | 1 + src/library_manager/llext_manager.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index dc3bd0a0808c..64ed48ab3f38 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -87,6 +87,7 @@ enum { LIB_MANAGER_TEXT, LIB_MANAGER_DATA, LIB_MANAGER_RODATA, + LIB_MANAGER_BSS, LIB_MANAGER_N_SEGMENTS, }; diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index fbdc96d40037..22f9f4dde51a 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -136,8 +136,8 @@ static int llext_manager_load_module(const struct llext *ext, const struct llext /* .bss, should be within writable data above */ void __sparse_cache *bss_addr = (void __sparse_cache *) - ebl->loader.sects[LLEXT_MEM_BSS].sh_addr; - size_t bss_size = ebl->loader.sects[LLEXT_MEM_BSS].sh_size; + mctx->segment[LIB_MANAGER_BSS].addr; + size_t bss_size = mctx->segment[LIB_MANAGER_BSS].size; int ret; /* Check, that .bss is within .data */ @@ -270,6 +270,13 @@ static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, mctx->segment[LIB_MANAGER_DATA].addr, mctx->segment[LIB_MANAGER_DATA].size); + mctx->segment[LIB_MANAGER_BSS].addr = ebl->loader.sects[LLEXT_MEM_BSS].sh_addr; + mctx->segment[LIB_MANAGER_BSS].size = ebl->loader.sects[LLEXT_MEM_BSS].sh_size; + + tr_dbg(&lib_manager_tr, ".bss: start: %#lx size %#x", + mctx->segment[LIB_MANAGER_BSS].addr, + mctx->segment[LIB_MANAGER_BSS].size); + ssize_t binfo_o = llext_find_section(&ebl->loader, ".mod_buildinfo"); if (binfo_o >= 0) From cc42fc0eb72254ba9e4f806dbc759c5025e10d8d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 10:45:40 +0100 Subject: [PATCH 07/11] rimage: auxiliary modules don't have TOML configuration Auxiliary modules, implemented as LLEXT and used to provide functionality for other modules, don't have TOML configuration, make rimage skip that step for them. Signed-off-by: Guennadi Liakhovetski --- tools/rimage/src/manifest.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tools/rimage/src/manifest.c b/tools/rimage/src/manifest.c index f0c92014ce00..40a85df47ab6 100644 --- a/tools/rimage/src/manifest.c +++ b/tools/rimage/src/manifest.c @@ -536,18 +536,20 @@ static int man_module_create_reloc(struct image *image, struct manifest_module * unsigned int i; for (i = 0, sof_mod = section.data; i < n_mod; i++, sof_mod++) { - int j = man_module_find_cfg(modules, &sof_mod->module); - - if (j < 0) { - elf_section_free(§ion); - return j; - } - - /* Found a TOML manifest, matching ELF */ if (i) (*man_module)++; - /* Use manifest created using toml files as template */ - **man_module = modules->mod_man[j]; + + if (sof_mod->module.type.load_type != SOF_MAN_MOD_TYPE_LLEXT_AUX) { + int j = man_module_find_cfg(modules, &sof_mod->module); + + if (j < 0) { + elf_section_free(§ion); + return j; + } + + /* Found a TOML manifest, matching ELF: use as a template */ + **man_module = modules->mod_man[j]; + } /* Use .manifest to update individual fields */ man_get_section_manifest(image, sof_mod, *man_module); man_module_fill_reloc(module, *man_module); @@ -723,6 +725,9 @@ static int man_create_modules_in_config(struct image *image, struct sof_man_fw_d SOF_MAN_MODULE_OFFSET(0)); i < modules->output_mod_cfg_count; i++, man_module++) { + if (man_module->type.load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX) + continue; + int j = man_module_find_cfg(modules, man_module); if (j < 0) From ffacce80d07edbada1c7639902eaac64773063eb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 20 Jan 2025 11:55:44 +0100 Subject: [PATCH 08/11] llext: simplify llext_manager_load_module() prototype llext_manager_load_module()'s ebl argument is only used for .bss alignment. Calculate it automatically to eliminate ebl, because it isn't available during following loads. e.g. when reloading dependencies. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 22f9f4dde51a..89c162265f52 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -116,8 +116,7 @@ static int llext_manager_load_data_from_storage(const struct llext *ext, return ret; } -static int llext_manager_load_module(const struct llext *ext, const struct llext_buf_loader *ebl, - const struct lib_manager_module *mctx) +static int llext_manager_load_module(const struct lib_manager_module *mctx) { /* Executable code (.text) */ void __sparse_cache *va_base_text = (void __sparse_cache *) @@ -144,13 +143,15 @@ static int llext_manager_load_module(const struct llext *ext, const struct llext if (bss_size && ((uintptr_t)bss_addr + bss_size <= (uintptr_t)va_base_data || (uintptr_t)bss_addr >= (uintptr_t)va_base_data + data_size)) { + size_t bss_align = MIN(PAGE_SZ, BIT(__builtin_ctz((uintptr_t)bss_addr))); + if ((uintptr_t)bss_addr + bss_size == (uintptr_t)va_base_data && !((uintptr_t)bss_addr & (PAGE_SZ - 1))) { /* .bss directly in front of writable data and properly aligned, prepend */ va_base_data = bss_addr; data_size += bss_size; } else if ((uintptr_t)bss_addr == (uintptr_t)va_base_data + - ALIGN_UP(data_size, ebl->loader.sects[LLEXT_MEM_BSS].sh_addralign)) { + ALIGN_UP(data_size, bss_align)) { /* .bss directly behind writable data, append */ data_size += bss_size; } else { @@ -161,6 +162,8 @@ static int llext_manager_load_module(const struct llext *ext, const struct llext } } + const struct llext *ext = mctx->llext; + /* Copy Code */ ret = llext_manager_load_data_from_storage(ext, va_base_text, ext->mem[LLEXT_MEM_TEXT], text_size, SYS_MM_MEM_PERM_EXEC); @@ -341,8 +344,7 @@ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx } static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw_desc *desc, - struct lib_manager_mod_ctx *ctx, struct llext_buf_loader *ebl, - const void **buildinfo, + struct lib_manager_mod_ctx *ctx, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) { struct sof_man_module *mod_array = (struct sof_man_module *)((uint8_t *)desc + @@ -404,9 +406,7 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw PAGE_SZ); uintptr_t dram_base = (uintptr_t)desc - SOF_MAN_ELF_TEXT_OFFSET; - - *ebl = (struct llext_buf_loader)LLEXT_BUF_LOADER((uint8_t *)dram_base + mod_offset, - mod_size); + struct llext_buf_loader ebl = LLEXT_BUF_LOADER((uint8_t *)dram_base + mod_offset, mod_size); /* * LLEXT linking is only needed once for all the "drivers" in the @@ -414,7 +414,7 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw * dependencies, sets up sections and retrieves buildinfo and * mod_manifest */ - ret = llext_manager_link(ebl, mod_array[entry_index - inst_idx].name, mctx, + ret = llext_manager_link(&ebl, mod_array[entry_index - inst_idx].name, mctx, buildinfo, mod_manifest); if (ret < 0) { tr_err(&lib_manager_tr, "linking failed: %d", ret); @@ -457,10 +457,9 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config /* Array of all "module drivers" (manifests) in the library */ const struct sof_man_module_manifest *mod_manifest; const struct sof_module_api_build_info *buildinfo = NULL; - struct llext_buf_loader ebl; /* "module file" index in the ctx->mod array */ - int mod_ctx_idx = llext_manager_link_single(module_id, desc, ctx, &ebl, + int mod_ctx_idx = llext_manager_link_single(module_id, desc, ctx, (const void **)&buildinfo, &mod_manifest); if (mod_ctx_idx < 0) @@ -477,7 +476,7 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config } /* Map executable code and data */ - int ret = llext_manager_load_module(mctx->llext, &ebl, mctx); + int ret = llext_manager_load_module(mctx); if (ret < 0) return 0; From 294f1550d23a5ea3e92dd80aa1fb889a8b14d454 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 28 Jan 2025 13:33:47 +0200 Subject: [PATCH 09/11] llext: keep minimal Zephyr context when unloading modules Currently when module's last instance is destroyed, it's freed and its Zephyr context is destroyed too. At the same time the module is kept in DRAM in a linked and relocated state, which means, that next time when we have to use it, we need to tell Zephyr to instantiate it while skipping the linking step. Additionally this makes handling dependencies inconvenient: they are created during the linking step by Zephyr and therefore are lost when the module is released. Then, as described above, they're not recreated when the linking step is skipped during a subsequent load. To fix this problem this commit avoids destroying module's Zephyr context when freeing. This costs around 200 bytes but makes handling of dependencies possible. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib_manager.h | 1 + src/library_manager/llext_manager.c | 72 +++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 64ed48ab3f38..23364d83ba7e 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -103,6 +103,7 @@ struct lib_manager_module { * the library-global driver list */ const struct sof_man_module_manifest *mod_manifest; struct llext *llext; /* Zephyr loadable extension context */ + bool mapped; struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS]; }; diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 89c162265f52..ccdf109145d3 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -116,7 +117,7 @@ static int llext_manager_load_data_from_storage(const struct llext *ext, return ret; } -static int llext_manager_load_module(const struct lib_manager_module *mctx) +static int llext_manager_load_module(struct lib_manager_module *mctx) { /* Executable code (.text) */ void __sparse_cache *va_base_text = (void __sparse_cache *) @@ -183,6 +184,7 @@ static int llext_manager_load_module(const struct lib_manager_module *mctx) goto e_rodata; memset((__sparse_force void *)bss_addr, 0, bss_size); + mctx->mapped = true; return 0; @@ -194,7 +196,7 @@ static int llext_manager_load_module(const struct lib_manager_module *mctx) return ret; } -static int llext_manager_unload_module(const struct lib_manager_module *mctx) +static int llext_manager_unload_module(struct lib_manager_module *mctx) { /* Executable code (.text) */ void __sparse_cache *va_base_text = (void __sparse_cache *) @@ -224,6 +226,8 @@ static int llext_manager_unload_module(const struct lib_manager_module *mctx) if (ret < 0 && !err) err = ret; + mctx->mapped = false; + return err; } @@ -237,16 +241,35 @@ static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, const struct sof_man_module_manifest **mod_manifest) { struct llext **llext = &mctx->llext; - /* Identify if this is the first time loading this module */ - struct llext_load_param ldr_parm = { - .relocate_local = !mctx->segment[LIB_MANAGER_TEXT].size, - .pre_located = true, - .section_detached = llext_manager_section_detached, - }; - int ret = llext_load(&ebl->loader, name, llext, &ldr_parm); - - if (ret) - return ret; + int ret; + + if (*llext && !mctx->mapped) { + /* + * All module instances have been terminated, so we freed SRAM, + * but we kept the full Zephyr LLEXT context. Now a new instance + * is starting, so we just re-use all the configuration and only + * re-allocate SRAM and copy the module into it + */ + *mod_manifest = mctx->mod_manifest; + + return 0; + } + + if (!*llext || mctx->mapped) { + /* + * Either the very first time loading this module, or the module + * is already mapped, we just call llext_load() to refcount it + */ + struct llext_load_param ldr_parm = { + .relocate_local = !*llext, + .pre_located = true, + .section_detached = llext_manager_section_detached, + }; + + ret = llext_load(&ebl->loader, name, llext, &ldr_parm); + if (ret) + return ret; + } mctx->segment[LIB_MANAGER_TEXT].addr = ebl->loader.sects[LLEXT_MEM_TEXT].sh_addr; mctx->segment[LIB_MANAGER_TEXT].size = ebl->loader.sects[LLEXT_MEM_TEXT].sh_size; @@ -323,7 +346,7 @@ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, for (i = 0, n_mod = 0, offs = ~0; i < desc->header.num_module_entries; i++) if (mod_array[i].segment[LIB_MANAGER_TEXT].file_offset != offs) { offs = mod_array[i].segment[LIB_MANAGER_TEXT].file_offset; - ctx->mod[n_mod].segment[LIB_MANAGER_TEXT].size = 0; + ctx->mod[n_mod].mapped = false; ctx->mod[n_mod].llext = NULL; ctx->mod[n_mod++].start_idx = i; } @@ -474,7 +497,9 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config tr_err(&lib_manager_tr, "Unsupported module API version"); return 0; } + } + if (!mctx->mapped) { /* Map executable code and data */ int ret = llext_manager_load_module(mctx); @@ -506,12 +531,31 @@ int llext_manager_free_module(const uint32_t component_id) unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); struct lib_manager_module *mctx = ctx->mod + mod_idx; - if (llext_unload(&mctx->llext)) + /* Protected by IPC serialization */ + if (mctx->llext->use_count > 1) { + /* llext_unload() will return a positive number */ + int ret = llext_unload(&mctx->llext); + + if (ret <= 0) { + tr_err(&lib_manager_tr, + "mod_id: %#x: invalid return code from llext_unload(): %d", + component_id, ret); + return ret ? : -EPROTO; + } + /* More users are active */ return 0; + } + /* + * The last instance of the module has been destroyed and it can now be + * unloaded from SRAM + */ tr_dbg(&lib_manager_tr, "mod_id: %#x", component_id); + /* Since the LLEXT context now is preserved, we have to flush logs ourselves */ + log_flush(); + return llext_manager_unload_module(mctx); } From 1c67251457ba5bcff70bdafa607425b3432e3cc0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 30 Jan 2025 10:47:37 +0100 Subject: [PATCH 10/11] llext: add dependencies Add support for auxiliary modules, exporting symbols to other modules. In such cases Zephyr LLEXT API generates a dependency list, available while the dependent module is loaded. SOF now preserves the minimum module context even while it isn't used, that includes dependency lists, so on a repeated load they're still available. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/llext_manager.h | 3 + src/library_manager/lib_manager.c | 14 +++- src/library_manager/llext_manager.c | 123 ++++++++++++++++++++++++++-- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/src/include/sof/llext_manager.h b/src/include/sof/llext_manager.h index a865d2d824a0..7394e62725cf 100644 --- a/src/include/sof/llext_manager.h +++ b/src/include/sof/llext_manager.h @@ -27,11 +27,14 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config int llext_manager_free_module(const uint32_t component_id); +int llext_manager_add_library(uint32_t module_id); + bool comp_is_llext(struct comp_dev *comp); #else #define module_is_llext(mod) false #define llext_manager_allocate_module(ipc_config, ipc_specific_config) 0 #define llext_manager_free_module(component_id) 0 +#define llext_manager_add_library(module_id) 0 #define comp_is_llext(comp) false #endif diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index 1ff8ab3efae8..3546834481e3 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -1032,14 +1032,22 @@ int lib_manager_load_library(uint32_t dma_id, uint32_t lib_id, uint32_t type) rfree((__sparse_force void *)man_tmp_buffer); cleanup: -#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL - core_kcps_adjust(cpu_get_id(), -(CLK_MAX_CPU_HZ / 1000)); -#endif rfree((void *)dma_ext->dma_addr); lib_manager_dma_deinit(dma_ext, dma_id); rfree(dma_ext); _ext_lib->runtime_data = NULL; + uint32_t module_id = lib_id << LIB_MANAGER_LIB_ID_SHIFT; + const struct sof_man_module *mod = lib_manager_get_module_manifest(module_id); + + if (module_is_llext(mod) && !ret) + /* Auxiliary LLEXT libraries need to be linked upon loading */ + ret = llext_manager_add_library(module_id); + +#if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL + core_kcps_adjust(cpu_get_id(), -(CLK_MAX_CPU_HZ / 1000)); +#endif + if (!ret) tr_info(&ipc_tr, "loaded library id: %u", lib_id); diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index ccdf109145d3..5134f48ac012 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -378,9 +378,6 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw tr_dbg(&lib_manager_tr, "mod_id: %u", module_id); - if (!ctx->mod) - llext_manager_mod_init(ctx, desc); - if (entry_index >= desc->header.num_module_entries) { tr_err(&lib_manager_tr, "Invalid driver index %u exceeds %d", entry_index, desc->header.num_module_entries - 1); @@ -462,12 +459,41 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw return mod_ctx_idx; } +static int llext_lib_find(const struct llext *llext, struct lib_manager_module **dep_ctx) +{ + struct ext_library *_ext_lib = ext_lib_get(); + unsigned int i, j; + + if (!llext) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(_ext_lib->desc); i++) { + if (!_ext_lib->desc[i]) + continue; + + for (j = 0; j < _ext_lib->desc[i]->n_mod; j++) + if (_ext_lib->desc[i]->mod[j].llext == llext) { + *dep_ctx = _ext_lib->desc[i]->mod + j; + return i; + } + } + + return -ENOENT; +} + +static void llext_depend_unlink(struct lib_manager_module *dep_ctx[], int n) +{ + for (; n >= 0; n--) + if (dep_ctx[n] && dep_ctx[n]->llext->use_count == 1) + llext_manager_unload_module(dep_ctx[n]); +} + uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config, const void *ipc_specific_config) { uint32_t module_id = IPC4_MOD_ID(ipc_config->id); /* Library manifest */ - struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *) + const struct sof_man_fw_desc *desc = (struct sof_man_fw_desc *) lib_manager_get_library_manifest(module_id); /* Library context */ struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); @@ -500,9 +526,50 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config } if (!mctx->mapped) { - /* Map executable code and data */ - int ret = llext_manager_load_module(mctx); + int i, ret; + /* + * Check if any dependencies need to be mapped - collect + * pointers to library contexts + */ + struct lib_manager_module *dep_ctx[LLEXT_MAX_DEPENDENCIES] = {}; + + for (i = 0; i < ARRAY_SIZE(mctx->llext->dependency); i++) { + /* Dependencies are filled from the beginning of the array upwards */ + if (!mctx->llext->dependency[i]) + break; + + /* + * Protected by the IPC serialization, but maybe we should protect the + * use-count explicitly too. Currently the use-count is first incremented + * when an auxiliary library is loaded, it was then additionally incremented + * when the current dependent module was mapped. If it's higher than two, + * then some other modules also depend on it and have already mapped it. + */ + if (mctx->llext->dependency[i]->use_count > 2) + continue; + + /* First user of this dependency, load it into SRAM */ + ret = llext_lib_find(mctx->llext->dependency[i], &dep_ctx[i]); + if (ret < 0) { + tr_err(&lib_manager_tr, + "Unmet dependency: cannot find dependency %u", i); + continue; + } + + tr_dbg(&lib_manager_tr, "%s depending on %s index %u, %u users", + mctx->llext->name, mctx->llext->dependency[i]->name, + dep_ctx[i]->start_idx, mctx->llext->dependency[i]->use_count); + + ret = llext_manager_load_module(dep_ctx[i]); + if (ret < 0) { + llext_depend_unlink(dep_ctx, i - 1); + return 0; + } + } + + /* Map executable code and data */ + ret = llext_manager_load_module(mctx); if (ret < 0) return 0; } @@ -547,6 +614,17 @@ int llext_manager_free_module(const uint32_t component_id) return 0; } + struct lib_manager_module *dep_ctx[LLEXT_MAX_DEPENDENCIES] = {}; + int i; /* signed to match llext_depend_unlink() */ + + for (i = 0; i < ARRAY_SIZE(mctx->llext->dependency); i++) + if (llext_lib_find(mctx->llext->dependency[i], &dep_ctx[i]) < 0) + break; + + /* Last user cleaning up, put dependencies */ + if (i) + llext_depend_unlink(dep_ctx, i - 1); + /* * The last instance of the module has been destroyed and it can now be * unloaded from SRAM @@ -559,6 +637,39 @@ int llext_manager_free_module(const uint32_t component_id) return llext_manager_unload_module(mctx); } +/* An auxiliary library has been loaded, need to read in its exported symbols */ +int llext_manager_add_library(uint32_t module_id) +{ + struct lib_manager_mod_ctx *const ctx = lib_manager_get_mod_ctx(module_id); + + if (ctx->mod) { + tr_err(&lib_manager_tr, "module_id: %#x: repeated load!", module_id); + return -EBUSY; + } + + const struct sof_man_fw_desc *desc = lib_manager_get_library_manifest(module_id); + unsigned int i; + + if (!ctx->mod) + llext_manager_mod_init(ctx, desc); + + for (i = 0; i < ctx->n_mod; i++) { + const struct sof_man_module *mod = lib_manager_get_module_manifest(module_id + i); + + if (mod->type.load_type == SOF_MAN_MOD_TYPE_LLEXT_AUX) { + const struct sof_man_module_manifest *mod_manifest; + const struct sof_module_api_build_info *buildinfo; + int ret = llext_manager_link_single(module_id + i, desc, ctx, + (const void **)&buildinfo, &mod_manifest); + + if (ret < 0) + return ret; + } + } + + return 0; +} + bool comp_is_llext(struct comp_dev *comp) { const uint32_t module_id = IPC4_MOD_ID(comp->ipc_config.id); From b8f3c9baa71dab1d0461bc42e884236e74210c29 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Jul 2024 12:55:45 +0200 Subject: [PATCH 11/11] fir: enable building FIR support code as an LLEXT object FIR support code is used by eq-fir and by tdfb. When both of them are built as LLEXT modules, FIR supporting functions can be dynamically loaded too. Signed-off-by: Guennadi Liakhovetski --- src/math/Kconfig | 2 +- src/math/fir.toml | 6 ++++++ src/math/fir_common.c | 18 ++++++++++++++++++ src/math/fir_generic.c | 2 ++ src/math/fir_hifi2ep.c | 2 ++ src/math/fir_hifi3.c | 1 + src/math/fir_llext/CMakeLists.txt | 10 ++++++++++ src/math/fir_llext/llext.toml.h | 5 +++++ uuid-registry.txt | 1 + zephyr/CMakeLists.txt | 16 +++++++++++----- 10 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/math/fir.toml create mode 100644 src/math/fir_common.c create mode 100644 src/math/fir_llext/CMakeLists.txt create mode 100644 src/math/fir_llext/llext.toml.h diff --git a/src/math/Kconfig b/src/math/Kconfig index ccd7520f20a7..b97f87bfe781 100644 --- a/src/math/Kconfig +++ b/src/math/Kconfig @@ -170,7 +170,7 @@ choice "FILTER_SIMD_LEVEL_SELECT" endchoice config MATH_FIR - bool "FIR filter library" + tristate "FIR filter library" default n help This option builds FIR (Finite Impulse Response) filter library. It diff --git a/src/math/fir.toml b/src/math/fir.toml new file mode 100644 index 000000000000..a81268637761 --- /dev/null +++ b/src/math/fir.toml @@ -0,0 +1,6 @@ + [[module.entry]] + name = "FIR" + uuid = "93446E12-1864-4E04-AFE0-3B1D778FFB79" + load_type = "3" + + index = __COUNTER__ diff --git a/src/math/fir_common.c b/src/math/fir_common.c new file mode 100644 index 000000000000..897ac857e07d --- /dev/null +++ b/src/math/fir_common.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2024 Intel Corporation. + +/* modular: llext dynamic link */ + +#include +#include +#include +#include +#include + +#include + +static const struct sof_man_module_manifest mod_manifest __section(".module") __used = + SOF_LLEXT_AUX_MANIFEST("FIR", NULL, SOF_REG_UUID(fir)); + +SOF_LLEXT_BUILDINFO; diff --git a/src/math/fir_generic.c b/src/math/fir_generic.c index 24fbdc665988..802242d4dbec 100644 --- a/src/math/fir_generic.c +++ b/src/math/fir_generic.c @@ -118,6 +118,7 @@ int32_t fir_32x16(struct fir_state_32x16 *fir, int32_t x) /* Q2.46 -> Q2.31, saturate to Q1.31 */ return sat_int32(y >> shift); } +EXPORT_SYMBOL(fir_32x16); void fir_32x16_2x(struct fir_state_32x16 *fir, int32_t x0, int32_t x1, int32_t *y0, int32_t *y1) { @@ -184,5 +185,6 @@ void fir_32x16_2x(struct fir_state_32x16 *fir, int32_t x0, int32_t x1, int32_t * *y0 = sat_int32(a0 >> shift); *y1 = sat_int32(a1 >> shift); } +EXPORT_SYMBOL(fir_32x16_2x); #endif diff --git a/src/math/fir_hifi2ep.c b/src/math/fir_hifi2ep.c index 7f3a2d42a829..2172e3e98d53 100644 --- a/src/math/fir_hifi2ep.c +++ b/src/math/fir_hifi2ep.c @@ -156,6 +156,7 @@ void fir_32x16_hifiep(struct fir_state_32x16 *fir, int32_t x, int32_t *y, int ls a = AE_SRAAQ56(AE_SLLASQ56S(a, lshift), rshift); AE_SQ32F_I(AE_ROUNDSQ32SYM(a), (ae_q32s *)y, 0); } +EXPORT_SYMBOL(fir_32x16_hifiep); /* HiFi EP has the follow number of reqisters that should not be exceeded * 4x 56 bit registers in register file Q @@ -249,5 +250,6 @@ void fir_32x16_2x_hifiep(struct fir_state_32x16 *fir, int32_t x0, int32_t x1, AE_SQ32F_I(AE_ROUNDSQ32SYM(b), (ae_q32s *)y1, 0); AE_SQ32F_I(AE_ROUNDSQ32SYM(a), (ae_q32s *)y0, 0); } +EXPORT_SYMBOL(fir_32x16_2x_hifiep); #endif diff --git a/src/math/fir_hifi3.c b/src/math/fir_hifi3.c index e71fc47fc900..a485bf235f79 100644 --- a/src/math/fir_hifi3.c +++ b/src/math/fir_hifi3.c @@ -162,6 +162,7 @@ void fir_32x16_hifi3(struct fir_state_32x16 *fir, ae_int32 x, ae_int32 *y, a = AE_SLAA64S(a, shift); AE_S32_L_I(AE_ROUND32F48SSYM(a), (ae_int32 *)y, 0); } +EXPORT_SYMBOL(fir_32x16_hifi3); /* HiFi EP has the follow number of reqisters that should not be exceeded * 4x 56 bit registers in register file Q diff --git a/src/math/fir_llext/CMakeLists.txt b/src/math/fir_llext/CMakeLists.txt new file mode 100644 index 000000000000..9b8d2531bea3 --- /dev/null +++ b/src/math/fir_llext/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +sof_llext_build("fir" + SOURCES ../fir_common.c + ../fir_generic.c + ../fir_hifi2ep.c + ../fir_hifi3.c + LIB openmodules +) diff --git a/src/math/fir_llext/llext.toml.h b/src/math/fir_llext/llext.toml.h new file mode 100644 index 000000000000..fa2eab98627b --- /dev/null +++ b/src/math/fir_llext/llext.toml.h @@ -0,0 +1,5 @@ +#include +#include "../fir.toml" + +[module] +count = __COUNTER__ diff --git a/uuid-registry.txt b/uuid-registry.txt index 7d7171ee1809..f06a9f259a1c 100644 --- a/uuid-registry.txt +++ b/uuid-registry.txt @@ -72,6 +72,7 @@ f6d15ad3-b122-458c-ae9b0ab0b5867aa0 dummy_dma 5150c0e6-27f9-4ec8-8351c705b642d12f eq_iir 889f6dcd-ddcd-4e05-aa5b0d39f8bca961 esai bfc7488c-75aa-4ce8-9dbed8da08a698c2 file +93446e12-1864-4e04-afe03b1d778ffb79 fir 61bca9a8-18d0-4a18-8e7b2639219804b7 gain c3c74249-058e-414f-82404da5f3fc2389 google_hotword bf0e1bbc-dc6a-45fe-bc902554cb137ab4 google_ctc_audio_processing diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 130736242587..567c03e5e5df 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -685,11 +685,17 @@ elseif(CONFIG_COMP_IIR) ) endif() -zephyr_library_sources_ifdef(CONFIG_MATH_FIR - ${SOF_MATH_PATH}/fir_generic.c - ${SOF_MATH_PATH}/fir_hifi2ep.c - ${SOF_MATH_PATH}/fir_hifi3.c -) +if(CONFIG_MATH_FIR STREQUAL "m") + add_subdirectory(${SOF_MATH_PATH}/fir_llext + ${PROJECT_BINARY_DIR}/fir_llext) + add_dependencies(app fir) +elseif(CONFIG_MATH_FIR) + zephyr_library_sources( + ${SOF_MATH_PATH}/fir_generic.c + ${SOF_MATH_PATH}/fir_hifi2ep.c + ${SOF_MATH_PATH}/fir_hifi3.c + ) +endif() zephyr_library_sources_ifdef(CONFIG_MATH_IIR_DF1 ${SOF_MATH_PATH}/iir_df1_generic.c