From 3262bf6bd95d4c56b8871d660fee8587af4d99e6 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Tue, 9 Dec 2025 16:53:33 +0100 Subject: [PATCH 1/5] userspace: proxy: Rename variables from user to user_ctx Update variable names to use user_ctx instead of user to improve clarity. Signed-off-by: Adrian Warecki --- .../module_adapter/library/userspace_proxy.c | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/audio/module_adapter/library/userspace_proxy.c b/src/audio/module_adapter/library/userspace_proxy.c index a7bfca2bb74a..e02be7095153 100644 --- a/src/audio/module_adapter/library/userspace_proxy.c +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -44,7 +44,7 @@ DECLARE_TR_CTX(userspace_proxy_tr, SOF_UUID(userspace_proxy_uuid), LOG_LEVEL_INF static const struct module_interface userspace_proxy_interface; -static int userspace_proxy_memory_init(struct userspace_context *user, +static int userspace_proxy_memory_init(struct userspace_context *user_ctx, const struct comp_driver *drv) { /* Add module private heap to memory partitions */ @@ -79,10 +79,10 @@ static int userspace_proxy_memory_init(struct userspace_context *user, &heap_part }; - return k_mem_domain_init(user->comp_dom, ARRAY_SIZE(parts_ptr), parts_ptr); + return k_mem_domain_init(user_ctx->comp_dom, ARRAY_SIZE(parts_ptr), parts_ptr); } -static int userspace_proxy_add_sections(struct userspace_context *user, uint32_t instance_id, +static int userspace_proxy_add_sections(struct userspace_context *user_ctx, uint32_t instance_id, const struct sof_man_module *const mod) { struct k_mem_partition mem_partition; @@ -103,7 +103,7 @@ static int userspace_proxy_add_sections(struct userspace_context *user, uint32_t mem_partition.start = mod->segment[idx].v_base_addr; mem_partition.size = mod->segment[idx].flags.r.length * CONFIG_MM_DRV_PAGE_SIZE; - ret = k_mem_domain_add_partition(user->comp_dom, &mem_partition); + ret = k_mem_domain_add_partition(user_ctx->comp_dom, &mem_partition); tr_dbg(&userspace_proxy_tr, "Add mod partition %p + %zx, attr = %u, ret = %d", UINT_TO_POINTER(mem_partition.start), mem_partition.size, @@ -116,7 +116,7 @@ static int userspace_proxy_add_sections(struct userspace_context *user, uint32_t lib_manager_get_instance_bss_address(instance_id, mod, &va_base, &mem_partition.size); mem_partition.start = POINTER_TO_UINT(va_base); mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW; - ret = k_mem_domain_add_partition(user->comp_dom, &mem_partition); + ret = k_mem_domain_add_partition(user_ctx->comp_dom, &mem_partition); tr_dbg(&userspace_proxy_tr, "Add bss partition %p + %zx, attr = %u, ret = %d", UINT_TO_POINTER(mem_partition.start), mem_partition.size, @@ -130,14 +130,14 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com const struct system_agent_params *agent_params, const void **agent_interface, const struct module_interface **ops) { - struct userspace_context *user; + struct userspace_context *context; struct k_mem_domain *domain; int ret; tr_dbg(&userspace_proxy_tr, "userspace create"); - user = k_heap_alloc(drv->user_heap, sizeof(struct userspace_context), K_FOREVER); - if (!user) + context = k_heap_alloc(drv->user_heap, sizeof(struct userspace_context), K_FOREVER); + if (!context) return -ENOMEM; /* Allocate memory domain struct */ @@ -146,13 +146,13 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com ret = -ENOMEM; goto error; } - user->comp_dom = domain; + context->comp_dom = domain; - ret = userspace_proxy_memory_init(user, drv); + ret = userspace_proxy_memory_init(context, drv); if (ret) goto error_dom; - ret = userspace_proxy_add_sections(user, agent_params->instance_id, manifest); + ret = userspace_proxy_add_sections(context, agent_params->instance_id, manifest); if (ret) goto error_dom; @@ -166,7 +166,7 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com } } - *user_ctx = user; + *user_ctx = context; /* Store a pointer to the module's interface. For the LMDK modules, the agent places a * pointer to the module interface at the address specified by agent_interface. Since this @@ -174,7 +174,7 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com * after the agent has been started. For other module types, the ops parameter points to a * valid module interface. */ - user->interface = *ops; + context->interface = *ops; /* All calls to the module interface must pass through the proxy. Set up our own interface. */ @@ -185,7 +185,7 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com error_dom: rfree(domain); error: - k_heap_free(drv->user_heap, user); + k_heap_free(drv->user_heap, context); return ret; } From 2b6bb1bf028d8d69fd0b72324ed282f91242e969 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Wed, 10 Dec 2025 17:46:41 +0100 Subject: [PATCH 2/5] lib_manager: Replace component_id parameter with config Change lib_manager_start_agent signature to accept a config object instead of component_id. Get core id from the ipc configuration and pass it to the system agent at startup. Signed-off-by: Adrian Warecki --- src/library_manager/lib_manager.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index 0588e5eea2be..be56984baf25 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -498,7 +498,7 @@ const struct sof_man_module *lib_manager_get_module_manifest(const uint32_t modu * \brief Starts system agent and returns module interface into agent_interface variable. * * \param[in] drv - Pointer to the component driver structure. - * \param[in] component_id - Identifier of the component. + * \param[in] config - Pointer to component ipc config structure * \param[in] args - Pointer to components' ipc configuration arguments. * \param[in] module_entry_point - Entry point address of the module. * \param[in] agent - Function pointer to the system agent start function. @@ -508,7 +508,8 @@ const struct sof_man_module *lib_manager_get_module_manifest(const uint32_t modu * * \return Error code returned by the system agent, 0 on success. */ -static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t component_id, +static int lib_manager_start_agent(const struct comp_driver *drv, + const struct comp_ipc_config *config, const struct sof_man_module *mod_manifest, const struct ipc_config_process *args, const uintptr_t module_entry_point, @@ -526,9 +527,9 @@ static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t mod_cfg.size = args->size >> 2; agent_params.entry_point = module_entry_point; - agent_params.module_id = IPC4_MOD_ID(component_id); - agent_params.instance_id = IPC4_INST_ID(component_id); - agent_params.core_id = 0; + agent_params.module_id = IPC4_MOD_ID(config->id); + agent_params.instance_id = IPC4_INST_ID(config->id); + agent_params.core_id = config->core; agent_params.log_handle = (uint32_t)drv->tctx; agent_params.mod_cfg = &mod_cfg; @@ -671,7 +672,7 @@ 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->id, mod, args, module_entry_point, agent, + ret = lib_manager_start_agent(drv, config, mod, args, module_entry_point, agent, agent_iface, &userspace, &ops); if (ret) goto err; From 1bfe0922a5a58362fd04ab6271dca4bf0558a16a Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Tue, 14 Oct 2025 22:24:03 +0200 Subject: [PATCH 3/5] userspace: Move selected variables to userspace-accessible memory partition Relocate specific variables to a memory partition accessible from userspace to enable running the system agent in userspace. This change ensures that only the required structures is accessible for userspace modules, while privileged data remains in kernel-only memory. Signed-off-by: Adrian Warecki --- src/audio/module_adapter/iadk/system_agent.cpp | 3 ++- src/audio/module_adapter/library/native_system_service.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/audio/module_adapter/iadk/system_agent.cpp b/src/audio/module_adapter/iadk/system_agent.cpp index 14f8e70f49a1..caed8cf65860 100644 --- a/src/audio/module_adapter/iadk/system_agent.cpp +++ b/src/audio/module_adapter/iadk/system_agent.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,7 @@ namespace system { /* Structure storing handles to system service operations */ -const AdspSystemService SystemAgent::system_service_ = { +const APP_TASK_DATA AdspSystemService SystemAgent::system_service_ = { native_system_service_log_message, native_system_service_safe_memcpy, native_system_service_safe_memmove, diff --git a/src/audio/module_adapter/library/native_system_service.c b/src/audio/module_adapter/library/native_system_service.c index 5126fa5ff12b..1c4688e885d0 100644 --- a/src/audio/module_adapter/library/native_system_service.c +++ b/src/audio/module_adapter/library/native_system_service.c @@ -19,6 +19,7 @@ #include #include #include +#include #define RSIZE_MAX 0x7FFFFFFF @@ -162,7 +163,7 @@ AdspErrorCode native_system_service_get_interface(enum interface_id id, return ADSP_NO_ERROR; } -const struct native_system_service native_system_service = { +const APP_TASK_DATA struct native_system_service native_system_service = { .basic = { .log_message = native_system_service_log_message, .safe_memcpy = native_system_service_safe_memcpy, From 95e5d0132a7b9137ea1d5370fd130782de537d60 Mon Sep 17 00:00:00 2001 From: Jaroslaw Stelter Date: Fri, 15 Sep 2023 14:49:23 +0200 Subject: [PATCH 4/5] userspace: proxy: Introduce user worker to service IPC The non-privileged modules code should be executed in separate thread executed in userspace mode. This way the code and data owned by such module will be isolated from other non-privileged modules in the system. The implementation creates user work queue thread that will service all IPC's for all such modules. Signed-off-by: Adrian Warecki Signed-off-by: Jaroslaw Stelter --- src/audio/module_adapter/CMakeLists.txt | 1 + .../module_adapter/library/userspace_proxy.c | 407 ++++++++++++++++-- .../library/userspace_proxy_user.c | 122 ++++++ .../module_adapter/library/userspace_proxy.h | 3 + .../library/userspace_proxy_user.h | 92 ++++ src/include/sof/schedule/dp_schedule.h | 1 + zephyr/Kconfig | 8 + 7 files changed, 596 insertions(+), 38 deletions(-) create mode 100644 src/audio/module_adapter/library/userspace_proxy_user.c create mode 100644 src/include/sof/audio/module_adapter/library/userspace_proxy_user.h diff --git a/src/audio/module_adapter/CMakeLists.txt b/src/audio/module_adapter/CMakeLists.txt index 25c9281dc64d..5910c7d99bc5 100644 --- a/src/audio/module_adapter/CMakeLists.txt +++ b/src/audio/module_adapter/CMakeLists.txt @@ -66,6 +66,7 @@ if(zephyr) ### Zephyr ### zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_PROXY library/userspace_proxy.c + library/userspace_proxy_user.c ) zephyr_library_sources_ifdef(CONFIG_PASSTHROUGH_CODEC diff --git a/src/audio/module_adapter/library/userspace_proxy.c b/src/audio/module_adapter/library/userspace_proxy.c index e02be7095153..4f8d49292904 100644 --- a/src/audio/module_adapter/library/userspace_proxy.c +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -27,14 +27,19 @@ #include #include +#include #include #include #include #include #include +#include #include + /* Assume that all the code runs in supervisor mode and don't make system calls. */ +#define __ZEPHYR_SUPERVISOR__ + LOG_MODULE_REGISTER(userspace_proxy, CONFIG_SOF_LOG_LEVEL); /* 6f6b6f4b-6f73-7466-20e1e62b9779f003 */ @@ -44,6 +49,166 @@ DECLARE_TR_CTX(userspace_proxy_tr, SOF_UUID(userspace_proxy_uuid), LOG_LEVEL_INF static const struct module_interface userspace_proxy_interface; +/* IPC requests targeting userspace modules are handled through a user work queue. + * Each userspace module provides its own work item that carries the IPC request parameters. + * The worker thread is switched into the module's memory domain and receives the work item. + * It invokes the appropriate module function in userspace context and writes the operation + * result back into the work item. + * + * There is only a single work queue, which is shared by all userspace modules. It is created + * dynamically when needed. Because SOF uses a single dedicated thread for handling IPC, there + * is no need to perform any additional serialization when accessing the worker. + */ +struct user_worker { + k_tid_t thread_id; /* ipc worker thread ID */ + uint32_t reference_count; /* module reference count */ + void *stack_ptr; /* pointer to worker stack */ + struct k_work_user_q work_queue; + struct k_event event; +}; + +static struct user_worker worker; + +static int user_worker_get(void) +{ + if (worker.reference_count) { + worker.reference_count++; + return 0; + } + + worker.stack_ptr = user_stack_allocate(CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE, + K_USER); + if (!worker.stack_ptr) { + tr_err(&userspace_proxy_tr, "Userspace worker stack allocation failed."); + return -ENOMEM; + } + + k_event_init(&worker.event); + k_work_user_queue_start(&worker.work_queue, worker.stack_ptr, + CONFIG_SOF_USERSPACE_PROXY_WORKER_STACK_SIZE, 0, NULL); + + worker.thread_id = k_work_user_queue_thread_get(&worker.work_queue); + + k_thread_access_grant(worker.thread_id, &worker.event); + + worker.reference_count++; + return 0; +} + +static void user_worker_put(void) +{ + /* Module removed so decrement counter */ + worker.reference_count--; + + /* Free worker resources if no more active user space modules */ + if (worker.reference_count == 0) { + k_thread_abort(worker.thread_id); + user_stack_free(worker.stack_ptr); + } +} + +static int user_work_item_init(struct userspace_context *user_ctx, struct k_heap *user_heap) +{ + struct user_work_item *work_item = NULL; + int ret; + + ret = user_worker_get(); + if (ret) + return ret; + + /* We have only a single userspace IPC worker. It handles requests for all userspace + * modules, which may run on different cores. Because the worker processes work items + * coming from any core, the work item must be allocated in coherent memory. + */ + work_item = sof_heap_alloc(user_heap, SOF_MEM_FLAG_COHERENT, sizeof(*work_item), 0); + if (!work_item) { + user_worker_put(); + return -ENOMEM; + } + + k_work_user_init(&work_item->work_item, userspace_proxy_worker_handler); + + work_item->event = &worker.event; + work_item->params.context = user_ctx; + user_ctx->work_item = work_item; + + return 0; +} + +static void user_work_item_free(struct userspace_context *user_ctx, struct k_heap *user_heap) +{ + sof_heap_free(user_heap, user_ctx->work_item); + user_worker_put(); +} + +static inline struct module_params *user_work_get_params(struct userspace_context *user_ctx) +{ + return &user_ctx->work_item->params; +} + +BUILD_ASSERT(IS_ALIGNED(MAILBOX_HOSTBOX_BASE, CONFIG_MMU_PAGE_SIZE), + "MAILBOX_HOSTBOX_BASE is not page aligned"); + + BUILD_ASSERT(IS_ALIGNED(MAILBOX_HOSTBOX_SIZE, CONFIG_MMU_PAGE_SIZE), + "MAILBOX_HOSTBOX_SIZE is not page aligned"); + +static int userspace_proxy_invoke(struct userspace_context *user_ctx, uint32_t cmd, + bool ipc_payload_access) +{ + struct module_params *params = user_work_get_params(user_ctx); + struct k_mem_partition ipc_part = { + .start = (uintptr_t)MAILBOX_HOSTBOX_BASE, + .size = MAILBOX_HOSTBOX_SIZE, + .attr = K_MEM_PARTITION_P_RO_U_RO, + }; + int ret, ret2; + + params->cmd = cmd; + + if (ipc_payload_access) { + ret = k_mem_domain_add_partition(user_ctx->comp_dom, &ipc_part); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "add mailbox to domain error: %d", ret); + return ret; + } + } + + /* Switch worker thread to module memory domain */ + ret = k_mem_domain_add_thread(user_ctx->comp_dom, worker.thread_id); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "failed to switch memory domain: error: %d", ret); + goto done; + } + + ret = k_work_user_submit_to_queue(&worker.work_queue, &user_ctx->work_item->work_item); + if (ret < 0) { + tr_err(&userspace_proxy_tr, "k_work_user_submit_to_queue(): error: %d", ret); + goto done; + } + + /* Timeout value is aligned with the ipc_wait_for_compound_msg function */ + if (!k_event_wait_safe(&worker.event, DP_TASK_EVENT_IPC_DONE, false, + Z_TIMEOUT_US(250 * 20))) { + tr_err(&userspace_proxy_tr, "ipc processing timedout."); + ret = -ETIMEDOUT; + } + +done: + if (ipc_payload_access) { + ret2 = k_mem_domain_remove_partition(user_ctx->comp_dom, &ipc_part); + if (ret2 < 0) { + tr_err(&userspace_proxy_tr, "Mailbox remove from domain error: %d", ret); + + if (!ret) + ret = ret2; + } + } + + return ret; +} + +extern struct k_mem_partition common_partition; + static int userspace_proxy_memory_init(struct userspace_context *user_ctx, const struct comp_driver *drv) { @@ -55,8 +220,8 @@ static int userspace_proxy_memory_init(struct userspace_context *user_ctx, POINTER_TO_UINT(heap->init_mem), heap->init_bytes, CONFIG_MM_DRV_PAGE_SIZE); - tr_dbg(&userspace_proxy_tr, "Heap partition %p + %zx, attr = %u", - UINT_TO_POINTER(heap_part.start), heap_part.size, heap_part.attr); + 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 @@ -67,18 +232,21 @@ static int userspace_proxy_memory_init(struct userspace_context *user_ctx, POINTER_TO_UINT(sys_cache_cached_ptr_get(heap->init_mem)), heap->init_bytes, CONFIG_MM_DRV_PAGE_SIZE); - tr_dbg(&userspace_proxy_tr, "Cached heap partition %p + %zx, attr = %u", - UINT_TO_POINTER(heap_cached_part.start), heap_cached_part.size, - heap_cached_part.attr); + tr_dbg(&userspace_proxy_tr, "Cached heap partition %#lx + %zx, attr = %u", + heap_cached_part.start, heap_cached_part.size, heap_cached_part.attr); #endif struct k_mem_partition *parts_ptr[] = { + &common_partition, #ifdef HEAP_PART_CACHED &heap_cached_part, #endif &heap_part }; + tr_dbg(&userspace_proxy_tr, "Common partition %#lx + %zx, attr = %u", + common_partition.start, common_partition.size, common_partition.attr); + return k_mem_domain_init(user_ctx->comp_dom, ARRAY_SIZE(parts_ptr), parts_ptr); } @@ -105,9 +273,8 @@ static int userspace_proxy_add_sections(struct userspace_context *user_ctx, uint ret = k_mem_domain_add_partition(user_ctx->comp_dom, &mem_partition); - tr_dbg(&userspace_proxy_tr, "Add mod partition %p + %zx, attr = %u, ret = %d", - UINT_TO_POINTER(mem_partition.start), mem_partition.size, - mem_partition.attr, ret); + tr_dbg(&userspace_proxy_tr, "Add mod partition %#lx + %zx, attr = %u, ret = %d", + mem_partition.start, mem_partition.size, mem_partition.attr, ret); if (ret < 0) return ret; @@ -118,13 +285,33 @@ static int userspace_proxy_add_sections(struct userspace_context *user_ctx, uint mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW; ret = k_mem_domain_add_partition(user_ctx->comp_dom, &mem_partition); - tr_dbg(&userspace_proxy_tr, "Add bss partition %p + %zx, attr = %u, ret = %d", - UINT_TO_POINTER(mem_partition.start), mem_partition.size, - mem_partition.attr, ret); + tr_dbg(&userspace_proxy_tr, "Add bss partition %#lx + %zx, attr = %u, ret = %d", + mem_partition.start, mem_partition.size, mem_partition.attr, ret); return ret; } +static int userspace_proxy_start_agent(struct userspace_context *user_ctx, + system_agent_start_fn start_fn, + const struct system_agent_params *agent_params, + const void **agent_interface) +{ + const byte_array_t * const mod_cfg = (byte_array_t *)agent_params->mod_cfg; + struct module_params *params = user_work_get_params(user_ctx); + int ret; + + params->ext.agent.start_fn = start_fn; + params->ext.agent.params = *agent_params; + params->ext.agent.mod_cfg = *mod_cfg; + + ret = userspace_proxy_invoke(user_ctx, USER_PROXY_MOD_CMD_AGENT_START, true); + if (ret) + return ret; + + *agent_interface = params->ext.agent.out_interface; + return 0; +} + 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 system_agent_params *agent_params, @@ -156,13 +343,17 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com if (ret) goto error_dom; + ret = user_work_item_init(context, drv->user_heap); + if (ret) + goto error_dom; + /* Start the system agent, if provided. */ - if (start_fn) { - ret = start_fn(agent_params, agent_interface); + if (start_fn) { + ret = userspace_proxy_start_agent(context, start_fn, agent_params, agent_interface); if (ret) { tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret); - goto error_dom; + goto error_work_item; } } @@ -182,6 +373,8 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com return 0; +error_work_item: + user_work_item_free(context, drv->user_heap); error_dom: rfree(domain); error: @@ -192,6 +385,7 @@ int userspace_proxy_create(struct userspace_context **user_ctx, const struct com void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx) { tr_dbg(&userspace_proxy_tr, "userspace proxy destroy"); + user_work_item_free(user_ctx, drv->user_heap); rfree(user_ctx->comp_dom); k_heap_free(drv->user_heap, user_ctx); } @@ -206,10 +400,18 @@ void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_con */ static int userspace_proxy_init(struct processing_module *mod) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); + params->mod = mod; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_INIT, true); + if (ret) + return ret; + /* Return status from module code operation. */ - return mod->user_ctx->interface->init(mod); + return params->status; } /** @@ -223,21 +425,40 @@ static int userspace_proxy_prepare(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->prepare) return 0; - return mod->user_ctx->interface->prepare(mod, sources, num_of_sources, sinks, num_of_sinks); + params->ext.proc.sources = sources; + params->ext.proc.num_of_sources = num_of_sources; + params->ext.proc.sinks = sinks; + params->ext.proc.num_of_sinks = num_of_sinks; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_PREPARE, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; } /** - * Copy parameters to user worker accessible space. - * Queue module init() operation and return its result. - * Module init() code is performed in user workqueue. + * Forward processing request to the module's process() implementation. * - * @param mod - pointer to processing module structure. - * @return 0 for success, error otherwise. + * It is invoked by the DP thread running in userspace, so no + * additional queuing or context switching is performed here. + * + * @param mod Pointer to the processing module instance. + * @param sources Array of input sources for the module. + * @param num_of_sources Number of input sources. + * @param sinks Array of output sinks for the module. + * @param num_of_sinks Number of output sinks. + * + * @return 0 on success, negative error code on failure. */ static int userspace_proxy_process(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks) @@ -255,10 +476,18 @@ static int userspace_proxy_process(struct processing_module *mod, struct sof_sou */ static int userspace_proxy_reset(struct processing_module *mod) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + if (!mod->user_ctx->interface->reset) return 0; - return mod->user_ctx->interface->reset(mod); + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_RESET, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; } /** @@ -271,13 +500,19 @@ static int userspace_proxy_reset(struct processing_module *mod) */ static int userspace_proxy_free(struct processing_module *mod) { + struct module_params *params = user_work_get_params(mod->user_ctx); int ret = 0; comp_dbg(mod->dev, "start"); - if (mod->user_ctx->interface->free) - ret = mod->user_ctx->interface->free(mod); + if (mod->user_ctx->interface->free) { + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_FREE, false); + if (ret) + return ret; + ret = params->status; + } + /* Destroy workqueue if this was last active userspace module */ userspace_proxy_destroy(mod->dev->drv, mod->user_ctx); mod->user_ctx = NULL; @@ -309,14 +544,28 @@ static int userspace_proxy_set_configuration(struct processing_module *mod, uint size_t fragment_size, uint8_t *response, size_t response_size) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->set_configuration) return 0; - return mod->user_ctx->interface->set_configuration(mod, config_id, pos, data_offset_size, - fragment, fragment_size, - response, response_size); + params->ext.set_conf.config_id = config_id; + params->ext.set_conf.pos = pos; + params->ext.set_conf.data_off_size = data_offset_size; + params->ext.set_conf.fragment = fragment; + params->ext.set_conf.fragment_size = fragment_size; + params->ext.set_conf.response = response; + params->ext.set_conf.response_size = response_size; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_SET_CONF, true); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; } /** @@ -338,13 +587,41 @@ static int userspace_proxy_get_configuration(struct processing_module *mod, uint uint32_t *data_offset_size, uint8_t *fragment, size_t fragment_size) { + struct module_params *params = user_work_get_params(mod->user_ctx); + struct k_mem_domain *domain = mod->user_ctx->comp_dom; + + /* Memory partition exposing the IPC response buffer. This buffer is allocated + * by the IPC driver and contains the payload of IPC replies sent to the host. + */ + struct k_mem_partition ipc_resp_part = { + .start = (uintptr_t)ipc_get()->comp_data, + .size = SOF_IPC_MSG_MAX_SIZE, + .attr = K_MEM_PARTITION_P_RW_U_RW, + }; + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->get_configuration) return -EIO; - return mod->user_ctx->interface->get_configuration(mod, config_id, data_offset_size, - fragment, fragment_size); + params->ext.get_conf.config_id = config_id; + params->ext.get_conf.data_off_size = data_offset_size; + params->ext.get_conf.fragment = fragment; + params->ext.get_conf.fragment_size = fragment_size; + + ret = k_mem_domain_add_partition(domain, &ipc_resp_part); + if (ret < 0) { + comp_err(mod->dev, "add response buffer to domain error: %d", ret); + return ret; + } + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_GET_CONF, true); + + k_mem_domain_remove_partition(domain, &ipc_resp_part); + + /* Return status from module code operation. */ + return ret ? ret : params->status; } /** @@ -359,12 +636,21 @@ static int userspace_proxy_get_configuration(struct processing_module *mod, uint static int userspace_proxy_set_processing_mode(struct processing_module *mod, enum module_processing_mode mode) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->set_processing_mode) return 0; - return mod->user_ctx->interface->set_processing_mode(mod, mode); + params->ext.proc_mode.mode = mode; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_SET_PROCMOD, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; } /** @@ -378,12 +664,20 @@ static int userspace_proxy_set_processing_mode(struct processing_module *mod, static enum module_processing_mode userspace_proxy_get_processing_mode(struct processing_module *mod) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->get_processing_mode) return -EIO; - return mod->user_ctx->interface->get_processing_mode(mod); + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_GET_PROCMOD, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->ext.proc_mode.mode; } /** @@ -400,14 +694,27 @@ static bool userspace_proxy_is_ready_to_process(struct processing_module *mod, struct sof_sink **sinks, int num_of_sinks) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->is_ready_to_process) return generic_module_is_ready_to_process(mod, sources, num_of_sources, sinks, num_of_sinks); - return mod->user_ctx->interface->is_ready_to_process(mod, sources, num_of_sources, - sinks, num_of_sinks); + params->ext.proc.sources = sources; + params->ext.proc.num_of_sources = num_of_sources; + params->ext.proc.sinks = sinks; + params->ext.proc.num_of_sinks = num_of_sinks; + + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_PROC_READY, false); + if (ret) + return generic_module_is_ready_to_process(mod, sources, num_of_sources, sinks, + num_of_sinks); + + /* Return status from module code operation. */ + return params->status; } /** @@ -421,12 +728,21 @@ static bool userspace_proxy_is_ready_to_process(struct processing_module *mod, */ static int userspace_proxy_bind(struct processing_module *mod, struct bind_info *bind_data) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->bind) return 0; - return mod->user_ctx->interface->bind(mod, bind_data); + params->ext.bind_data = bind_data; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_BIND, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; } /** @@ -440,12 +756,21 @@ static int userspace_proxy_bind(struct processing_module *mod, struct bind_info */ static int userspace_proxy_unbind(struct processing_module *mod, struct bind_info *unbind_data) { + struct module_params *params = user_work_get_params(mod->user_ctx); + int ret; + comp_dbg(mod->dev, "start"); if (!mod->user_ctx->interface->unbind) return 0; - return mod->user_ctx->interface->unbind(mod, unbind_data); + params->ext.bind_data = unbind_data; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_UNBIND, false); + if (ret) + return ret; + + /* Return status from module code operation. */ + return params->status; } /** @@ -458,12 +783,18 @@ static int userspace_proxy_unbind(struct processing_module *mod, struct bind_inf */ static int userspace_proxy_trigger(struct processing_module *mod, int cmd) { + struct module_params *params = user_work_get_params(mod->user_ctx); int ret = 0; comp_dbg(mod->dev, "start"); - if (mod->user_ctx->interface->trigger) - ret = mod->user_ctx->interface->trigger(mod, cmd); + if (mod->user_ctx->interface->trigger) { + params->ext.trigger_data = cmd; + ret = userspace_proxy_invoke(mod->user_ctx, USER_PROXY_MOD_CMD_TRIGGER, false); + if (ret) + return ret; + ret = params->status; + } if (!ret) ret = module_adapter_set_state(mod, mod->dev, cmd); @@ -472,7 +803,7 @@ static int userspace_proxy_trigger(struct processing_module *mod, int cmd) return ret; } -/* Userspace Module Adapter API */ +/* Userspace Proxy Module API */ APP_TASK_DATA static const struct module_interface userspace_proxy_interface = { .init = userspace_proxy_init, .is_ready_to_process = userspace_proxy_is_ready_to_process, diff --git a/src/audio/module_adapter/library/userspace_proxy_user.c b/src/audio/module_adapter/library/userspace_proxy_user.c new file mode 100644 index 000000000000..722a790e66fb --- /dev/null +++ b/src/audio/module_adapter/library/userspace_proxy_user.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2025 Intel Corporation. All rights reserved. +// +// Author: Adrian Warecki + +/** + * \file audio/module_adapter/library/userspace_proxy_user.c + * \brief Userspace proxy functions executed only in userspace context. + * \authors Adrian Warecki + */ + +/* Assume that all the code runs in user mode and unconditionally makes system calls. */ +#define __ZEPHYR_USER__ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +void userspace_proxy_handle_request(struct processing_module *mod, struct module_params *params) +{ + const struct module_interface *ops = params->context->interface; + + switch (params->cmd) { + case USER_PROXY_MOD_CMD_AGENT_START: + /* Set pointer to user accessible mod_cfg structure. */ + params->ext.agent.params.mod_cfg = ¶ms->ext.agent.mod_cfg; + + params->status = params->ext.agent.start_fn(¶ms->ext.agent.params, + ¶ms->ext.agent.out_interface); + break; + + case USER_PROXY_MOD_CMD_INIT: + params->status = ops->init(params->mod); + break; + + case USER_PROXY_MOD_CMD_PREPARE: + params->status = ops->prepare(params->mod, params->ext.proc.sources, + params->ext.proc.num_of_sources, + params->ext.proc.sinks, + params->ext.proc.num_of_sinks); + break; + + case USER_PROXY_MOD_CMD_PROC_READY: + params->status = ops->is_ready_to_process(params->mod, + params->ext.proc.sources, + params->ext.proc.num_of_sources, + params->ext.proc.sinks, + params->ext.proc.num_of_sinks); + break; + + case USER_PROXY_MOD_CMD_BIND: + params->status = ops->bind(params->mod, params->ext.bind_data); + break; + + case USER_PROXY_MOD_CMD_UNBIND: + params->status = ops->unbind(params->mod, params->ext.bind_data); + break; + + case USER_PROXY_MOD_CMD_RESET: + params->status = ops->reset(params->mod); + break; + + case USER_PROXY_MOD_CMD_FREE: + params->status = ops->free(params->mod); + break; + + case USER_PROXY_MOD_CMD_SET_CONF: + params->status = ops->set_configuration(params->mod, + params->ext.set_conf.config_id, + params->ext.set_conf.pos, + params->ext.set_conf.data_off_size, + params->ext.set_conf.fragment, + params->ext.set_conf.fragment_size, + params->ext.set_conf.response, + params->ext.set_conf.response_size); + break; + + case USER_PROXY_MOD_CMD_GET_CONF: + params->status = ops->get_configuration(params->mod, + params->ext.get_conf.config_id, + params->ext.get_conf.data_off_size, + params->ext.get_conf.fragment, + params->ext.get_conf.fragment_size); + break; + + case USER_PROXY_MOD_CMD_SET_PROCMOD: + params->status = ops->set_processing_mode(params->mod, + params->ext.proc_mode.mode); + break; + + case USER_PROXY_MOD_CMD_GET_PROCMOD: + params->ext.proc_mode.mode = ops->get_processing_mode(params->mod); + break; + + case USER_PROXY_MOD_CMD_TRIGGER: + params->status = ops->trigger(params->mod, params->ext.trigger_data); + break; + + default: + params->status = -EINVAL; + break; + } +} + +void userspace_proxy_worker_handler(struct k_work_user *work_item) +{ + struct user_work_item *user_work_item = CONTAINER_OF(work_item, struct user_work_item, + work_item); + struct module_params *params = &user_work_item->params; + + userspace_proxy_handle_request(params->mod, params); + k_event_post(user_work_item->event, DP_TASK_EVENT_IPC_DONE); +} diff --git a/src/include/sof/audio/module_adapter/library/userspace_proxy.h b/src/include/sof/audio/module_adapter/library/userspace_proxy.h index 150812183a90..872646d1eec9 100644 --- a/src/include/sof/audio/module_adapter/library/userspace_proxy.h +++ b/src/include/sof/audio/module_adapter/library/userspace_proxy.h @@ -14,10 +14,12 @@ #include #include +#include #include #include #include +#include struct module_interface; struct comp_driver; @@ -28,6 +30,7 @@ struct system_agent_params; struct userspace_context { struct k_mem_domain *comp_dom; /* Module specific memory domain */ const struct module_interface *interface; /* Userspace module interface */ + struct user_work_item *work_item; /* work item for user worker thread */ }; #endif /* CONFIG_USERSPACE */ diff --git a/src/include/sof/audio/module_adapter/library/userspace_proxy_user.h b/src/include/sof/audio/module_adapter/library/userspace_proxy_user.h new file mode 100644 index 000000000000..e571705cec17 --- /dev/null +++ b/src/include/sof/audio/module_adapter/library/userspace_proxy_user.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2025 Intel Corporation. All rights reserved. + * + * Author: Adrian Warecki + */ + +#ifndef __SOF_AUDIO_USERSPACE_PROXY_USER_H__ +#define __SOF_AUDIO_USERSPACE_PROXY_USER_H__ + +#if CONFIG_SOF_USERSPACE_PROXY +struct module_agent_params { + system_agent_start_fn start_fn; + struct system_agent_params params; + byte_array_t mod_cfg; + const void *out_interface; +}; + +struct module_large_cfg_set_params { + uint32_t config_id; + enum module_cfg_fragment_position pos; + uint32_t data_off_size; + const uint8_t *fragment; + size_t fragment_size; + uint8_t *response; + size_t response_size; +}; + +struct module_large_cfg_get_params { + uint32_t config_id; + uint32_t *data_off_size; + uint8_t *fragment; + size_t fragment_size; +}; + +struct module_processing_mode_params { + enum module_processing_mode mode; +}; + +struct module_process_params { + struct sof_source **sources; + int num_of_sources; + struct sof_sink **sinks; + int num_of_sinks; +}; + +enum userspace_proxy_cmd { + USER_PROXY_MOD_CMD_AGENT_START, + USER_PROXY_MOD_CMD_INIT, + USER_PROXY_MOD_CMD_PREPARE, + USER_PROXY_MOD_CMD_PROC_READY, + USER_PROXY_MOD_CMD_SET_PROCMOD, + USER_PROXY_MOD_CMD_GET_PROCMOD, + USER_PROXY_MOD_CMD_SET_CONF, + USER_PROXY_MOD_CMD_GET_CONF, + USER_PROXY_MOD_CMD_BIND, + USER_PROXY_MOD_CMD_UNBIND, + USER_PROXY_MOD_CMD_RESET, + USER_PROXY_MOD_CMD_FREE, + USER_PROXY_MOD_CMD_TRIGGER +}; + +struct module_params { + enum userspace_proxy_cmd cmd; + int status; + struct processing_module *mod; + struct userspace_context *context; + /* The field used in the union depends on the value of cmd */ + union { + struct module_agent_params agent; + struct module_large_cfg_set_params set_conf; + struct module_large_cfg_get_params get_conf; + struct module_processing_mode_params proc_mode; + struct module_process_params proc; + struct bind_info *bind_data; + int trigger_data; + } ext; +}; + +struct user_work_item { + struct k_work_user work_item; /* ipc worker workitem */ + struct k_event *event; /* ipc worker done event */ + struct module_params params; +}; + +void userspace_proxy_handle_request(struct processing_module *mod, struct module_params *params); + +void userspace_proxy_worker_handler(struct k_work_user *work_item); + +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + +#endif /* __SOF_AUDIO_USERSPACE_PROXY_USER_H__ */ diff --git a/src/include/sof/schedule/dp_schedule.h b/src/include/sof/schedule/dp_schedule.h index 27afcf9e5d01..37b8f1fc3f2c 100644 --- a/src/include/sof/schedule/dp_schedule.h +++ b/src/include/sof/schedule/dp_schedule.h @@ -92,6 +92,7 @@ enum { DP_TASK_EVENT_PROCESS = BIT(0), /* Need to process data */ DP_TASK_EVENT_CANCEL = BIT(1), /* Thread cancellation */ DP_TASK_EVENT_IPC = BIT(2), /* IPC message */ + DP_TASK_EVENT_IPC_DONE = BIT(3), /* IPC processing has completed. */ }; struct bind_info; diff --git a/zephyr/Kconfig b/zephyr/Kconfig index a4ee1a846af4..06b000d6bfb2 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -113,6 +113,14 @@ config SOF_USERSPACE_PROXY It is responsible for forwarding module function calls coming from sof running in kernelspace to the module code executed with user privileges. +config SOF_USERSPACE_PROXY_WORKER_STACK_SIZE + int "Userspace IPC worker stack size" + default 4096 + help + Size of the stack used by the IPC worker thread created by the + userspace proxy. This worker handles IPC requests for modules + running in userspace. + config SOF_USERSPACE_APPLICATION bool default USERSPACE && !SOF_USERSPACE_PROXY From 4b1124b5c7665df18f09ef9a6478cd2ef2ccf965 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Thu, 22 Jan 2026 15:05:19 +0100 Subject: [PATCH 5/5] west.yml: update zephyr to c1a2b3be45 Total of 490 commits. Changes include: c1a2b3be459 xtensa: Restore the EXCCAUSE register when returning from Double Exception fd8188a4082 xtensa: Remove saving EXCCAUSE in BSA from _Level1Vector 61e9f9ea045 soc: intel_adsp: rename CONFIG_SOC_INTEL_ACE* to CONFIG_SOC_ACE* 1dae40fa2ef soc: rename CONFIG_INTEL_CAVS_V25 to CONFIG_SOC_CAVSV25 58f9d5e6c35 doc: releases: release-notes: 4.4: Add notes on new bits d320921cd18 tests: subsys: llext: Add test for LLEXT_RODATA_NO_RELOC. Signed-off-by: Adrian Warecki --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index d398a5b54f23..eed08c27d52f 100644 --- a/west.yml +++ b/west.yml @@ -43,7 +43,7 @@ manifest: - name: zephyr repo-path: zephyr - revision: 7e3c5ab2bb2a9f4bf1b0afd43c872e6f19461777 + revision: c1a2b3be459d4f34d31ae54774fd57e96438d237 remote: zephyrproject # Import some projects listed in zephyr/west.yml@revision