From c51277d6d4918dd3fb1124c61a4b063cf735e315 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Mon, 23 Mar 2026 13:24:32 +0100 Subject: [PATCH 1/4] manifest: rimage: llext: Add user_mode param to SOF_LLEXT_MODULE_MANIFEST Extend the SOF_LLEXT_MODULE_MANIFEST macro with an optional variadic parameter to specify the user_mode flag for llext modules. When the argument is omitted, user_mode defaults to 0, preserving backward compatibility with existing module manifests. Update the rimage to propagate the user_mode field from the module manifest to manifest structure in the final firmware binary image. Signed-off-by: Adrian Warecki --- src/include/module/module/llext.h | 4 +++- tools/rimage/src/manifest.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/include/module/module/llext.h b/src/include/module/module/llext.h index 2f05cb6c692a..2ef0d47d77cc 100644 --- a/src/include/module/module/llext.h +++ b/src/include/module/module/llext.h @@ -6,7 +6,7 @@ #ifndef MODULE_LLEXT_H #define MODULE_LLEXT_H -#define SOF_LLEXT_MODULE_MANIFEST(manifest_name, entry, affinity, mod_uuid, instances) \ +#define SOF_LLEXT_MODULE_MANIFEST(manifest_name, entry, affinity, mod_uuid, instances, ...) \ { \ .module = { \ .name = manifest_name, \ @@ -16,6 +16,8 @@ .type = { \ .load_type = SOF_MAN_MOD_TYPE_LLEXT, \ .domain_ll = 1, \ + .user_mode = COND_CODE_0(NUM_VA_ARGS_LESS_1(_, ##__VA_ARGS__), (0), \ + (GET_ARG_N(1, __VA_ARGS__))), \ }, \ .affinity_mask = (affinity), \ } \ diff --git a/tools/rimage/src/manifest.c b/tools/rimage/src/manifest.c index 6f3a161e37a7..fb30dc7ab219 100644 --- a/tools/rimage/src/manifest.c +++ b/tools/rimage/src/manifest.c @@ -212,6 +212,7 @@ static void man_get_section_manifest(struct image *image, man_module->type.domain_dp = sof_mod->module.type.domain_dp; man_module->type.domain_ll = sof_mod->module.type.domain_ll; man_module->type.load_type = sof_mod->module.type.load_type; + man_module->type.user_mode = sof_mod->module.type.user_mode; /* text segment */ segment = &man_module->segment[SOF_MAN_SEGMENT_TEXT]; From 2da084526b5b64d532957a8d92b612006f77d534 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Tue, 3 Mar 2026 17:53:22 +0100 Subject: [PATCH 2/4] userspace: proxy: Add support for llext modules Add support for userspace llext loadable modules to the userspace proxy. Call lib_manager_start_agent unconditionally, even when the system agent is not used, as this function is responsible for creating the userspace module proxy. Signed-off-by: Adrian Warecki --- .../module_adapter/library/userspace_proxy.c | 13 ++++++++--- src/library_manager/lib_manager.c | 23 ++++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/audio/module_adapter/library/userspace_proxy.c b/src/audio/module_adapter/library/userspace_proxy.c index 3859fbd129f9..d77fa003e8a6 100644 --- a/src/audio/module_adapter/library/userspace_proxy.c +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -163,6 +164,7 @@ static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap work_item->event = &worker.event; #endif work_item->params.context = user_ctx; + work_item->params.mod = NULL; user_ctx->work_item = work_item; return 0; @@ -382,7 +384,7 @@ static int userspace_proxy_start_agent(struct userspace_context *user_ctx, } int userspace_proxy_create(struct userspace_context **user_ctx, const struct comp_driver *drv, - const struct sof_man_module *manifest, system_agent_start_fn start_fn, + const struct sof_man_module *manifest, system_agent_start_fn agent_fn, const struct system_agent_params *agent_params, const void **agent_interface, const struct module_interface **ops) { @@ -410,7 +412,12 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com if (ret) goto error_dom; - ret = userspace_proxy_add_sections(context, agent_params->instance_id, manifest); + if (agent_fn) + ret = userspace_proxy_add_sections(context, agent_params->instance_id, manifest); + else + /* llext modules do not use the system agent. */ + ret = llext_manager_add_domain(agent_params->module_id, domain); + if (ret) goto error_dom; @@ -418,7 +425,7 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com if (ret) goto error_dom; - ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface); + ret = userspace_proxy_start_agent(context, agent_fn, agent_params, agent_interface); if (ret) { tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret); goto error_work_item; diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index b81ec6bbe738..951828808b7c 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -543,12 +543,14 @@ static int lib_manager_start_agent(const struct comp_driver *drv, return ret; } #endif /* CONFIG_SOF_USERSPACE_PROXY */ + if (agent) { + ret = agent(&agent_params, agent_interface); + if (ret) + tr_err(&lib_manager_tr, "System agent start failed %d!", ret); + return ret; + } - ret = agent(&agent_params, agent_interface); - if (ret) - tr_err(&lib_manager_tr, "System agent start failed %d!", ret); - - return ret; + return 0; } enum buildinfo_mod_type { MOD_TYPE_INVALID, MOD_TYPE_IADK, MOD_TYPE_LMDK, MOD_TYPE_LLEXT }; @@ -654,6 +656,7 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, case MOD_TYPE_LLEXT: agent = NULL; ops = (const struct module_interface *)module_entry_point; + agent_iface = NULL; break; case MOD_TYPE_LMDK: agent = &native_system_agent_start; @@ -671,12 +674,10 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, } /* At this point module resources are allocated and it is moved to L2 memory. */ - if (agent) { - ret = lib_manager_start_agent(drv, config, mod, args, module_entry_point, agent, - agent_iface, &userspace, &ops); - if (ret) - goto err; - } + ret = lib_manager_start_agent(drv, config, mod, args, module_entry_point, agent, + agent_iface, &userspace, &ops); + if (ret) + goto err; if (comp_set_adapter_ops(drv, ops) < 0) goto err; From 7fa81f8a99e92e265f337dc07b10274bc8f7fac9 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Fri, 20 Mar 2026 11:18:53 +0100 Subject: [PATCH 3/4] fast_get: Enable buffer sharing when using module driver heap Allow fast_get sram buffer sharing across multiple userspace module instances when CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP is enabled. The module driver heap is shared by all instances of a given module, so allocated buffers can safely be reused between them. Simplify checking whether a calling thread runs in userspace by verifying if the K_USER flag is set. Signed-off-by: Adrian Warecki --- zephyr/lib/fast-get.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zephyr/lib/fast-get.c b/zephyr/lib/fast-get.c index 85dd28027023..1bd8b9dbd600 100644 --- a/zephyr/lib/fast-get.c +++ b/zephyr/lib/fast-get.c @@ -150,7 +150,11 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) alloc_align = PLATFORM_DCACHE_ALIGN; } - if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE)) + if (size > FAST_GET_MAX_COPY_SIZE || !IS_ENABLED(CONFIG_USERSPACE) || + /* The module driver heap is shared by all instances of a given module. + * Instances can share the allocated buffer. + */ + IS_ENABLED(CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP)) alloc_ptr = dram_ptr; else /* When userspace is enabled only share large buffers */ @@ -183,8 +187,8 @@ const void *fast_get(struct k_heap *heap, const void *dram_ptr, size_t size) ret = entry->sram_ptr; #if CONFIG_USERSPACE - /* We only get there for large buffers */ - if (k_current_get()->mem_domain_info.mem_domain->num_partitions > 1) { + /* We only get there for large buffers or module driver heap is in use */ + if (k_current_get()->base.user_options & K_USER && size > FAST_GET_MAX_COPY_SIZE) { /* A userspace thread makes the request */ if (k_current_get() != entry->thread && !fast_get_domain_exists(k_current_get(), ret, From 761633afcd0ed3113fc4959ebb7c9ab58972696c Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 19 Mar 2026 16:00:28 +0100 Subject: [PATCH 4/4] userspace: proxy: Add partition for k_heap struct Add a memory partition for the module driver heap k_heap structure to the module's memory domain to allow it to be referenced by syscalls. When a new memory domain is created, only L2 entries mapped with OPTION_SAVE_ATTRS are copied. Memory mapped dynamically during firmware execution is not accessible in new memory domains by default. Update the code to reflect the removal of memory double mapping in Zephyr by replacing the CONFIG_XTENSA_MMU_DOUBLE_MAP with a simple CONFIG_SOF_ZEPHYR_HEAP_CACHED check. Signed-off-by: Adrian Warecki --- .../module_adapter/library/userspace_proxy.c | 25 ++++++++++++++++--- zephyr/lib/userspace_helper.c | 2 -- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/audio/module_adapter/library/userspace_proxy.c b/src/audio/module_adapter/library/userspace_proxy.c index d77fa003e8a6..75724d1f5c50 100644 --- a/src/audio/module_adapter/library/userspace_proxy.c +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -276,8 +276,24 @@ static int userspace_proxy_memory_init(struct userspace_context *user_ctx, tr_dbg(&userspace_proxy_tr, "Heap partition %#lx + %zx, attr = %u", heap_part.start, heap_part.size, heap_part.attr); -#if !defined(CONFIG_XTENSA_MMU_DOUBLE_MAP) && defined(CONFIG_SOF_ZEPHYR_HEAP_CACHED) -#define HEAP_PART_CACHED + /* When a new memory domain is created, only the "factory" entries from the L2 page + * tables are copied. Memory that was dynamically mapped during firmware execution + * will not be accessible from the new domain. The k_heap structure (drv->user_heap) + * resides in such dynamically mapped memory, so we must explicitly add a partition + * for it to ensure that syscalls can access this structure from the userspace domain. + */ + struct k_mem_partition heap_struct_part; + + k_mem_region_align(&heap_struct_part.start, &heap_struct_part.size, + POINTER_TO_UINT(drv->user_heap), + sizeof(*drv->user_heap), CONFIG_MM_DRV_PAGE_SIZE); + heap_struct_part.attr = K_MEM_PARTITION_P_RW_U_NA | + user_get_partition_attr(heap_struct_part.start); + + tr_err(&userspace_proxy_tr, "Heap struct partition %#lx + %zx, attr = %u", + heap_struct_part.start, heap_struct_part.size, heap_struct_part.attr); + +#if defined(CONFIG_SOF_ZEPHYR_HEAP_CACHED) /* Add cached module private heap to memory partitions */ struct k_mem_partition heap_cached_part = { .attr = K_MEM_PARTITION_P_RW_U_RW | XTENSA_MMU_CACHED_WB @@ -296,10 +312,11 @@ static int userspace_proxy_memory_init(struct userspace_context *user_ctx, * These include ops structures marked with APP_TASK_DATA. */ &common_partition, -#ifdef HEAP_PART_CACHED +#ifdef CONFIG_SOF_ZEPHYR_HEAP_CACHED &heap_cached_part, #endif - &heap_part + &heap_part, + &heap_struct_part }; tr_dbg(&userspace_proxy_tr, "Common partition %#lx + %zx, attr = %u", diff --git a/zephyr/lib/userspace_helper.c b/zephyr/lib/userspace_helper.c index 8c4aef423e15..cce57ac8ee4e 100644 --- a/zephyr/lib/userspace_helper.c +++ b/zephyr/lib/userspace_helper.c @@ -23,8 +23,6 @@ #include #include -#define MODULE_DRIVER_HEAP_CACHED CONFIG_SOF_ZEPHYR_HEAP_CACHED - /* Zephyr includes */ #include #include