From 553fd78c5d93d140a914ddf9629357f93d4efa44 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Jul 2024 12:51:39 +0200 Subject: [PATCH 01/14] 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 | 14 +++++++++++++- src/include/sof/llext_manager.h | 3 ++- .../rimage/src/include/rimage/sof/user/manifest.h | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/include/module/module/llext.h b/src/include/module/module/llext.h index 05b8f0ffb5f5..2daab49a4ede 100644 --- a/src/include/module/module/llext.h +++ b/src/include/module/module/llext.h @@ -10,7 +10,7 @@ { \ .module = { \ .name = manifest_name, \ - .uuid = {mod_uuid}, \ + .uuid = {mod_uuid}, \ .entry_point = (uint32_t)(entry), \ .instance_max_count = instances, \ .type = { \ @@ -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 829b1b134810..29beb7eda62b 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 073ce9b72a99c4240cfa13c7ce0347a89e2410c8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Jul 2024 12:55:45 +0200 Subject: [PATCH 02/14] 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 | 21 +++++++++++++++++++++ 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 +++++ zephyr/CMakeLists.txt | 16 +++++++++++----- 9 files changed, 59 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..beca09dc5590 --- /dev/null +++ b/src/math/fir_common.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2024 Intel Corporation. + +/* modular: llext dynamic link */ + +#include +#include +#include +#include + +#include + +/* 93446e12-1864-4e04-afe0-3b1d778ffb79 */ +#define UUID_FIR 0x12, 0x6e, 0x44, 0x93, 0x64, 0x18, 0x04, 0x4e, \ + 0xaf, 0xe0, 0x3b, 0x1d, 0x77, 0x8f, 0xfb, 0x79 + +static const struct sof_man_module_manifest mod_manifest __section(".module") __used = + SOF_LLEXT_AUX_MANIFEST("FIR", NULL, 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/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 68eb85834942..508d998195be 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -671,11 +671,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 From 39846fc7cce3f4b10bd70c489cadca71daae0df7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 16 Jul 2024 11:04:38 +0200 Subject: [PATCH 03/14] llext: change a function parameter llext_manager_link() doesn't the whole of struct module_data, it only needs a pointer to a pointer to struct llext. Modify it to only receive the scope, that it needs. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 0aaa0797419f..bb84e3131ee9 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -230,7 +230,7 @@ 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 module_data *md, + struct lib_manager_module *mctx, struct llext **llext, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) { @@ -240,7 +240,7 @@ static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, .pre_located = true, .section_detached = llext_manager_section_detached, }; - int ret = llext_load(&ebl->loader, name, &md->llext, &ldr_parm); + int ret = llext_load(&ebl->loader, name, llext, &ldr_parm); if (ret) return ret; @@ -407,7 +407,7 @@ 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, + ret = llext_manager_link(&ebl, mod_array[entry_index - inst_idx].name, mctx, &md->llext, (const void **)&buildinfo, &mod_manifest); if (ret < 0) { tr_err(&lib_manager_tr, "linking failed: %d", ret); From 7f5315389077194d79b44780e0a1b1f4ff8c0eb9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 16 Jul 2024 12:33:09 +0200 Subject: [PATCH 04/14] 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 | 24 ++++++++--------------- src/library_manager/llext_manager.c | 19 ++++++++++++------ 6 files changed, 31 insertions(+), 32 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 ba1f8eadbfb0..8b4ae435de30 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -95,9 +95,12 @@ struct lib_manager_segment_desc { size_t size; }; +struct llext; + struct lib_manager_module { unsigned int start_idx; 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]; }; @@ -188,8 +191,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 ae824d512dc5..99b044cdbd2e 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,28 +536,24 @@ 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; + 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); if (ret < 0) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index bb84e3131ee9..617a383b9b1b 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -329,8 +329,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); @@ -348,7 +347,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; @@ -407,8 +405,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; @@ -423,7 +421,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; @@ -441,6 +439,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 5a8750397f198afa85857cf2dcc22fcd6446ddbb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 23 Aug 2024 11:52:17 +0200 Subject: [PATCH 05/14] 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 | 11 ++++------- src/library_manager/llext_manager.c | 18 +++++++++--------- 3 files changed, 13 insertions(+), 19 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 99b044cdbd2e..045fd6355cbf 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -547,18 +547,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 = 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(module_id); - if (ret < 0) - comp_err(dev, "lib_manager_free_module() failed!"); - } + /* Free module resources allocated in L2 memory. */ + ret = lib_manager_free_module(module_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 617a383b9b1b..5ab800256a90 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -439,15 +439,6 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config 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); @@ -461,9 +452,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 917dd85cf5b7354e8ac6e0ee5b52b6482c22a959 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 17 Jan 2025 16:08:10 +0100 Subject: [PATCH 06/14] 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 8b4ae435de30..898de6c43489 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 5ab800256a90..7103784d39af 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 ad78b55bacf6128a3e468a1b97eb521e34c38226 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 17 Jan 2025 17:06:08 +0100 Subject: [PATCH 07/14] 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 7103784d39af..d7e777e55480 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, @@ -290,6 +290,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, const struct sof_man_module *mod_array) @@ -325,6 +326,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; @@ -336,29 +338,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, mod_array); @@ -366,11 +357,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. @@ -386,9 +379,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) @@ -409,17 +402,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) { @@ -428,22 +475,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_free_module(const uint32_t component_id) From 5598d0e4cca16943ab13f83d9b56293c136b27ae Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 10:45:40 +0100 Subject: [PATCH 08/14] 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 d7a1fbe58e4e..0b55c04363a5 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 e33ec1476082eb101bd099ba4f912251ddd376cc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 10:56:39 +0100 Subject: [PATCH 09/14] llext: (cosmetic) document some structure fields Add comments to two structure fields, whose meaning isn't immediately obvious otherwise. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib_manager.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 898de6c43489..7cac4d5510f5 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -99,14 +99,15 @@ struct lib_manager_segment_desc { struct llext; struct lib_manager_module { - unsigned int start_idx; + 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]; }; struct lib_manager_mod_ctx { - void *base_addr; + void *base_addr; /* library storage address (DRAM) */ unsigned int n_mod; struct lib_manager_module *mod; }; From 4d5c973801273cdb8dd38d524c44c2130bf1bb78 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 20 Jan 2025 11:55:44 +0100 Subject: [PATCH 10/14] 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 | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index d7e777e55480..14fc13c055a9 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 llext *ext, 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 { @@ -339,8 +340,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 + @@ -402,9 +402,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 @@ -412,7 +410,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); @@ -455,10 +453,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) @@ -475,7 +472,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->llext, mctx); if (ret < 0) return 0; From 25ec1e43ed66c5404371351bf01cdf882df2711b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 15:39:44 +0100 Subject: [PATCH 11/14] llext: simplify llext_manager_mod_init() prototype Let llext_manager_mod_init() calculate mod_array itself internally, it has all the data for that. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/llext_manager.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 14fc13c055a9..e94178e5f876 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -293,9 +293,10 @@ static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, /* 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, - const struct sof_man_module *mod_array) + const struct sof_man_fw_desc *desc) { + struct sof_man_module *mod_array = (struct sof_man_module *)((uint8_t *)desc + + SOF_MAN_MODULE_OFFSET(0)); unsigned int i, n_mod; size_t offs; @@ -352,7 +353,7 @@ 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, mod_array); + 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", From 6aa2db62c18f5cef21744536055788b680c2d529 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 15:46:41 +0100 Subject: [PATCH 12/14] llext: move library initialisation to load time We need to initialise library context once for each loaded library. Instead of doing it at link time while checking, if it has already been initialised, we can do that at load time. 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 | 19 ++++++++++++++++--- 3 files changed, 30 insertions(+), 6 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 045fd6355cbf..f8b6e3443bb1 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -1033,14 +1033,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) + /* LLEXT libraries need to be initialized upon loading */ + ret = llext_manager_add_library(lib_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 e94178e5f876..325cac77c2b8 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -352,9 +352,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); @@ -512,6 +509,22 @@ int llext_manager_free_module(const uint32_t component_id) return llext_manager_unload_module(mctx); } +/* A library has been loaded, need to initialize its context */ +int llext_manager_add_library(uint32_t lib_id) +{ + uint32_t module_id = lib_id << LIB_MANAGER_LIB_ID_SHIFT; + struct lib_manager_mod_ctx *const ctx = lib_manager_get_mod_ctx(module_id); + const struct sof_man_fw_desc *desc = lib_manager_get_library_manifest(module_id); + unsigned int i; + + if (ctx->mod) { + tr_err(&lib_manager_tr, "lib_id: %u: repeated load!", lib_id); + return -EBUSY; + } + + return llext_manager_mod_init(ctx, desc); +} + bool comp_is_llext(struct comp_dev *comp) { const uint32_t module_id = IPC4_MOD_ID(comp->ipc_config.id); From 66b19e80288004b3bf6271fbbe64b09815219bc4 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 16:19:51 +0100 Subject: [PATCH 13/14] llext: add dependencies Add support for auxiliary modules, exporting symbols to other modules. In such cases Zephyr LLEXT API generates a dependency list, but it is only available while the dependent module is loaded. SOF can unload that dependent module from SRAM while still relying on it being present in a resolved state in DRAM. Then on a subsequent loading of that dependent, we don't need to regenerate the dependency list. For that we store it ourselves in module context. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib_manager.h | 2 + src/library_manager/llext_manager.c | 144 +++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 7cac4d5510f5..43d7366dbfb4 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -104,6 +104,8 @@ struct lib_manager_module { 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]; + unsigned int n_depend; + const char **depend; }; struct lib_manager_mod_ctx { diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 325cac77c2b8..9dbab67ff488 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -230,6 +230,41 @@ static bool llext_manager_section_detached(const elf_shdr_t *shdr) return shdr->sh_addr < SOF_MODULE_DRAM_LINK_END; } +static int llext_manager_add_dependency(struct llext_loader *loader, + struct lib_manager_module *mctx) +{ + if (mctx->n_depend) + return 0; + + unsigned int n_dep; + + for (n_dep = 0; n_dep < LLEXT_MAX_DEPENDENCIES && loader->dependency[n_dep][0]; n_dep++) + ; + + if (!n_dep) + return 0; + + void *dep = rmalloc(SOF_MEM_ZONE_RUNTIME_SHARED, SOF_MEM_FLAG_COHERENT, + SOF_MEM_CAPS_RAM, n_dep * LLEXT_MAX_NAME_LENGTH); + + if (!dep) + return -ENOMEM; + + memcpy(dep, loader->dependency, n_dep * LLEXT_MAX_NAME_LENGTH); + + mctx->depend = dep; + mctx->n_depend = n_dep; + + return 0; +} + +static void llext_manager_restore_dependency(struct llext_loader *loader, + struct lib_manager_module *mctx) +{ + if (mctx->n_depend) + memcpy(loader->dependency, mctx->depend, mctx->n_depend * LLEXT_MAX_NAME_LENGTH); +} + static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, struct lib_manager_module *mctx, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) @@ -241,11 +276,18 @@ static int llext_manager_link(struct llext_buf_loader *ebl, const char *name, .pre_located = true, .section_detached = llext_manager_section_detached, }; + + llext_manager_restore_dependency(&ebl->loader, mctx); + int ret = llext_load(&ebl->loader, name, llext, &ldr_parm); if (ret) return ret; + ret = llext_manager_add_dependency(&ebl->loader, mctx); + if (ret < 0) + 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; @@ -321,6 +363,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].n_depend = 0; ctx->mod[n_mod].segment[LIB_MANAGER_TEXT].size = 0; ctx->mod[n_mod++].start_idx = i; } @@ -340,6 +383,35 @@ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx return i - 1; } +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]); +} + static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw_desc *desc, struct lib_manager_mod_ctx *ctx, const void **buildinfo, const struct sof_man_module_manifest **mod_manifest) @@ -451,6 +523,7 @@ 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; + int ret, i; /* "module file" index in the ctx->mod array */ int mod_ctx_idx = llext_manager_link_single(module_id, desc, ctx, @@ -469,9 +542,44 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config return 0; } - /* Map executable code and data */ - int ret = llext_manager_load_module(mctx->llext, mctx); + /* + * 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; + } + + ret = llext_manager_load_module(dep_ctx[i]->llext, 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->llext, mctx); if (ret < 0) return 0; } @@ -500,12 +608,23 @@ 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; + struct lib_manager_module *dep_ctx[LLEXT_MAX_DEPENDENCIES] = {}; + int dep_cnt; + + for (dep_cnt = 0; dep_cnt < ARRAY_SIZE(mctx->llext->dependency); dep_cnt++) + if (llext_lib_find(mctx->llext->dependency[dep_cnt], &dep_ctx[dep_cnt]) < 0) + break; + if (llext_unload(&mctx->llext)) /* More users are active */ return 0; tr_dbg(&lib_manager_tr, "mod_id: %#x", component_id); + /* Last user cleaning up, put dependencies */ + if (dep_cnt > 0) + llext_depend_unlink(dep_ctx, dep_cnt - 1); + return llext_manager_unload_module(mctx); } @@ -522,7 +641,26 @@ int llext_manager_add_library(uint32_t lib_id) return -EBUSY; } - return llext_manager_mod_init(ctx, desc); + int ret = llext_manager_mod_init(ctx, desc); + + if (ret < 0) + return ret; + + 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) From dc2e92cec3ad2a85b191b59b6d0b23de4cb968be Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jan 2025 17:21:22 +0100 Subject: [PATCH 14/14] [DNM] test with a Zephyr PR Zephyr PR 84389 enables dependencies, but as it stands it will need some rework. Signed-off-by: Guennadi Liakhovetski --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 6fd718c542ea..086873f76d66 100644 --- a/west.yml +++ b/west.yml @@ -43,7 +43,7 @@ manifest: - name: zephyr repo-path: zephyr - revision: 42701fdb27298d20b4040cea5cde375b72c27cfc + revision: pull/84389/head remote: zephyrproject # Import some projects listed in zephyr/west.yml@revision