From ea1fccd550c20f98bb1c7404f274a6a61dae6847 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Wed, 5 Nov 2025 18:07:18 +0100 Subject: [PATCH 1/6] module: generic: Update parameter name in mod_alloc_ext documentation Rename the parameter `bytes` to `size` in the documentation of the `mod_alloc_ext` function in `generic.h` to match the actual parameter name in the function definition. Signed-off-by: Adrian Warecki --- src/include/sof/audio/module_adapter/module/generic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index c1e730893517..d91627d2e135 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -194,7 +194,7 @@ void *mod_alloc_ext(struct processing_module *mod, uint32_t flags, size_t size, /** * Allocates aligned memory block for module. * @param mod Pointer to the module this memory block is allocated for. - * @param bytes Size in bytes. + * @param size Size in bytes. * @param alignment Alignment in bytes. * @return Pointer to the allocated memory or NULL if failed. * From e2386cc1560a9f57853bb76f32b4ab4298b65245 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Tue, 4 Nov 2025 12:57:10 +0100 Subject: [PATCH 2/6] userspace: Add option to toggle driver heap for userspace modules Introduce a configuration option to control heap sharing between userspace module instances. When enabled, instances of the same module type share a private heap allocated for the module driver. When disabled, each instance maintains its own independent heap. Rename DRV_HEAP_SIZE to more meaningful name USER_MOD_HEAP_SIZE. This allows fine-grained control over memory isolation and resource sharing in userspace modules. Signed-off-by: Adrian Warecki --- src/include/sof/audio/component.h | 2 +- zephyr/Kconfig | 8 ++++++++ zephyr/include/rtos/userspace_helper.h | 4 ++-- zephyr/lib/userspace_helper.c | 6 +++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 2d0e9faba610..02639f823419 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -1214,7 +1214,7 @@ bool comp_update_performance_data(struct comp_dev *dev, uint32_t cycles_used); static inline int user_get_buffer_memory_region(const struct comp_driver *drv) { -#if CONFIG_USERSPACE +#if CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP return drv->user_heap ? SOF_MEM_FLAG_USER_SHARED_BUFFER : SOF_MEM_FLAG_USER; #else return SOF_MEM_FLAG_USER; diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 1314454a0466..b94af9bd4381 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -67,6 +67,14 @@ config SOF_ZEPHYR_VIRTUAL_HEAP_REGION_SIZE help This config defines size of virtual heap region shared between all cores +config SOF_USERSPACE_USE_DRIVER_HEAP + bool "Use driver heap for SOF userspace modules" + depends on USERSPACE + help + When selected, multiple instances of the same userspace module will share + a private heap created for that module's driver. Otherwise, each module + instance will have its own independent private heap. + config SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE hex "Size of the private heap created for each userspace module" default 0x1000 diff --git a/zephyr/include/rtos/userspace_helper.h b/zephyr/include/rtos/userspace_helper.h index 254c8a2f96f1..23b69598745b 100644 --- a/zephyr/include/rtos/userspace_helper.h +++ b/zephyr/include/rtos/userspace_helper.h @@ -19,8 +19,8 @@ #include -#define DRV_HEAP_SIZE ALIGN_UP(CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE, \ - CONFIG_MM_DRV_PAGE_SIZE) +#define USER_MOD_HEAP_SIZE ALIGN_UP(CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE, \ + CONFIG_MM_DRV_PAGE_SIZE) #define APP_TASK_BSS K_APP_BMEM(common_partition) #define APP_TASK_DATA K_APP_DMEM(common_partition) diff --git a/zephyr/lib/userspace_helper.c b/zephyr/lib/userspace_helper.c index b26370b12d25..41c374b1df43 100644 --- a/zephyr/lib/userspace_helper.c +++ b/zephyr/lib/userspace_helper.c @@ -36,16 +36,16 @@ struct sys_heap *module_driver_heap_init(void) if (!mod_drv_heap) return NULL; - void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, DRV_HEAP_SIZE, + void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, USER_MOD_HEAP_SIZE, CONFIG_MM_DRV_PAGE_SIZE); if (!mem) { rfree(mod_drv_heap); return NULL; } - sys_heap_init(mod_drv_heap, mem, DRV_HEAP_SIZE); + sys_heap_init(mod_drv_heap, mem, USER_MOD_HEAP_SIZE); mod_drv_heap->init_mem = mem; - mod_drv_heap->init_bytes = DRV_HEAP_SIZE; + mod_drv_heap->init_bytes = USER_MOD_HEAP_SIZE; return mod_drv_heap; } From d9f0c6bebd54f0b83df93500309a8b8cd523192e Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Mon, 3 Nov 2025 17:26:03 +0100 Subject: [PATCH 3/6] module: Move system_agent_start parameters into system_agent_params struct Replace multiple arguments passed to system_agent_start and native_system_agent_start with a single pointer to system_agent_params. This improves readability and simplifies function signature. Signed-off-by: Adrian Warecki --- .../module_adapter/iadk/system_agent.cpp | 12 ++++++---- .../library/native_system_agent.c | 15 ++++++------ .../audio/module_adapter/iadk/system_agent.h | 5 ++-- .../library/native_system_agent.h | 24 ++++++++++--------- src/library_manager/lib_manager.c | 14 +++++++---- 5 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/audio/module_adapter/iadk/system_agent.cpp b/src/audio/module_adapter/iadk/system_agent.cpp index 860d5b1b279b..14f8e70f49a1 100644 --- a/src/audio/module_adapter/iadk/system_agent.cpp +++ b/src/audio/module_adapter/iadk/system_agent.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace intel_adsp; using namespace intel_adsp::system; @@ -124,16 +125,17 @@ int SystemAgent::CheckIn(ProcessingModuleFactoryInterface& module_factory, typedef int (*create_instance_f)(uint32_t module_id, uint32_t instance_id, uint32_t core_id, void *mod_cfg, void *parent_ppl, void **mod_ptr); -int system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void* mod_cfg, +int system_agent_start(const struct system_agent_params *params, const void **adapter) { uint32_t ret; - SystemAgent system_agent(module_id, instance_id, core_id, log_handle); + SystemAgent system_agent(params->module_id, params->instance_id, params->core_id, + params->log_handle); void* system_agent_p = reinterpret_cast(&system_agent); - create_instance_f ci = (create_instance_f)(entry_point); - ret = ci(module_id, instance_id, core_id, mod_cfg, NULL, &system_agent_p); + create_instance_f ci = (create_instance_f)(params->entry_point); + ret = ci(params->module_id, params->instance_id, params->core_id, params->mod_cfg, NULL, + &system_agent_p); IadkModuleAdapter* module_adapter = reinterpret_cast(system_agent_p); *adapter = module_adapter; diff --git a/src/audio/module_adapter/library/native_system_agent.c b/src/audio/module_adapter/library/native_system_agent.c index 9a93af6f3565..b93405b8bd27 100644 --- a/src/audio/module_adapter/library/native_system_agent.c +++ b/src/audio/module_adapter/library/native_system_agent.c @@ -19,21 +19,20 @@ typedef void* (*native_create_instance_f)(void *mod_cfg, void *parent_ppl, struct native_system_agent native_sys_agent; -int native_system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void *mod_cfg, +int native_system_agent_start(const struct system_agent_params *params, const void **iface) { - native_sys_agent.module_id = module_id; - native_sys_agent.instance_id = instance_id; - native_sys_agent.core_id = core_id; - native_sys_agent.log_handle = log_handle; + native_sys_agent.module_id = params->module_id; + native_sys_agent.instance_id = params->instance_id; + native_sys_agent.core_id = params->core_id; + native_sys_agent.log_handle = params->log_handle; const void *ret; void *system_agent_p = &native_sys_agent; - native_create_instance_f ci = (native_create_instance_f)entry_point; + native_create_instance_f ci = (native_create_instance_f)params->entry_point; - ret = ci(mod_cfg, NULL, &system_agent_p); + ret = ci(params->mod_cfg, NULL, &system_agent_p); if (!ret) return -EINVAL; diff --git a/src/include/sof/audio/module_adapter/iadk/system_agent.h b/src/include/sof/audio/module_adapter/iadk/system_agent.h index a0470167c796..e7aab98c748c 100644 --- a/src/include/sof/audio/module_adapter/iadk/system_agent.h +++ b/src/include/sof/audio/module_adapter/iadk/system_agent.h @@ -107,8 +107,9 @@ extern "C" { * method (the variant with 7 parameters) via a parameter that initially contained the address to * the agent system. The system_agent_start function returns it in the variable adapter. */ -int system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void *mod_cfg, const void **adapter); +struct system_agent_params; + +int system_agent_start(const struct system_agent_params *params, const void **adapter); #ifdef __cplusplus } #endif diff --git a/src/include/sof/audio/module_adapter/library/native_system_agent.h b/src/include/sof/audio/module_adapter/library/native_system_agent.h index 657bc6ac643d..70c0f6b1aad1 100644 --- a/src/include/sof/audio/module_adapter/library/native_system_agent.h +++ b/src/include/sof/audio/module_adapter/library/native_system_agent.h @@ -11,9 +11,17 @@ #include #include -typedef int (*system_agent_start_fn)(uintptr_t entry_point, uint32_t module_id, - uint32_t instance_id, uint32_t core_id, uint32_t log_handle, - void *mod_cfg, const void **adapter); +struct system_agent_params { + uintptr_t entry_point; /* The module entry point function address. */ + uint32_t module_id; /* The identifier for the module. */ + uint32_t instance_id; /* The instance identifier of the module. */ + uint32_t core_id; /* Core on which the module will run. */ + uint32_t log_handle; /* The handle for logging purposes. */ + void *mod_cfg; /* Pointer to the module configuration data. */ +}; + +typedef int (*system_agent_start_fn)(const struct system_agent_params *params, + const void **adapter); struct native_system_agent { struct system_service system_service; @@ -29,18 +37,12 @@ struct native_system_agent { * * This function initializes and starts the native system agent with the provided parameters. * - * @param[in] entry_point - The module entry point function address. - * @param[in] module_id - The identifier for the module. - * @param[in] instance_id - The instance identifier of the module. - * @param[in] core_id - Core on which the module will run. - * @param[in] log_handle - The handle for logging purposes. - * @param[in] mod_cfg - Pointer to the module configuration data. + * @param[in] params - Pointer to the system agent parameter structure * @param[out] iface - Pointer to the module interface. * * @return Returns 0 on success or an error code on failure. */ -int native_system_agent_start(uintptr_t entry_point, uint32_t module_id, uint32_t instance_id, - uint32_t core_id, uint32_t log_handle, void *mod_cfg, +int native_system_agent_start(const struct system_agent_params *params, const void **iface); #endif /* __NATIVE_SYSTEM_AGENT_H__ */ diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index 9e7de75a88ee..d3404724d8bb 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -501,9 +501,7 @@ static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t const system_agent_start_fn agent, const void **agent_interface) { - const uint32_t module_id = IPC4_MOD_ID(component_id); - const uint32_t instance_id = IPC4_INST_ID(component_id); - const uint32_t log_handle = (uint32_t)drv->tctx; + struct system_agent_params agent_params; byte_array_t mod_cfg; int ret; @@ -511,8 +509,14 @@ static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t /* Intel modules expects DW size here */ mod_cfg.size = args->size >> 2; - ret = agent(module_entry_point, module_id, instance_id, 0, log_handle, &mod_cfg, - agent_interface); + 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.log_handle = (uint32_t)drv->tctx; + agent_params.mod_cfg = &mod_cfg; + + ret = agent(&agent_params, agent_interface); if (ret) tr_err(&lib_manager_tr, "System agent start failed %d!", ret); From 84b3a04bc8933e24aa533d813338a8d945326222 Mon Sep 17 00:00:00 2001 From: Jaroslaw Stelter Date: Fri, 15 Sep 2023 14:49:23 +0200 Subject: [PATCH 4/6] userspace: Introduce userspace proxy Introduce initial support for userspace proxy logic. This includes configuring the module's memory domain to ensure proper access rights and creating intermediary functions that forward calls to the module's interface. Signed-off-by: Jaroslaw Stelter Signed-off-by: Adrian Warecki --- src/audio/module_adapter/CMakeLists.txt | 4 + .../module_adapter/library/userspace_proxy.c | 489 ++++++++++++++++++ .../module_adapter/library/userspace_proxy.h | 42 +- uuid-registry.txt | 1 + zephyr/Kconfig | 8 + 5 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 src/audio/module_adapter/library/userspace_proxy.c diff --git a/src/audio/module_adapter/CMakeLists.txt b/src/audio/module_adapter/CMakeLists.txt index ab15558d361d..e341e90d872c 100644 --- a/src/audio/module_adapter/CMakeLists.txt +++ b/src/audio/module_adapter/CMakeLists.txt @@ -57,6 +57,10 @@ if(zephyr) ### Zephyr ### library/native_system_service.c ) + zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_PROXY + library/userspace_proxy.c + ) + zephyr_library_sources_ifdef(CONFIG_PASSTHROUGH_CODEC module/passthrough.c ) diff --git a/src/audio/module_adapter/library/userspace_proxy.c b/src/audio/module_adapter/library/userspace_proxy.c new file mode 100644 index 000000000000..3bc6124cebc4 --- /dev/null +++ b/src/audio/module_adapter/library/userspace_proxy.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// +// Author: Jaroslaw Stelter +// Author: Adrian Warecki + +/** + * \file audio/module_adapter/library/userspace_proxy.c + * \brief Userspace proxy. Acts as an intermediary between SOF and a userspace module. + * \brief Responsible for preparing the memory domain required for userspace execution + * \brief and forwarding API calls. The proxy invokes corresponding module methods + * \brief in userspace context. Enables execution of any module implementing module_interface + * \brief as a userspace module. + * \authors Adrian Warecki + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(userspace_proxy, CONFIG_SOF_LOG_LEVEL); + +/* 6f6b6f4b-6f73-7466-20e1e62b9779f003 */ +SOF_DEFINE_REG_UUID(userspace_proxy); + +DECLARE_TR_CTX(userspace_proxy_tr, SOF_UUID(userspace_proxy_uuid), LOG_LEVEL_INFO); + +static const struct module_interface userspace_proxy_interface; + +static int userspace_proxy_memory_init(struct userspace_context *user, + const struct comp_driver *drv) +{ + /* Add module private heap to memory partitions */ + struct k_mem_partition heap_part = { .attr = K_MEM_PARTITION_P_RW_U_RW }; + + k_mem_region_align(&heap_part.start, &heap_part.size, + POINTER_TO_UINT(drv->user_heap->init_mem), + drv->user_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); + +#if !defined(CONFIG_XTENSA_MMU_DOUBLE_MAP) && defined(CONFIG_SOF_ZEPHYR_HEAP_CACHED) +#define HEAP_PART_CACHED + /* Add cached module private heap to memory partitions */ + struct k_mem_partition heap_cached_part = { .attr = K_MEM_PARTITION_P_RW_U_RW }; + + k_mem_region_align(&heap_cached_part.start, &heap_cached_part.size, + POINTER_TO_UINT(sys_cache_cached_ptr_get(drv->user_heap->init_mem)), + drv->user_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); +#endif + + struct k_mem_partition *parts_ptr[] = { +#ifdef HEAP_PART_CACHED + &heap_part_cached, +#endif + &heap_part + }; + + return k_mem_domain_init(user->comp_dom, ARRAY_SIZE(parts_ptr), parts_ptr); +} + +static int userspace_proxy_add_sections(struct userspace_context *user, uint32_t instance_id, + const struct sof_man_module *const mod) +{ + struct k_mem_partition mem_partition; + void *va_base; + int idx, ret; + + for (idx = 0; idx < ARRAY_SIZE(mod->segment); ++idx) { + if (!mod->segment[idx].flags.r.load) + continue; + + if (mod->segment[idx].flags.r.code) + mem_partition.attr = K_MEM_PARTITION_P_RX_U_RX; + else if (!mod->segment[idx].flags.r.readonly) + mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW; + else + mem_partition.attr = K_MEM_PARTITION_P_RO_U_RO; + + 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); + + 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); + + if (ret < 0) + return ret; + } + + 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); + + 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); + + return ret; +} + +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, + const void **agent_interface, const struct module_interface **ops) +{ + struct userspace_context *user; + struct k_mem_domain *domain; + int ret; + + tr_dbg(&userspace_proxy_tr, "userspace create"); + + user = sys_heap_alloc(drv->user_heap, sizeof(struct userspace_context)); + if (!user) + return -ENOMEM; + + /* Allocate memory domain struct */ + domain = rzalloc(SOF_MEM_FLAG_KERNEL, sizeof(*domain)); + if (!domain) { + ret = -ENOMEM; + goto error; + } + user->comp_dom = domain; + + ret = userspace_proxy_memory_init(user, drv); + if (ret) + goto error_dom; + + ret = userspace_proxy_add_sections(user, agent_params->instance_id, manifest); + if (ret) + goto error_dom; + + /* Start the system agent, if provided. */ + if (start_fn) { + ret = start_fn(agent_params, agent_interface); + + if (ret) { + tr_err(&userspace_proxy_tr, "System agent failed with error %d.", ret); + goto error_dom; + } + } + + *user_ctx = user; + + /* 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 + * points to ops, the assignment of the module interface used by this proxy must occur + * after the agent has been started. For other module types, the ops parameter points to a + * valid module interface. + */ + user->interface = *ops; + + /* All calls to the module interface must pass through the proxy. Set up our own interface. + */ + *ops = &userspace_proxy_interface; + + return 0; + +error_dom: + rfree(domain); +error: + sys_heap_free(drv->user_heap, user); + return ret; +} + +void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx) +{ + tr_dbg(&userspace_proxy_tr, "userspace proxy destroy"); + rfree(user_ctx->comp_dom); + sys_heap_free(drv->user_heap, user_ctx); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module init() operation and return its result. + * Module init() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_init(struct processing_module *mod) +{ + comp_dbg(mod->dev, "start"); + + /* Return status from module code operation. */ + return mod->user_ctx->interface->init(mod); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module prepare() operation and return its result. + * Module prepare() code is performed in user workqueue. + * + * @return 0 for success, error otherwise. + */ +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) +{ + 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); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module init() operation and return its result. + * Module init() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +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) +{ + return mod->user_ctx->interface->process(mod, sources, num_of_sources, sinks, num_of_sinks); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module reset() operation and return its result. + * Module reset() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_reset(struct processing_module *mod) +{ + if (!mod->user_ctx->interface->reset) + return 0; + + return mod->user_ctx->interface->reset(mod); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module free() operation and return its result. + * Module free() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_free(struct processing_module *mod) +{ + int ret = 0; + + comp_dbg(mod->dev, "start"); + + if (mod->user_ctx->interface->free) + ret = mod->user_ctx->interface->free(mod); + + userspace_proxy_destroy(mod->dev->drv, mod->user_ctx); + mod->user_ctx = NULL; + + /* Return status from module code operation. */ + return ret; +} + +/** + * Copy parameters to user worker accessible space. + * Queue module set_configuration() operation and return its result. + * Module set_configuration() code is performed in user workqueue. + * + * @param[in] mod - struct processing_module pointer + * @param[in] config_id - Configuration ID + * @param[in] pos - position of the fragment in the large message + * @param[in] data_offset_size: size of the whole configuration if it is the first fragment or the + * only fragment. Otherwise, it is the offset of the fragment in the whole + * configuration. + * @param[in] fragment: configuration fragment buffer + * @param[in] fragment_size: size of @fragment + * @params[in] response: optional response buffer to fill + * @params[in] response_size: size of @response + * + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_set_configuration(struct processing_module *mod, uint32_t config_id, + enum module_cfg_fragment_position pos, + uint32_t data_offset_size, const uint8_t *fragment, + size_t fragment_size, uint8_t *response, + size_t response_size) +{ + 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); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module get_configuration() operation and return its result. + * Module get_configuration() code is performed in user workqueue. + * + * @param[in] mod - struct processing_module pointer + * @param[in] config_id - Configuration ID + * @param[in] data_offset_size: size of the whole configuration if it is the first fragment or the + * only fragment. Otherwise, it is the offset of the fragment in the whole + * configuration. + * @param[in] fragment: configuration fragment buffer + * @param[in] fragment_size: size of @fragment + * + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_get_configuration(struct processing_module *mod, uint32_t config_id, + uint32_t *data_offset_size, uint8_t *fragment, + size_t fragment_size) +{ + 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); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module set_processing_mode() operation and return its result. + * Module set_processing_mode() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @param mode - processing mode to be set. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_set_processing_mode(struct processing_module *mod, + enum module_processing_mode mode) +{ + 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); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module get_processing_mode() operation and return its result. + * Module get_processing_mode() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return processing mode. + */ +static +enum module_processing_mode userspace_proxy_get_processing_mode(struct processing_module *mod) +{ + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->get_processing_mode) + return -EIO; + + return mod->user_ctx->interface->get_processing_mode(mod); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module is_ready_to_process() operation and return its result. + * Module is_ready_to_process() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return true if the module is ready to process + */ +static bool userspace_proxy_is_ready_to_process(struct processing_module *mod, + struct sof_source **sources, + int num_of_sources, + struct sof_sink **sinks, + int num_of_sinks) +{ + 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); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module bind() operation and return its result. + * Module bind() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @param bind_data - pointer to bind_info structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_bind(struct processing_module *mod, struct bind_info *bind_data) +{ + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->bind) + return 0; + + return mod->user_ctx->interface->bind(mod, bind_data); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module unbind() operation and return its result. + * Module unbind() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @param unbind_data - pointer to bind_info structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_unbind(struct processing_module *mod, struct bind_info *unbind_data) +{ + comp_dbg(mod->dev, "start"); + + if (!mod->user_ctx->interface->unbind) + return 0; + + return mod->user_ctx->interface->unbind(mod, unbind_data); +} + +/** + * Copy parameters to user worker accessible space. + * Queue module trigger() operation and return its result. + * Module trigger() code is performed in user workqueue. + * + * @param mod - pointer to processing module structure. + * @return 0 for success, error otherwise. + */ +static int userspace_proxy_trigger(struct processing_module *mod, int cmd) +{ + int ret = 0; + + comp_dbg(mod->dev, "start"); + + if (mod->user_ctx->interface->trigger) + ret = mod->user_ctx->interface->trigger(mod, cmd); + + if (!ret) + ret = module_adapter_set_state(mod, mod->dev, cmd); + + /* Return status from module code operation. */ + return ret; +} + +/* Userspace Module Adapter 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, + .prepare = userspace_proxy_prepare, + .process = userspace_proxy_process, + .set_configuration = userspace_proxy_set_configuration, + .get_configuration = userspace_proxy_get_configuration, + .set_processing_mode = userspace_proxy_set_processing_mode, + .get_processing_mode = userspace_proxy_get_processing_mode, + .reset = userspace_proxy_reset, + .free = userspace_proxy_free, + .bind = userspace_proxy_bind, + .unbind = userspace_proxy_unbind, + .trigger = userspace_proxy_trigger, +}; 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 52381ac7c175..150812183a90 100644 --- a/src/include/sof/audio/module_adapter/library/userspace_proxy.h +++ b/src/include/sof/audio/module_adapter/library/userspace_proxy.h @@ -14,12 +14,52 @@ #include #include +#include + +#include +#include + +struct module_interface; +struct comp_driver; +struct sof_man_module; +struct system_agent_params; /* Processing module structure fields needed for user mode */ struct userspace_context { struct k_mem_domain *comp_dom; /* Module specific memory domain */ + const struct module_interface *interface; /* Userspace module interface */ }; - #endif /* CONFIG_USERSPACE */ +#if CONFIG_SOF_USERSPACE_PROXY +/** + * Creates userspace module proxy + * + * @param user_ctx - pointer to pointer of userspace module context + * @param drv - pointer to component driver + * @param manifest - pointer to module manifest + * @param start_fn - pointer to system agent start function + * @param agent_params - pointer to system_agent_params + * @param agent_interface - pointer to variable to store module interface created by agent + * @param ops - Pointer to a variable that will hold the address of the module interface + * structure. The function stores a pointer to its own userspace proxy + * interface structure in this variable. + * + * @return 0 for success, error otherwise. + */ +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, + const void **agent_interface, const struct module_interface **ops); + +/** + * Destroy userspace module proxy + * + * @param drv - pointer to component driver + * @param user_ctx - pointer to userspace module context + */ +void userspace_proxy_destroy(const struct comp_driver *drv, struct userspace_context *user_ctx); + +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + #endif /* __SOF_AUDIO_USERSPACE_PROXY_H__ */ diff --git a/uuid-registry.txt b/uuid-registry.txt index 99a1a34df3db..84ded352b44f 100644 --- a/uuid-registry.txt +++ b/uuid-registry.txt @@ -170,6 +170,7 @@ c51dc642-a2e1-48df-a490e2748cb6363e tflmcly 04e3f894-2c5c-4f2e-8dc1694eeaab53fa tone e93326d8-0d14-4bf0-bcb9e063d3d80136 twb_sched 42f8060c-832f-4dbf-b24751e961997b34 up_down_mixer +6f6b6f4b-6f73-7466-20e1e62b9779f003 userspace_proxy b77e677e-5ff4-4188-af14fba8bdbf8682 volume 8a171323-94a3-4e1d-afe9fe5dbaa4c393 volume4 1028070e-04e8-46ab-8d8110a0116ce738 wait diff --git a/zephyr/Kconfig b/zephyr/Kconfig index b94af9bd4381..854eb211e904 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -84,6 +84,14 @@ config SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE module has its own independent heap to which only it has access. This heap is shared between instances of the same module. +config SOF_USERSPACE_PROXY + bool "Use userspace proxy to support userspace modules" + select SOF_USERSPACE_USE_DRIVER_HEAP + help + When set, userspace modules are launched inside a container created by 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 ZEPHYR_NATIVE_DRIVERS bool "Use Zephyr native drivers" help From 91462c1c22dddd27957fcfe756cf89b311f875e1 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Wed, 5 Mar 2025 12:15:06 +0100 Subject: [PATCH 5/6] module_adapter: Add user_ctx parameter to module_adapter_new_ext Extend the module_adapter_new_ext function with an additional user_ctx parameter. This enables the caller to explicitly set the userspace context field within the processing module structure during module adapter initialization. Signed-off-by: Adrian Warecki --- src/audio/module_adapter/module_adapter.c | 12 +++++++----- .../sof/audio/module_adapter/module/generic.h | 3 ++- src/library_manager/lib_manager.c | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/audio/module_adapter/module_adapter.c b/src/audio/module_adapter/module_adapter.c index 65ffd97ac18b..05f7bc705599 100644 --- a/src/audio/module_adapter/module_adapter.c +++ b/src/audio/module_adapter/module_adapter.c @@ -41,7 +41,7 @@ LOG_MODULE_REGISTER(module_adapter, CONFIG_SOF_LOG_LEVEL); struct comp_dev *module_adapter_new(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec) { - return module_adapter_new_ext(drv, config, spec, NULL); + return module_adapter_new_ext(drv, config, spec, NULL, NULL); } static struct processing_module *module_adapter_mem_alloc(const struct comp_driver *drv, @@ -108,7 +108,7 @@ static void module_adapter_mem_free(struct processing_module *mod) */ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec, - void *mod_priv) + void *mod_priv, struct userspace_context *user_ctx) { int ret; struct module_config *dst; @@ -127,14 +127,16 @@ struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, if (!mod) return NULL; - dst = &mod->priv.cfg; module_set_private_data(mod, mod_priv); + list_init(&mod->raw_data_buffers_list); +#if CONFIG_USERSPACE + mod->user_ctx = user_ctx; +#endif /* CONFIG_USERSPACE */ struct comp_dev *dev = mod->dev; - list_init(&mod->raw_data_buffers_list); - + dst = &mod->priv.cfg; ret = module_adapter_init_data(dev, dst, config, spec); if (ret) { comp_err(dev, "%d: module init data failed", diff --git a/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index d91627d2e135..484c0d6856f7 100644 --- a/src/include/sof/audio/module_adapter/module/generic.h +++ b/src/include/sof/audio/module_adapter/module/generic.h @@ -300,9 +300,10 @@ int module_unbind(struct processing_module *mod, struct bind_info *unbind_data); struct comp_dev *module_adapter_new(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec); +struct userspace_context; struct comp_dev *module_adapter_new_ext(const struct comp_driver *drv, const struct comp_ipc_config *config, const void *spec, - void *mod_priv); + void *mod_priv, struct userspace_context *user_ctx); int module_adapter_prepare(struct comp_dev *dev); int module_adapter_params(struct comp_dev *dev, struct sof_ipc_stream_params *params); int module_adapter_copy(struct comp_dev *dev); diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index d3404724d8bb..da88d8967719 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -645,7 +645,7 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, if (ops && comp_set_adapter_ops(drv, ops) < 0) goto err; - dev = module_adapter_new_ext(drv, config, spec, adapter_priv); + dev = module_adapter_new_ext(drv, config, spec, adapter_priv, NULL); if (!dev) goto err; From a9a440334b532c3d758d9f786303b0b1d83bd981 Mon Sep 17 00:00:00 2001 From: Adrian Warecki Date: Mon, 7 Jul 2025 19:35:32 +0200 Subject: [PATCH 6/6] ptl: mmu: Enable module data processing in userspace Introduce support for loading userspace modules. Allow the DP thread to process audio data in userspace. Signed-off-by: Jaroslaw Stelter Signed-off-by: Adrian Warecki --- src/audio/module_adapter/module/modules.c | 3 +- src/library_manager/lib_manager.c | 80 +++++++++++++++++------ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/audio/module_adapter/module/modules.c b/src/audio/module_adapter/module/modules.c index 3bb34c449478..66f257f62c11 100644 --- a/src/audio/module_adapter/module/modules.c +++ b/src/audio/module_adapter/module/modules.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -201,7 +202,7 @@ static int modules_reset(struct processing_module *mod) } /* Processing Module Adapter API*/ -const struct module_interface processing_module_adapter_interface = { +APP_TASK_DATA const struct module_interface processing_module_adapter_interface = { .init = modules_init, .prepare = modules_prepare, .process = modules_process, diff --git a/src/library_manager/lib_manager.c b/src/library_manager/lib_manager.c index da88d8967719..8bdc8545c808 100644 --- a/src/library_manager/lib_manager.c +++ b/src/library_manager/lib_manager.c @@ -3,7 +3,8 @@ // Copyright(c) 2022 Intel Corporation. All rights reserved. // // Author: Jaroslaw Stelter -// Pawel Dobrowolski +// Pawel Dobrowolski +// Adrian Warecki /* * Dynamic module loading functions. @@ -18,11 +19,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -492,14 +495,19 @@ const struct sof_man_module *lib_manager_get_module_manifest(const uint32_t modu * \param[in] module_entry_point - Entry point address of the module. * \param[in] agent - Function pointer to the system agent start function. * \param[out] agent_interface - Pointer to store the module interface returned by the system agent. + * \param[out] userspace - Pointer to store the userspace module proxy context. + * \param[out] ops - Pointer to store pointer to the module interface structure. * * \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, + const struct sof_man_module *mod_manifest, const struct ipc_config_process *args, const uintptr_t module_entry_point, const system_agent_start_fn agent, - const void **agent_interface) + const void **agent_interface, + struct userspace_context **userspace, + const struct module_interface **ops) { struct system_agent_params agent_params; byte_array_t mod_cfg; @@ -516,6 +524,17 @@ static int lib_manager_start_agent(const struct comp_driver *drv, const uint32_t agent_params.log_handle = (uint32_t)drv->tctx; agent_params.mod_cfg = &mod_cfg; +#if CONFIG_SOF_USERSPACE_PROXY + /* If drv->user_heap is allocated, it means the module is userspace. */ + if (drv->user_heap) { + ret = userspace_proxy_create(userspace, drv, mod_manifest, agent, &agent_params, + agent_interface, ops); + if (ret) + tr_err(&lib_manager_tr, "userspace_proxy_create failed! %d", ret); + return ret; + } +#endif /* CONFIG_SOF_USERSPACE_PROXY */ + ret = agent(&agent_params, agent_interface); if (ret) tr_err(&lib_manager_tr, "System agent start failed %d!", ret); @@ -584,7 +603,8 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, const struct sof_man_fw_desc *const desc = lib_manager_get_library_manifest(config->id); const struct ipc_config_process *args = (const struct ipc_config_process *)spec; const uint32_t entry_index = LIB_MANAGER_GET_MODULE_INDEX(config->id); - const struct module_interface *ops = NULL; + struct userspace_context *userspace = NULL; + const struct module_interface *ops; const struct sof_man_module *mod; system_agent_start_fn agent; void *adapter_priv = NULL; @@ -592,6 +612,13 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, struct comp_dev *dev; int ret; +#ifdef CONFIG_SOF_USERSPACE_PROXY + if (drv->user_heap && config->proc_domain != COMP_PROCESSING_DOMAIN_DP) { + tr_err(&lib_manager_tr, "Userspace supports only DP modules."); + return NULL; + } +#endif + tr_dbg(&lib_manager_tr, "start"); if (!desc) { tr_err(&lib_manager_tr, "Error: Couldn't find loadable module with id %u.", @@ -623,35 +650,39 @@ static struct comp_dev *lib_manager_module_create(const struct comp_driver *drv, agent = &native_system_agent_start; agent_iface = (const void **)&ops; break; +#if CONFIG_INTEL_MODULES case MOD_TYPE_IADK: agent = &system_agent_start; - /* processing_module_adapter_interface is already assigned to drv->adapter_ops by - * the lib_manager_prepare_module_adapter function. - */ + ops = &processing_module_adapter_interface; agent_iface = (const void **)&adapter_priv; break; +#endif case MOD_TYPE_INVALID: goto err; } /* At this point module resources are allocated and it is moved to L2 memory. */ if (agent) { - ret = lib_manager_start_agent(drv, config->id, args, module_entry_point, agent, - agent_iface); + ret = lib_manager_start_agent(drv, config->id, mod, args, module_entry_point, agent, + agent_iface, &userspace, &ops); if (ret) goto err; } - if (ops && comp_set_adapter_ops(drv, ops) < 0) + if (comp_set_adapter_ops(drv, ops) < 0) goto err; - dev = module_adapter_new_ext(drv, config, spec, adapter_priv, NULL); + dev = module_adapter_new_ext(drv, config, spec, adapter_priv, userspace); if (!dev) goto err; return dev; err: +#if CONFIG_SOF_USERSPACE_PROXY + if (userspace) + userspace_proxy_destroy(drv, userspace); +#endif /* CONFIG_SOF_USERSPACE_PROXY */ lib_manager_free_module(config->id); return NULL; } @@ -698,18 +729,16 @@ static void lib_manager_prepare_module_adapter(struct comp_driver *drv, const st drv->ops.dai_ts_start = module_adapter_ts_start_op; drv->ops.dai_ts_stop = module_adapter_ts_stop_op; drv->ops.dai_ts_get = module_adapter_ts_get_op; -#if CONFIG_INTEL_MODULES - drv->adapter_ops = &processing_module_adapter_interface; -#endif } int lib_manager_register_module(const uint32_t component_id) { - struct comp_driver_info *new_drv_info; - struct comp_driver *drv = NULL; const struct sof_man_module *mod = lib_manager_get_module_manifest(component_id); const struct sof_uuid *uid = (struct sof_uuid *)&mod->uuid; - int ret; + struct comp_driver_info *new_drv_info; + struct sys_heap *drv_heap = NULL; + struct comp_driver *drv = NULL; + int ret = -ENOMEM; if (!mod) { tr_err(&lib_manager_tr, "Error: Couldn't find loadable module with id %u.", @@ -726,13 +755,23 @@ int lib_manager_register_module(const uint32_t component_id) return -ENOMEM; } - drv = rzalloc(SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, - sizeof(struct comp_driver)); +#if CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP + if (mod->type.user_mode) { + drv_heap = module_driver_heap_init(); + if (!drv_heap) { + tr_err(&lib_manager_tr, "failed to allocate driver heap!"); + goto cleanup; + } + } +#endif /* CONFIG_SOF_USERSPACE_USE_DRIVER_HEAP */ + + drv = module_driver_heap_rmalloc(drv_heap, SOF_MEM_FLAG_KERNEL | SOF_MEM_FLAG_COHERENT, + sizeof(struct comp_driver)); if (!drv) { tr_err(&lib_manager_tr, "failed to allocate comp_driver"); - ret = -ENOMEM; goto cleanup; } + drv->user_heap = drv_heap; lib_manager_prepare_module_adapter(drv, uid); @@ -745,8 +784,9 @@ int lib_manager_register_module(const uint32_t component_id) cleanup: if (ret < 0) { - rfree(drv); rfree(new_drv_info); + module_driver_heap_free(drv_heap, drv); + module_driver_heap_remove(drv_heap); } return ret;