From b5f2221b5fd9aa8ba92f7a09f53a90bc659a1d5e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 22 May 2026 15:03:28 +0200 Subject: [PATCH 01/10] lib-manager: use user heap for the driver object When CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP isn't selected, dynamically allocated driver objects should still be accessible to the userspace. Signed-off-by: Guennadi Liakhovetski --- src/library_manager/lib_manager.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index 19c317ed1089..8d41eafa7e8e 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -819,6 +819,8 @@ int lib_manager_register_module(const uint32_t component_id) goto cleanup; } } +#else + drv_heap = sof_sys_user_heap_get(); #endif /* CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP */ drv = sof_heap_alloc(drv_heap, SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, From 985431dd50d26eab6ad93351c7987bc36f63fb2f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 22 May 2026 15:37:31 +0200 Subject: [PATCH 02/10] llext: map modules for userspace LL When loading and linking LLEXT modules map them automatically for the LL memory domain, unless they belong to the DP domain. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib_manager.h | 1 + src/library_manager/llext_manager.c | 66 +++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 29c226eb61a7..2e641476fcb4 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -115,6 +115,7 @@ struct lib_manager_module { struct llext_buf_loader *ebl; /* Zephyr loadable extension buffer loader */ unsigned int n_dependent; /* For auxiliary modules: number of dependents */ bool mapped; + bool domain_dp; 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 4c1e4f02d5b5..b9e8f7ec4b29 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -199,6 +201,13 @@ static void llext_manager_unmap_detached_sections(const struct llext_loader *ldr #endif } +#ifdef CONFIG_USERSPACE +static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, + struct k_mem_domain *domain); +static int llext_manager_rm_mod_domain(struct lib_manager_module *mctx, + struct k_mem_domain *domain); +#endif + static int llext_manager_load_module(struct lib_manager_module *mctx) { /* Executable code (.text) */ @@ -292,8 +301,22 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) memset((__sparse_force void *)bss_addr, 0, bss_size); mctx->mapped = true; - return 0; +#ifdef CONFIG_SOF_USERSPACE_LL + if (!mctx->domain_dp) { + ret = llext_manager_add_mod_domain(mctx, zephyr_ll_mem_domain()); + if (ret < 0) { + tr_err(&lib_manager_tr, "failed to add domain: %d", ret); + goto e_data; + } + } +#endif + return 0; +#ifdef CONFIG_SOF_USERSPACE_LL +e_data: + if (data_size) + llext_manager_align_unmap(va_base_data, data_size); +#endif e_rodata: if (rodata_size) llext_manager_align_unmap(va_base_rodata, rodata_size); @@ -353,6 +376,11 @@ static int llext_manager_unload_module(struct lib_manager_module *mctx) mctx->mapped = false; +#ifdef CONFIG_SOF_USERSPACE_LL + if (!mctx->domain_dp) + llext_manager_rm_mod_domain(mctx, zephyr_ll_mem_domain()); +#endif + return err; } @@ -496,6 +524,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].mapped = false; + ctx->mod[n_mod].domain_dp = false; ctx->mod[n_mod].llext = NULL; ctx->mod[n_mod].ebl = NULL; ctx->mod[n_mod].n_dependent = 0; @@ -738,6 +767,8 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config dep_ctx[i] = dep; } + /* Avoid mapping DP modules to the LL domain */ + mctx->domain_dp = ipc_config->proc_domain == COMP_PROCESSING_DOMAIN_DP; /* Map executable code and data */ ret = llext_manager_load_module(mctx); if (ret < 0) @@ -778,13 +809,8 @@ static int llext_manager_rm_partition(struct k_mem_domain *domain, return k_mem_domain_remove_partition(domain, &part); } -int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *domain) +static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct k_mem_domain *domain) { - const uint32_t module_id = IPC4_MOD_ID(component_id); - struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); - const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); - const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); - struct lib_manager_module *mctx = ctx->mod + mod_idx; const struct llext *ext = mctx->llext; const struct llext_loader *ldr = &mctx->ebl->loader; @@ -800,6 +826,13 @@ int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *d uintptr_t va_base_data = mctx->segment[LIB_MANAGER_DATA].addr; size_t data_size = mctx->segment[LIB_MANAGER_DATA].size; + /* + * Add to domain on first load: for "normal" modules use_count == 1, + * for dependencies use_count == 2 and n_dependent == 1 + */ + if (ext->use_count > 1 && mctx->n_dependent != 1) + return 0; + int ret = llext_manager_add_partition(domain, va_base_text, text_size, K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); @@ -914,7 +947,7 @@ int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *d return ret; } -int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *domain) +int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *domain) { const uint32_t module_id = IPC4_MOD_ID(component_id); struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); @@ -922,6 +955,12 @@ int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *do const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); struct lib_manager_module *mctx = ctx->mod + mod_idx; + /* FIXME: handle dependencies */ + return llext_manager_add_mod_domain(mctx, domain); +} + +static int llext_manager_rm_mod_domain(struct lib_manager_module *mctx, struct k_mem_domain *domain) +{ /* Executable code (.text) */ uintptr_t va_base_text = mctx->segment[LIB_MANAGER_TEXT].addr; size_t text_size = mctx->segment[LIB_MANAGER_TEXT].size; @@ -988,6 +1027,17 @@ int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *do return ret; } + +int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *domain) +{ + const uint32_t module_id = IPC4_MOD_ID(component_id); + struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); + const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); + const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + struct lib_manager_module *mctx = ctx->mod + mod_idx; + + return llext_manager_rm_mod_domain(mctx, domain); +} #endif int llext_manager_free_module(const uint32_t component_id) From 2bd3637e543703e5d52f6097fa70a3ca623ed468 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Jun 2026 15:57:14 +0200 Subject: [PATCH 03/10] ipc: allocate userspace IPC thread dynamically Prepare for multi-core support: allocate the IPC thread dynamically and extract thread initialisation into a separate function. Signed-off-by: Guennadi Liakhovetski --- src/ipc/ipc-common.c | 77 +++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index afc8fe45de05..eb961a95bdfe 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -318,7 +318,6 @@ void ipc_schedule_process(struct ipc *ipc) #define IPC_USER_EVENT_CMD BIT(0) #define IPC_USER_EVENT_STOP BIT(1) -static struct k_thread ipc_user_thread; static K_THREAD_STACK_DEFINE(ipc_user_stack, CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE); /** @@ -419,6 +418,54 @@ static void ipc_user_thread_fn(void *p1, void *p2, void *p3) } } +__cold static int ipc_user_init_thread(struct ipc_user *ipc_user) +{ + char thread_name[] = "ll_user0"; + int ret; + + assert_can_be_cold(); + + /* Allocate kernel objects for the user-space thread */ + ipc_user->event = k_object_alloc(K_OBJ_EVENT); + if (!ipc_user->event) { + LOG_ERR("user IPC event alloc failed"); + return -ENOMEM; + } + k_event_init(ipc_user->event); + + ipc_user->thread = k_object_alloc(K_OBJ_THREAD); + if (!ipc_user->thread) { + LOG_ERR("user IPC thread alloc failed"); + ret = -ENOMEM; + goto e_event; + } + + k_thread_create(ipc_user->thread, ipc_user_stack, + CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE, + ipc_user_thread_fn, ipc_user, NULL, NULL, + -1, K_USER, K_FOREVER); + + k_thread_cpu_pin(ipc_user->thread, PLATFORM_PRIMARY_CORE_ID); + k_thread_name_set(ipc_user->thread, thread_name); + + /* + * Each userspace IPC thread must be able to wait on its private event + * and signal completion on the primary core semaphore + */ + k_thread_access_grant(ipc_user->thread, ipc_user->sem, ipc_user->event); + user_grant_dai_access_all(ipc_user->thread); + user_grant_dma_access_all(ipc_user->thread); + k_mem_domain_add_thread(zephyr_ll_mem_domain(), ipc_user->thread); + user_ll_grant_access(ipc_user->thread, PLATFORM_PRIMARY_CORE_ID); + + return 0; + +e_event: + k_object_free(ipc_user->event); + + return ret; +} + __cold static void ipc_user_init(void) { struct ipc *ipc = ipc_get(); @@ -443,38 +490,22 @@ __cold static void ipc_user_init(void) k_sem_init(ipc_user->sem, 0, 1); - /* Allocate kernel objects for the user-space thread */ - ipc_user->event = k_object_alloc(K_OBJ_EVENT); - if (!ipc_user->event) { - LOG_ERR("user IPC event alloc failed"); + ret = ipc_user_init_thread(ipc_user); + if (ret < 0) { + LOG_ERR("user IPC thread initialization failed"); sof_panic(SOF_IPC_PANIC_IPC); } - k_event_init(ipc_user->event); - k_thread_create(&ipc_user_thread, ipc_user_stack, - CONFIG_SOF_IPC_USER_THREAD_STACK_SIZE, - ipc_user_thread_fn, ipc_user, NULL, NULL, - -1, K_USER, K_FOREVER); - - ipc_user->thread = &ipc_user_thread; - k_thread_access_grant(&ipc_user_thread, ipc_user->sem, ipc_user->event); - user_grant_dai_access_all(&ipc_user_thread); - user_grant_dma_access_all(&ipc_user_thread); - ret = user_access_to_mailbox(zephyr_ll_mem_domain(), &ipc_user_thread); + ret = user_access_to_mailbox(zephyr_ll_mem_domain(), ipc_user->thread); if (ret < 0) { LOG_ERR("ipc user: mailbox access grant failed: %d", ret); sof_panic(SOF_IPC_PANIC_IPC); } - user_ll_grant_access(&ipc_user_thread, PLATFORM_PRIMARY_CORE_ID); - k_mem_domain_add_thread(zephyr_ll_mem_domain(), &ipc_user_thread); - - k_thread_cpu_pin(&ipc_user_thread, PLATFORM_PRIMARY_CORE_ID); - k_thread_name_set(&ipc_user_thread, "ipc_user"); /* Store references in ipc struct so kernel handler can forward commands */ ipc->ipc_user_pdata = ipc_user; - k_thread_start(&ipc_user_thread); + k_thread_start(ipc_user->thread); struct task *task = zephyr_ll_task_alloc(); @@ -486,7 +517,7 @@ __cold static void ipc_user_init(void) * Needed so user-space dai_common_new() can call * k_thread_access_grant(audio_thread, dai_mutex) from user context. */ - k_thread_access_grant(&ipc_user_thread, ipc_user->audio_thread); + k_thread_access_grant(ipc_user->thread, ipc_user->audio_thread); /* Wait for user thread startup — consumes the initial k_sem_give from thread */ k_sem_take(ipc->ipc_user_pdata->sem, K_FOREVER); From 9d0fab93681faa9d144eb8a86217428c5be4c54b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Jun 2026 16:03:36 +0200 Subject: [PATCH 04/10] ipc: allocate userspace context uncached Userspace IPC context is global, allocate it uncached. Signed-off-by: Guennadi Liakhovetski --- src/ipc/ipc-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ipc/ipc-common.c b/src/ipc/ipc-common.c index eb961a95bdfe..96b957f9fabb 100644 --- a/src/ipc/ipc-common.c +++ b/src/ipc/ipc-common.c @@ -469,7 +469,8 @@ __cold static int ipc_user_init_thread(struct ipc_user *ipc_user) __cold static void ipc_user_init(void) { struct ipc *ipc = ipc_get(); - struct ipc_user *ipc_user = sof_heap_alloc(sof_sys_user_heap_get(), SOF_MEM_FLAG_USER, + struct ipc_user *ipc_user = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*ipc_user), 0); int ret; From d925c13ee7b6e968a086b6b9057e6b56d91702d2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 22 Jun 2026 12:04:57 +0200 Subject: [PATCH 05/10] ipc: ipc4: extract pipeline ID detection into a function The SOF_IPC4_GLB_SET_PIPELINE_STATE IPC can apply to one or to multiple pipelines. Extract pipeline ID detection into a function to be re-used with userspace IPC processing. Signed-off-by: Guennadi Liakhovetski --- src/ipc/ipc4/handler-user.c | 75 +++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/ipc/ipc4/handler-user.c b/src/ipc/ipc4/handler-user.c index bc1712b2762a..ee00d595f435 100644 --- a/src/ipc/ipc4/handler-user.c +++ b/src/ipc/ipc4/handler-user.c @@ -428,6 +428,44 @@ __cold const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data_wrapper return ipc4_get_pipeline_data(); } +static int ipc4_pipeline_id_get(struct ipc4_message_request *ipc4, + struct ipc4_pipeline_set_state *state, + const uint32_t **ppl_id, unsigned int *ppl_count) +{ + if (!state->extension.r.multi_ppl) { + if (ppl_count) + *ppl_count = 1; + if (ppl_id) + *ppl_id = NULL; + return state->primary.r.ppl_id; + } + + const struct ipc4_pipeline_set_state_data *ppl_data = ipc4_get_pipeline_data(); + unsigned int cnt = ppl_data->pipelines_count; + + /* + * pipelines_count is read straight from the host-provided + * mailbox payload, so cap it at what the mailbox can + * physically hold. Anything larger means the host promised + * more ppl_id[] entries than fit in MAILBOX_HOSTBOX, and + * dereferencing the flex array would read out of bounds. + */ + if (cnt > (MAILBOX_HOSTBOX_SIZE - sizeof(struct ipc4_pipeline_set_state_data)) / + sizeof(uint32_t)) { + ipc_cmd_err(&ipc_tr, "ipc: pipelines_count %u exceeds mailbox bound", + cnt); + return -EINVAL; + } + dcache_invalidate_region((__sparse_force void __sparse_cache *)ppl_data->ppl_id, + sizeof(int) * cnt); + if (ppl_count) + *ppl_count = cnt; + if (ppl_id) + *ppl_id = ppl_data->ppl_id; + + return ppl_data->ppl_id[0]; +} + /** * \brief Process SET_PIPELINE_STATE IPC4 message (prepare + trigger phases). * @param[in] ipc4 IPC4 message request. @@ -435,46 +473,27 @@ __cold const struct ipc4_pipeline_set_state_data *ipc4_get_pipeline_data_wrapper */ int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) { - const struct ipc4_pipeline_set_state_data *ppl_data; struct ipc4_pipeline_set_state state; struct ipc_comp_dev *ppl_icd; struct ipc *ipc = ipc_get(); uint32_t cmd, ppl_count; - uint32_t id = 0; + unsigned int id; const uint32_t *ppl_id; bool use_idc = false; uint32_t idx; - int ret = 0; + int ret; int i; state.primary.dat = ipc4->primary.dat; state.extension.dat = ipc4->extension.dat; cmd = state.primary.r.ppl_state; - ppl_data = ipc4_get_pipeline_data(); - if (state.extension.r.multi_ppl) { - ppl_count = ppl_data->pipelines_count; - /* - * pipelines_count is read straight from the host-provided - * mailbox payload, so cap it at what the mailbox can - * physically hold. Anything larger means the host promised - * more ppl_id[] entries than fit in MAILBOX_HOSTBOX, and - * dereferencing the flex array would read out of bounds. - */ - if (ppl_count > (MAILBOX_HOSTBOX_SIZE - - sizeof(struct ipc4_pipeline_set_state_data)) / - sizeof(uint32_t)) { - ipc_cmd_err(&ipc_tr, - "ipc: pipelines_count %u exceeds mailbox bound", - ppl_count); - return IPC4_ERROR_INVALID_PARAM; - } - ppl_id = ppl_data->ppl_id; - dcache_invalidate_region((__sparse_force void __sparse_cache *)ppl_id, - sizeof(int) * ppl_count); - } else { - ppl_count = 1; - id = state.primary.r.ppl_id; + ret = ipc4_pipeline_id_get(ipc4, &state, &ppl_id, &ppl_count); + if (ret < 0) + return IPC4_ERROR_INVALID_PARAM; + + if (ppl_count == 1) { + id = ret; ppl_id = &id; } @@ -573,7 +592,7 @@ int ipc4_set_pipeline_state(struct ipc4_message_request *ipc4) return ret; } - return ret; + return IPC4_SUCCESS; } __cold static int ipc4_process_chain_dma(struct ipc4_message_request *ipc4) From 45f7accc107e475a0fd61fdbd84f708bd3b668c7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Jul 2026 12:04:43 +0200 Subject: [PATCH 06/10] schedule: dp: make compatible with userspace LL DP scheduler operations, instance data and DP module memory have to be accessible to the userspace LL scheduler. Allocate dynamic data on the userspace heap and place static data in the userspace accessible ELF section. Signed-off-by: Guennadi Liakhovetski --- src/audio/module_adapter/module_adapter.c | 4 ++++ src/schedule/zephyr_dp_schedule.c | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index 53e8c75c42e2..a2b293e4ad0d 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -93,7 +93,11 @@ static struct processing_module *module_adapter_mem_alloc(const struct comp_driv comp_cl_err(drv, "Failed to allocate DP module heap / vregion"); return NULL; } +#ifdef CONFIG_SOF_USERSPACE_LL + mod_heap = sof_sys_user_heap_get(); +#else mod_heap = NULL; +#endif } else { #ifdef CONFIG_SOF_USERSPACE_LL mod_heap = sof_sys_user_heap_get(); diff --git a/src/schedule/zephyr_dp_schedule.c b/src/schedule/zephyr_dp_schedule.c index 25fa8b319457..b3a5530b27d4 100644 --- a/src/schedule/zephyr_dp_schedule.c +++ b/src/schedule/zephyr_dp_schedule.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -29,6 +28,9 @@ #include +#include +#include + LOG_MODULE_REGISTER(dp_schedule, CONFIG_SOF_LOG_LEVEL); SOF_DEFINE_REG_UUID(dp_sched); @@ -337,7 +339,7 @@ static int scheduler_dp_task_shedule(void *data, struct task *task, uint64_t sta return 0; } -static struct scheduler_ops schedule_dp_ops = { +APP_SYSUSER_DATA static struct scheduler_ops schedule_dp_ops = { .schedule_task = scheduler_dp_task_shedule, #if CONFIG_SOF_USERSPACE_APPLICATION .schedule_task_cancel = scheduler_dp_task_cancel, @@ -353,12 +355,12 @@ __cold int scheduler_dp_init(void) assert_can_be_cold(); - struct scheduler_dp_data *dp_sch = rzalloc(SOF_MEM_FLAG_KERNEL, - sizeof(struct scheduler_dp_data)); + struct scheduler_dp_data *dp_sch = sof_heap_alloc(sof_sys_user_heap_get(), + SOF_MEM_FLAG_KERNEL, sizeof(*dp_sch), 0); if (!dp_sch) return -ENOMEM; - dp_sch->ll_tick_src.priv_data = NULL; + memset(dp_sch, 0, sizeof(*dp_sch)); list_init(&dp_sch->tasks); scheduler_init(SOF_SCHEDULE_DP, &schedule_dp_ops, dp_sch); From 0cb0d3850d700b614ded682687160cd4077b7c27 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 5 Aug 2026 15:11:49 +0200 Subject: [PATCH 07/10] ipc: fix a compiler warning Fix an "unused variable" compiler warning for when buildins with CONFIG_SOF_USERSPACE_LL=y. Signed-off-by: Guennadi Liakhovetski --- src/ipc/ipc-helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ipc/ipc-helper.c b/src/ipc/ipc-helper.c index cbd17d00da4c..567255a9e22a 100644 --- a/src/ipc/ipc-helper.c +++ b/src/ipc/ipc-helper.c @@ -297,7 +297,9 @@ __cold int ipc_comp_free(struct ipc *ipc, uint32_t comp_id) struct comp_buffer *buffer; struct comp_buffer *safe; struct list_item *clist; +#ifndef CONFIG_SOF_USERSPACE_LL uint32_t flags; +#endif assert_can_be_cold(); From 50cae156b26caf9a70116306be881bc5020aecaf Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 3 Aug 2026 10:08:15 +0200 Subject: [PATCH 08/10] ipc: (cosmetic) reduce the number of type-casts Change several pointers from "char *" to "unsigned char *" to reduce the number of type-casts. Signed-off-by: Guennadi Liakhovetski --- src/ipc/ipc4/helper.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ipc/ipc4/helper.c b/src/ipc/ipc4/helper.c index fd8bd8bf3dbc..fa19a0fe6079 100644 --- a/src/ipc/ipc4/helper.c +++ b/src/ipc/ipc4/helper.c @@ -88,24 +88,25 @@ void ipc_build_trace_posn(struct sof_ipc_dma_trace_posn *posn) } #if CONFIG_LIBRARY -static inline char *ipc4_get_comp_new_data(void) +static inline unsigned char *ipc4_get_comp_new_data(void) { struct ipc *ipc = ipc_get(); - char *data = (char *)ipc->comp_data + sizeof(struct ipc4_module_init_instance); + unsigned char *data = (unsigned char *)ipc->comp_data + + sizeof(struct ipc4_module_init_instance); return data; } -static const struct comp_driver *ipc4_library_get_comp_drv(char *data) +static const struct comp_driver *ipc4_library_get_comp_drv(unsigned char *data) { return ipc4_get_drv(data); } #else -__cold static inline char *ipc4_get_comp_new_data(void) +__cold static inline unsigned char *ipc4_get_comp_new_data(void) { assert_can_be_cold(); - return (char *)MAILBOX_HOSTBOX_BASE; + return (unsigned char *)MAILBOX_HOSTBOX_BASE; } #endif @@ -116,7 +117,7 @@ __cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_i const struct comp_driver *drv; struct comp_dev *dev; uint32_t comp_id; - char *data; + unsigned char *data; assert_can_be_cold(); @@ -190,13 +191,13 @@ __cold struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_i if (drv->type == SOF_COMP_MODULE_ADAPTER) { const struct ipc_config_process spec = { - .data = (const unsigned char *)data, + .data = data, .size = ipc_config.ipc_config_size, }; - dev = drv->ops.create(drv, &ipc_config, (const void *)&spec); + dev = drv->ops.create(drv, &ipc_config, &spec); } else { - dev = drv->ops.create(drv, &ipc_config, (const void *)data); + dev = drv->ops.create(drv, &ipc_config, data); } if (!dev) return NULL; @@ -241,7 +242,7 @@ __cold struct comp_dev *comp_new_ipc4_user(struct ipc4_message_request *ipc4, struct comp_ipc_config ipc_config; struct comp_dev *dev; uint32_t comp_id; - char *data; + unsigned char *data; int ret; assert_can_be_cold(); @@ -303,13 +304,13 @@ __cold struct comp_dev *comp_new_ipc4_user(struct ipc4_message_request *ipc4, if (drv->type == SOF_COMP_MODULE_ADAPTER) { const struct ipc_config_process spec = { - .data = (const unsigned char *)data, + .data = data, .size = ipc_config.ipc_config_size, }; - dev = drv->ops.create(drv, &ipc_config, (const void *)&spec); + dev = drv->ops.create(drv, &ipc_config, &spec); } else { - dev = drv->ops.create(drv, &ipc_config, (const void *)data); + dev = drv->ops.create(drv, &ipc_config, data); } if (!dev) return NULL; From 518f3e5815b0f610b03d0c9452df8a807a1c0d2f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 3 Aug 2026 10:18:58 +0200 Subject: [PATCH 09/10] component: use a local UUID copy for LLEXT instances The .uid field in struct comp_driver is used for driver identification using its unique UUID. However with LLEXT that UUID is located in DRAM, which makes access to it difficult from userspace threads. Make a local copy of it instead for reliable driver searching from different contexts. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/audio/component.h | 2 ++ src/library_manager/lib_manager.c | 3 ++- test/ztest/unit/fast-get/CMakeLists.txt | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index db8a69e63646..bf5fce875038 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -593,6 +594,7 @@ struct comp_driver { * Currently used by module_adapter. */ struct k_heap *user_heap; /**< Userspace heap */ + struct sof_uuid uid_cp; /**< UUID copy for LLEXT modules */ }; /** \brief Holds constant pointer to component driver */ diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index 8d41eafa7e8e..1b1dd5f2cc9e 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -760,7 +760,8 @@ static void lib_manager_module_free(struct comp_dev *dev) static void lib_manager_prepare_module_adapter(struct comp_driver *drv, const struct sof_uuid *uuid) { drv->type = SOF_COMP_MODULE_ADAPTER; - drv->uid = uuid; + drv->uid_cp = *uuid; + drv->uid = &drv->uid_cp; drv->tctx = &lib_manager_tr; drv->ops.create = lib_manager_module_create; drv->ops.prepare = module_adapter_prepare; diff --git a/test/ztest/unit/fast-get/CMakeLists.txt b/test/ztest/unit/fast-get/CMakeLists.txt index 39b79142d8bd..3fe6a87680f4 100644 --- a/test/ztest/unit/fast-get/CMakeLists.txt +++ b/test/ztest/unit/fast-get/CMakeLists.txt @@ -5,13 +5,18 @@ project(test_fast_get) set(SOF_ROOT "${PROJECT_SOURCE_DIR}/../../../..") +# Set SOF top directory for UUID registry generation +set(sof_top_dir ${SOF_ROOT}) + # Include SOF CMake functions include(${SOF_ROOT}/scripts/cmake/misc.cmake) +include(${SOF_ROOT}/scripts/cmake/uuid-registry.cmake) target_include_directories(app PRIVATE ${SOF_ROOT}/zephyr/include ${SOF_ROOT}/src/include ${SOF_ROOT}/src/platform/posix/include + ${PROJECT_BINARY_DIR}/include/generated # For uuid-registry.h ) # Define SOF-specific configurations for unit testing From 7c6e6e2d3bc71f9e076b716381cdcd34eface850 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 3 Aug 2026 13:47:41 +0200 Subject: [PATCH 10/10] llext: add a check to llext_manager_mod_find() Add a check to llext_manager_mod_find() in case scanning the array reached the last element, that the index indeed is within that element's range. Return an error otherwise. Signed-off-by: Guennadi Liakhovetski --- src/include/sof/lib_manager.h | 1 + src/library_manager/llext_manager.c | 39 ++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 2e641476fcb4..fb277ac75f64 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -114,6 +114,7 @@ struct lib_manager_module { struct llext *llext; /* Zephyr loadable extension context */ struct llext_buf_loader *ebl; /* Zephyr loadable extension buffer loader */ unsigned int n_dependent; /* For auxiliary modules: number of dependents */ + unsigned int n_mod; bool mapped; bool domain_dp; 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 b9e8f7ec4b29..4ad3c55d55bc 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -528,14 +528,19 @@ static int llext_manager_mod_init(struct lib_manager_mod_ctx *ctx, ctx->mod[n_mod].llext = NULL; ctx->mod[n_mod].ebl = NULL; ctx->mod[n_mod].n_dependent = 0; - ctx->mod[n_mod++].start_idx = i; + ctx->mod[n_mod].start_idx = i; + if (n_mod) + ctx->mod[n_mod - 1].n_mod = i - ctx->mod[n_mod - 1].start_idx; + n_mod++; } + ctx->mod[n_mod - 1].n_mod = desc->header.num_module_entries - ctx->mod[n_mod - 1].start_idx; + 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) +static int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx, unsigned int idx) { unsigned int i; @@ -543,6 +548,12 @@ static unsigned int llext_manager_mod_find(const struct lib_manager_mod_ctx *ctx if (ctx->mod[i].start_idx > idx) break; + if (i == ctx->n_mod && ctx->mod[i - 1].start_idx + ctx->mod[i - 1].n_mod <= idx) { + tr_err(&lib_manager_tr, "%u beyond %u + %u after %u", idx, + ctx->mod[i - 1].start_idx, ctx->mod[i - 1].n_mod, i); + return -ENOENT; + } + return i - 1; } @@ -564,7 +575,11 @@ static int llext_manager_link_single(uint32_t module_id, const struct sof_man_fw return -EINVAL; } - unsigned int mod_ctx_idx = llext_manager_mod_find(ctx, entry_index); + int mod_ctx_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_ctx_idx < 0) + return mod_ctx_idx; + struct lib_manager_module *mctx = ctx->mod + mod_ctx_idx; size_t mod_size; int i, inst_idx; @@ -952,7 +967,11 @@ int llext_manager_add_domain(const uint32_t component_id, struct k_mem_domain *d const uint32_t module_id = IPC4_MOD_ID(component_id); struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); - const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + const int mod_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_idx < 0) + return mod_idx; + struct lib_manager_module *mctx = ctx->mod + mod_idx; /* FIXME: handle dependencies */ @@ -1033,7 +1052,11 @@ int llext_manager_rm_domain(const uint32_t component_id, struct k_mem_domain *do const uint32_t module_id = IPC4_MOD_ID(component_id); struct lib_manager_mod_ctx *ctx = lib_manager_get_mod_ctx(module_id); const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(module_id); - const unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + const int mod_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_idx < 0) + return mod_idx; + struct lib_manager_module *mctx = ctx->mod + mod_idx; return llext_manager_rm_mod_domain(mctx, domain); @@ -1058,7 +1081,11 @@ int llext_manager_free_module(const uint32_t component_id) return -ENOENT; } - unsigned int mod_idx = llext_manager_mod_find(ctx, entry_index); + int mod_idx = llext_manager_mod_find(ctx, entry_index); + + if (mod_idx < 0) + return mod_idx; + struct lib_manager_module *mctx = ctx->mod + mod_idx; /* Protected by IPC serialization */