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/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/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/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/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/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/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/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/src/include/sof/audio/module_adapter/module/generic.h b/src/include/sof/audio/module_adapter/module/generic.h index c1e730893517..484c0d6856f7 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. * @@ -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 9e7de75a88ee..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,18 +495,21 @@ 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) { - 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 +517,25 @@ 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; + +#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); @@ -580,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; @@ -588,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.", @@ -619,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); + 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; } @@ -694,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.", @@ -722,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); @@ -741,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; 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 1314454a0466..854eb211e904 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 @@ -76,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 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; }