diff --git a/posix/include/rtos/mutex.h b/posix/include/rtos/mutex.h index 19b360bdaea5..85216c7d8acc 100644 --- a/posix/include/rtos/mutex.h +++ b/posix/include/rtos/mutex.h @@ -62,4 +62,31 @@ static inline int sys_mutex_unlock(struct sys_mutex *mutex) return 0; } +/** + * @brief User-space accessible mutex stub for host/testbench builds. + */ +struct sof_umutex { + struct k_mutex mutex; /**< Inline k_mutex for POSIX (no dynamic alloc needed) */ +}; + +static inline int sof_umutex_init(struct sof_umutex *umutex) +{ + return k_mutex_init(&umutex->mutex); +} + +static inline int sof_umutex_lock(struct sof_umutex *umutex, k_timeout_t timeout) +{ + return k_mutex_lock(&umutex->mutex, timeout); +} + +static inline int sof_umutex_unlock(struct sof_umutex *umutex) +{ + return k_mutex_unlock(&umutex->mutex); +} + +static inline void sof_umutex_free(struct sof_umutex *umutex) +{ + /* No-op on POSIX — no kernel objects to free */ +} + #endif diff --git a/posix/include/rtos/umutex.h b/posix/include/rtos/umutex.h new file mode 100644 index 000000000000..1b2007cf059a --- /dev/null +++ b/posix/include/rtos/umutex.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// + +#ifndef __POSIX_RTOS_UMUTEX_H__ +#define __POSIX_RTOS_UMUTEX_H__ + +#include + +/* sof_umutex type and operations are defined in rtos/mutex.h for POSIX */ + +#endif /* __POSIX_RTOS_UMUTEX_H__ */ diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 454132de150e..f7e0a0a2936b 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -23,6 +23,7 @@ #include #include #include +#include /* zephyr_ll_user_heap() */ #include #include #include @@ -225,55 +226,62 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, /* called from ipc/ipc3/dai.c */ int dai_get_handshake(struct dai *dai, int direction, int stream_id) { - k_spinlock_key_t key = k_spin_lock(&dai->lock); - const struct dai_properties *props = dai_get_properties(dai->dev, direction, - stream_id); - int hs_id = props->dma_hs_id; + struct dai_properties props; + int ret; - k_spin_unlock(&dai->lock, key); + sof_umutex_lock(&dai->lock, K_FOREVER); + ret = dai_get_properties_copy(dai->dev, direction, stream_id, &props); + sof_umutex_unlock(&dai->lock); + if (ret < 0) + return ret; - return hs_id; + return props.dma_hs_id; } /* called from ipc/ipc3/dai.c and ipc/ipc4/dai.c */ int dai_get_fifo_depth(struct dai *dai, int direction) { - const struct dai_properties *props; - k_spinlock_key_t key; - int fifo_depth; + struct dai_properties props; + int ret; if (!dai) return 0; - key = k_spin_lock(&dai->lock); - props = dai_get_properties(dai->dev, direction, 0); - fifo_depth = props->fifo_depth; - k_spin_unlock(&dai->lock, key); + sof_umutex_lock(&dai->lock, K_FOREVER); + ret = dai_get_properties_copy(dai->dev, direction, 0, &props); + sof_umutex_unlock(&dai->lock); + if (ret < 0) + return 0; - return fifo_depth; + return props.fifo_depth; } int dai_get_stream_id(struct dai *dai, int direction) { - k_spinlock_key_t key = k_spin_lock(&dai->lock); - const struct dai_properties *props = dai_get_properties(dai->dev, direction, 0); - int stream_id = props->stream_id; + struct dai_properties props; + int ret; - k_spin_unlock(&dai->lock, key); + sof_umutex_lock(&dai->lock, K_FOREVER); + ret = dai_get_properties_copy(dai->dev, direction, 0, &props); + sof_umutex_unlock(&dai->lock); + if (ret < 0) + return ret; - return stream_id; + return props.stream_id; } static int dai_get_fifo(struct dai *dai, int direction, int stream_id) { - k_spinlock_key_t key = k_spin_lock(&dai->lock); - const struct dai_properties *props = dai_get_properties(dai->dev, direction, - stream_id); - int fifo_address = props->fifo_address; + struct dai_properties props; + int ret; - k_spin_unlock(&dai->lock, key); + sof_umutex_lock(&dai->lock, K_FOREVER); + ret = dai_get_properties_copy(dai->dev, direction, stream_id, &props); + sof_umutex_unlock(&dai->lock); + if (ret < 0) + return ret; - return fifo_address; + return props.fifo_address; } /* this is called by DMA driver every time descriptor has completed */ @@ -501,6 +509,7 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, const struct ipc_config_dai *dai_cfg) { uint32_t dir; + int ret; assert_can_be_cold(); @@ -524,11 +533,27 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, return -ENODEV; } - k_spinlock_init(&dd->dai->lock); + ret = sof_umutex_init(&dd->dai->lock); + if (ret < 0) { + dai_put(dd->dai); + comp_err(dev, "sof_umutex_init() failed: %d", ret); + return ret; + } dma_sg_init(&dd->config.elem_array); dd->xrun = 0; +#ifdef CONFIG_SOF_USERSPACE_LL + /* + * copier_dai_create() uses mod_zalloc() to allocate + * the 'dd' dai data object and does not set dd->alloc_ctx. + * If LL is run in user-space, assign the 'heap' here. + */ + dd->alloc_ctx.heap = zephyr_ll_user_heap(); +#else + dd->alloc_ctx.heap = NULL; +#endif + /* I/O performance init, keep it last so the function does not reach this in case * of return on error, so that we do not waste a slot */ @@ -581,6 +606,7 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, struct comp_dev *dev; const struct ipc_config_dai *dai_cfg = spec; struct dai_data *dd; + struct k_heap *heap = NULL; int ret; assert_can_be_cold(); @@ -593,10 +619,12 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, dev->ipc_config = *config; - dd = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd)); + dd = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(*dd), 0); if (!dd) goto e_data; + memset(dd, 0, sizeof(*dd)); + comp_set_drvdata(dev, dd); ret = dai_common_new(dd, dev, dai_cfg); @@ -610,7 +638,7 @@ __cold static struct comp_dev *dai_new(const struct comp_driver *drv, return dev; error: - rfree(dd); + sof_heap_free(heap, dd); e_data: comp_free_device(dev); return NULL; @@ -636,9 +664,11 @@ __cold void dai_common_free(struct dai_data *dd) dai_release_llp_slot(dd); + sof_umutex_free(&dd->dai->lock); + dai_put(dd->dai); - rfree(dd->dai_spec_config); + sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config); } __cold static void dai_free(struct comp_dev *dev) @@ -652,7 +682,8 @@ __cold static void dai_free(struct comp_dev *dev) dai_common_free(dd); - rfree(dd); + /* heap is NULL to match what is passed in dai_new() */ + sof_heap_free(NULL, dd); comp_free_device(dev); } @@ -847,7 +878,7 @@ static int dai_set_sg_config(struct dai_data *dd, struct comp_dev *dev, uint32_t } while (--max_block_count > 0); } - err = dma_sg_alloc(NULL, &config->elem_array, SOF_MEM_FLAG_USER, + err = dma_sg_alloc(dd->alloc_ctx.heap, &config->elem_array, SOF_MEM_FLAG_USER, config->direction, period_count, period_bytes, @@ -873,8 +904,9 @@ static int dai_set_dma_config(struct dai_data *dd, struct comp_dev *dev) comp_dbg(dev, "entry"); - dma_cfg = rmalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, - sizeof(struct dma_config)); + dma_cfg = sof_heap_alloc(dd->alloc_ctx.heap, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, + sizeof(struct dma_config), 0); if (!dma_cfg) { comp_err(dev, "dma_cfg allocation failed"); return -ENOMEM; @@ -903,10 +935,11 @@ static int dai_set_dma_config(struct dai_data *dd, struct comp_dev *dev) else dma_cfg->dma_slot = config->src_dev; - dma_block_cfg = rballoc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, - sizeof(struct dma_block_config) * dma_cfg->block_count); + dma_block_cfg = sof_heap_alloc(dd->alloc_ctx.heap, + SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT | SOF_MEM_FLAG_DMA, + sizeof(struct dma_block_config) * dma_cfg->block_count, 0); if (!dma_block_cfg) { - rfree(dma_cfg); + sof_heap_free(dd->alloc_ctx.heap, dma_cfg); comp_err(dev, "dma_block_config allocation failed"); return -ENOMEM; } @@ -1040,7 +1073,7 @@ static int dai_set_dma_buffer(struct dai_data *dd, struct comp_dev *dev, return err; } } else { - dd->dma_buffer = buffer_alloc_range(NULL, buffer_size_preferred, buffer_size, + dd->dma_buffer = buffer_alloc_range(&dd->alloc_ctx, buffer_size_preferred, buffer_size, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_DMA, addr_align, BUFFER_USAGE_NOT_SHARED); if (!dd->dma_buffer) { @@ -1128,8 +1161,8 @@ int dai_common_params(struct dai_data *dd, struct comp_dev *dev, if (err < 0) { buffer_free(dd->dma_buffer); dd->dma_buffer = NULL; - dma_sg_free(NULL, &config->elem_array); - rfree(dd->z_config); + dma_sg_free(dd->alloc_ctx.heap, &config->elem_array); + sof_heap_free(dd->alloc_ctx.heap, dd->z_config); dd->z_config = NULL; } @@ -1255,10 +1288,10 @@ void dai_common_reset(struct dai_data *dd, struct comp_dev *dev) if (!dd->delayed_dma_stop) dai_dma_release(dd, dev); - dma_sg_free(NULL, &config->elem_array); + dma_sg_free(dd->alloc_ctx.heap, &config->elem_array); if (dd->z_config) { - rfree(dd->z_config->head_block); - rfree(dd->z_config); + sof_heap_free(dd->alloc_ctx.heap, dd->z_config->head_block); + sof_heap_free(dd->alloc_ctx.heap, dd->z_config); dd->z_config = NULL; } @@ -1948,17 +1981,18 @@ static int dai_ts_stop_op(struct comp_dev *dev) uint32_t dai_get_init_delay_ms(struct dai *dai) { - const struct dai_properties *props; - k_spinlock_key_t key; - uint32_t init_delay; + struct dai_properties props; + uint32_t init_delay = 0; + int ret; if (!dai) return 0; - key = k_spin_lock(&dai->lock); - props = dai_get_properties(dai->dev, 0, 0); - init_delay = props->reg_init_delay; - k_spin_unlock(&dai->lock, key); + sof_umutex_lock(&dai->lock, K_FOREVER); + ret = dai_get_properties_copy(dai->dev, 0, 0, &props); + if (!ret) + init_delay = props.reg_init_delay; + sof_umutex_unlock(&dai->lock); return init_delay; } diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 7fc9feb53736..fed83de58374 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -37,6 +36,16 @@ struct dai_hw_params; struct timestamp_data; struct dai_ts_data; +struct k_heap; +struct vregion; +struct mod_alloc_ctx { + struct k_heap *heap; + struct vregion *vreg; +}; + +/* dai.h requires definition for mod_alloc_ctx */ +#include + /** \addtogroup component_api Component API * @{ */ @@ -579,13 +588,6 @@ struct comp_ops { uint64_t (*get_total_data_processed)(struct comp_dev *dev, uint32_t stream_no, bool input); }; -struct k_heap; -struct vregion; -struct mod_alloc_ctx { - struct k_heap *heap; - struct vregion *vreg; -}; - /** * Audio component base driver "class" * - used by all other component types. diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index d25474c4816d..6a0845555eaa 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -23,11 +23,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -52,7 +54,7 @@ struct dai { uint32_t dma_dev; const struct device *dev; const struct dai_data *dd; - struct k_spinlock lock; /* protect properties */ + struct sof_umutex lock; /* protect properties */ }; union hdalink_cfg { @@ -168,6 +170,8 @@ struct dai_data { #endif /* Copier gain params */ struct copier_gain_params *gain_data; + + struct mod_alloc_ctx alloc_ctx; }; /* these 3 are here to satisfy clk.c and ssp.h interconnection, will be removed leter */ diff --git a/src/ipc/ipc4/dai.c b/src/ipc/ipc4/dai.c index 9724e7578c99..87eb594aa6a9 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -387,15 +387,17 @@ __cold int dai_config(struct dai_data *dd, struct comp_dev *dev, /* allocated dai_config if not yet */ if (!dd->dai_spec_config) { size = sizeof(*copier_cfg); - dd->dai_spec_config = rzalloc(SOF_MEM_FLAG_USER, size); + dd->dai_spec_config = sof_heap_alloc(dd->alloc_ctx.heap, SOF_MEM_FLAG_USER, size, 0); if (!dd->dai_spec_config) { comp_err(dev, "No memory for size %d", size); return -ENOMEM; } + memset(dd->dai_spec_config, 0, size); + ret = memcpy_s(dd->dai_spec_config, size, copier_cfg, size); if (ret < 0) { - rfree(dd->dai_spec_config); + sof_heap_free(dd->alloc_ctx.heap, dd->dai_spec_config); dd->dai_spec_config = NULL; return -EINVAL; } diff --git a/src/lib/dai.c b/src/lib/dai.c index bd7c02edcb2d..8957bd1042f5 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -11,6 +11,7 @@ #include #include #include +#include /* for zephyr_ll_user_heap() */ #include #include #include @@ -311,6 +312,11 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) { const struct device *dev; struct dai *d; + struct k_heap *heap = NULL; + +#ifdef CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); +#endif dev = dai_get_device(type, index); if (!dev) { @@ -319,10 +325,12 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) return NULL; } - d = rzalloc(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(struct dai)); + d = sof_heap_alloc(heap, SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, sizeof(struct dai), 0); if (!d) return NULL; + memset(d, 0, sizeof(struct dai)); + d->index = index; d->type = type; d->dev = dev; @@ -332,7 +340,7 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) if (dai_probe(d->dev)) { tr_err(&dai_tr, "dai_get: failed to probe dai with index %d type %d", index, type); - rfree(d); + sof_heap_free(heap, d); return NULL; } @@ -343,6 +351,11 @@ struct dai *dai_get(uint32_t type, uint32_t index, uint32_t flags) void dai_put(struct dai *dai) { int ret; + struct k_heap *heap = NULL; + +#ifdef CONFIG_SOF_USERSPACE_LL + heap = zephyr_ll_user_heap(); +#endif ret = dai_remove(dai->dev); if (ret < 0) { @@ -350,7 +363,7 @@ void dai_put(struct dai *dai) dai->index, ret); } - rfree(dai); + sof_heap_free(heap, dai); } #else static inline const struct dai_type_info *dai_find_type(uint32_t type) diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 91598a776db0..649c0cb12604 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -549,6 +549,12 @@ if(CONFIG_SOF_USERSPACE_INTERFACE_DMA) zephyr_syscall_header(include/sof/lib/sof_dma.h) endif() +zephyr_library_sources(lib/umutex.c) +if(CONFIG_SOF_USERSPACE_INTERFACE_MUTEX) + zephyr_library_sources(syscall/umutex.c) + zephyr_syscall_header(include/rtos/umutex.h) +endif() + # Mandatory Files used on all platforms. # Commented files will be added/removed as integration dictates. zephyr_library_sources( diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f1d1896c4234..111e38b0e28b 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -29,10 +29,25 @@ config SOF_USERSPACE_INTERFACE_DMA help Allow user-space threads to use the SOF DMA interface. +config SOF_USERSPACE_INTERFACE_ALLOC + bool "Enable SOF heap alloc interface to userspace threads" + depends on USERSPACE + help + Allow user-space threads to use sof_heap_alloc/sof_heap_free + as Zephyr system calls. + +config SOF_USERSPACE_INTERFACE_MUTEX + bool "User-space mutex interface" + depends on USERSPACE + help + Enables the sof_umutex API for dynamically-allocated + user-space accessible mutexes backed by k_object_alloc. + config SOF_USERSPACE_LL bool "Run Low-Latency pipelines in userspace threads" depends on USERSPACE select SOF_USERSPACE_INTERFACE_DMA + select SOF_USERSPACE_INTERFACE_MUTEX help Run Low-Latency (LL) pipelines in userspace threads. This adds memory protection between operating system resources and diff --git a/zephyr/include/rtos/mutex.h b/zephyr/include/rtos/mutex.h index a8886f768d1e..bf1f25813888 100644 --- a/zephyr/include/rtos/mutex.h +++ b/zephyr/include/rtos/mutex.h @@ -8,5 +8,6 @@ #include /* k_mutex_*() */ #include /* for sys_mutex */ +#include /* for sof_umutex */ #endif /* __ZEPHYR_RTOS_MUTEX_H__ */ diff --git a/zephyr/include/rtos/umutex.h b/zephyr/include/rtos/umutex.h new file mode 100644 index 000000000000..5bea79f36181 --- /dev/null +++ b/zephyr/include/rtos/umutex.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. +// + +#ifndef __ZEPHYR_RTOS_UMUTEX_H__ +#define __ZEPHYR_RTOS_UMUTEX_H__ + +#include + +/** + * @brief User-space accessible mutex with dynamic allocation. + * + * This mutex variant can be dynamically allocated at runtime. The state + * struct resides in user-accessible memory; access to the mutex is granted + * to any thread that can access this memory region (no per-thread + * k_thread_access_grant needed). + * + * The backing k_mutex is allocated via k_object_alloc(K_OBJ_MUTEX) during + * init and freed via k_object_free() during free. + */ +struct sof_umutex { + struct k_mutex *mutex; /**< Pointer to dynamically-allocated backing k_mutex */ +}; + +/** + * @brief Initialize a dynamic user-space mutex. + * + * Allocates the backing k_mutex kernel object. The sof_umutex struct + * must reside in memory accessible to the calling thread. + * + * @param umutex Pointer to the sof_umutex state (in user-accessible memory) + * @return 0 on success, -ENOMEM if allocation fails + */ +__syscall int sof_umutex_init(struct sof_umutex *umutex); + +/** + * @brief Lock a dynamic user-space mutex. + * + * @param umutex Pointer to the sof_umutex state + * @param timeout Timeout value (K_FOREVER for indefinite wait) + * @return 0 on success, -EAGAIN on timeout, -EINVAL if not initialized + */ +__syscall int sof_umutex_lock(struct sof_umutex *umutex, k_timeout_t timeout); + +/** + * @brief Unlock a dynamic user-space mutex. + * + * @param umutex Pointer to the sof_umutex state + * @return 0 on success, -EINVAL if not initialized, -EPERM if not owner + */ +__syscall int sof_umutex_unlock(struct sof_umutex *umutex); + +/** + * @brief Free a dynamic user-space mutex. + * + * Releases the backing k_mutex kernel object via k_object_free(). + * The sof_umutex state must not be used after this call. + * + * @param umutex Pointer to the sof_umutex state + */ +__syscall void sof_umutex_free(struct sof_umutex *umutex); + +int z_impl_sof_umutex_init(struct sof_umutex *umutex); +int z_impl_sof_umutex_lock(struct sof_umutex *umutex, k_timeout_t timeout); +int z_impl_sof_umutex_unlock(struct sof_umutex *umutex); +void z_impl_sof_umutex_free(struct sof_umutex *umutex); + +#ifdef CONFIG_SOF_USERSPACE_INTERFACE_MUTEX +#include +#else +static inline int sof_umutex_init(struct sof_umutex *umutex) +{ + return z_impl_sof_umutex_init(umutex); +} + +static inline int sof_umutex_lock(struct sof_umutex *umutex, k_timeout_t timeout) +{ + return z_impl_sof_umutex_lock(umutex, timeout); +} + +static inline int sof_umutex_unlock(struct sof_umutex *umutex) +{ + return z_impl_sof_umutex_unlock(umutex); +} + +static inline void sof_umutex_free(struct sof_umutex *umutex) +{ + z_impl_sof_umutex_free(umutex); +} + +#endif /* CONFIG_SOF_USERSPACE_INTERFACE_MUTEX */ + +#endif /* __ZEPHYR_RTOS_UMUTEX_H__ */ diff --git a/zephyr/lib/umutex.c b/zephyr/lib/umutex.c new file mode 100644 index 000000000000..0a1f9d686650 --- /dev/null +++ b/zephyr/lib/umutex.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +/** + * @file + * @brief SOF dynamic user-space mutex implementation. + * + * Provides the kernel-side implementation of sof_umutex operations. + * The backing k_mutex is dynamically allocated via k_object_alloc + * and freed via k_object_free. + */ + +#include +#include +#include + +int z_impl_sof_umutex_init(struct sof_umutex *umutex) +{ + struct k_mutex *m; + int ret; + + m = k_object_alloc(K_OBJ_MUTEX); + if (m == NULL) { + return -ENOMEM; + } + + ret = k_mutex_init(m); + if (ret) { + k_object_free(m); + return ret; + } + + umutex->mutex = m; + return 0; +} + +int z_impl_sof_umutex_lock(struct sof_umutex *umutex, k_timeout_t timeout) +{ + if (umutex->mutex == NULL) { + return -EINVAL; + } + + return k_mutex_lock(umutex->mutex, timeout); +} + +int z_impl_sof_umutex_unlock(struct sof_umutex *umutex) +{ + if (umutex->mutex == NULL) { + return -EINVAL; + } + + return k_mutex_unlock(umutex->mutex); +} + +void z_impl_sof_umutex_free(struct sof_umutex *umutex) +{ + if (umutex->mutex != NULL) { + k_object_free(umutex->mutex); + umutex->mutex = NULL; + } +} diff --git a/zephyr/syscall/sof_dma.c b/zephyr/syscall/sof_dma.c index ed69ffc78423..f12a29aa1efb 100644 --- a/zephyr/syscall/sof_dma.c +++ b/zephyr/syscall/sof_dma.c @@ -109,19 +109,28 @@ static inline void z_vrfy_sof_dma_release_channel(struct sof_dma *dma, */ static inline struct dma_block_config *deep_copy_dma_blk_cfg_list(struct dma_config *cfg) { - struct dma_block_config *kern_cfg = - rmalloc(0, sizeof(*kern_cfg) * cfg->block_count); + struct dma_block_config *kern_cfg; struct dma_block_config *kern_prev = NULL, *kern_next, *user_next; int i = 0; + if (!cfg->block_count) + return NULL; + + kern_cfg = rmalloc(0, sizeof(*kern_cfg) * cfg->block_count); if (!kern_cfg) return NULL; for (user_next = cfg->head_block, kern_next = kern_cfg; user_next; - user_next = user_next->next_block, kern_next++) { - if (++i > cfg->block_count) - goto err; + user_next = user_next->next_block, kern_next++, i++) { + if (i == cfg->block_count) { + /* last block can point to first one */ + if (user_next != cfg->head_block) + goto err; + + kern_prev->next_block = kern_cfg; + break; + } if (k_usermode_from_copy(kern_next, user_next, sizeof(*kern_next))) goto err; diff --git a/zephyr/syscall/umutex.c b/zephyr/syscall/umutex.c new file mode 100644 index 000000000000..0692068e21eb --- /dev/null +++ b/zephyr/syscall/umutex.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2026 Intel Corporation. + +/** + * @file + * @brief SOF dynamic user-space mutex syscall verification. + * + * Verify handlers ensure the calling user thread has write access to + * the sof_umutex state struct before forwarding to the implementation. + */ + +#include +#include + +static inline int z_vrfy_sof_umutex_init(struct sof_umutex *umutex) +{ + K_OOPS(K_SYSCALL_MEMORY_WRITE(umutex, sizeof(struct sof_umutex))); + return z_impl_sof_umutex_init(umutex); +} +#include + +static inline int z_vrfy_sof_umutex_lock(struct sof_umutex *umutex, + k_timeout_t timeout) +{ + K_OOPS(K_SYSCALL_MEMORY_WRITE(umutex, sizeof(struct sof_umutex))); + return z_impl_sof_umutex_lock(umutex, timeout); +} +#include + +static inline int z_vrfy_sof_umutex_unlock(struct sof_umutex *umutex) +{ + K_OOPS(K_SYSCALL_MEMORY_WRITE(umutex, sizeof(struct sof_umutex))); + return z_impl_sof_umutex_unlock(umutex); +} +#include + +static inline void z_vrfy_sof_umutex_free(struct sof_umutex *umutex) +{ + K_OOPS(K_SYSCALL_MEMORY_WRITE(umutex, sizeof(struct sof_umutex))); + z_impl_sof_umutex_free(umutex); +} +#include diff --git a/zephyr/test/CMakeLists.txt b/zephyr/test/CMakeLists.txt index f548c98c5e73..fbf0f0a60253 100644 --- a/zephyr/test/CMakeLists.txt +++ b/zephyr/test/CMakeLists.txt @@ -8,6 +8,13 @@ if(CONFIG_SOF_BOOT_TEST) zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace/ksem.c ) + if(CONFIG_USERSPACE AND CONFIG_SOF_USERSPACE_INTERFACE_ALLOC) + zephyr_library_sources(userspace/test_heap_alloc.c) + endif() + + if(CONFIG_SOF_USERSPACE_INTERFACE_MUTEX) + zephyr_library_sources(userspace/test_umutex.c) + endif() endif() if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_SOF_USERSPACE_INTERFACE_DMA) diff --git a/zephyr/test/userspace/test_umutex.c b/zephyr/test/userspace/test_umutex.c new file mode 100644 index 000000000000..5f655f1063c2 --- /dev/null +++ b/zephyr/test/userspace/test_umutex.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright(c) 2026 Intel Corporation. + */ + +/** + * @file + * @brief Test case for sof_umutex API from a Zephyr user-space thread. + * + * Validates that sof_umutex_init/lock/unlock/free work correctly when + * called from user-space context. + */ + +#include +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_DECLARE(sof_boot_test, LOG_LEVEL_DBG); + +#define USER_STACKSIZE 2048 + +static struct k_thread umutex_user_thread; +static K_THREAD_STACK_DEFINE(umutex_user_stack, USER_STACKSIZE); + +/* Memory partition for test data accessible from user-space */ +K_APPMEM_PARTITION_DEFINE(umutex_test_part); + +/* Place the sof_umutex state in the user-accessible partition */ +K_APP_BMEM(umutex_test_part) static struct sof_umutex test_umutex; + +static void umutex_user_function(void *p1, void *p2, void *p3) +{ + int ret; + + __ASSERT(k_is_user_context(), "isn't user"); + + LOG_INF("umutex test thread %s (%s)", + k_is_user_context() ? "UserSpace!" : "privileged mode.", + CONFIG_BOARD_TARGET); + + /* Initialize the umutex — allocates backing k_mutex */ + ret = sof_umutex_init(&test_umutex); + zassert_equal(ret, 0, "sof_umutex_init failed: %d", ret); + + LOG_INF("sof_umutex_init succeeded"); + + /* Lock the mutex */ + ret = sof_umutex_lock(&test_umutex, K_FOREVER); + zassert_equal(ret, 0, "sof_umutex_lock failed: %d", ret); + + LOG_INF("sof_umutex_lock succeeded"); + + /* Unlock the mutex */ + ret = sof_umutex_unlock(&test_umutex); + zassert_equal(ret, 0, "sof_umutex_unlock failed: %d", ret); + + LOG_INF("sof_umutex_unlock succeeded"); + + /* Free the mutex — releases backing k_mutex */ + sof_umutex_free(&test_umutex); + + LOG_INF("sof_umutex_free done"); +} + +static void test_user_thread_umutex(void) +{ + /* Add test partition to LL memory domain so user thread can access test_umutex */ + k_mem_domain_add_partition(zephyr_ll_mem_domain(), &umutex_test_part); + + k_thread_create(&umutex_user_thread, umutex_user_stack, USER_STACKSIZE, + umutex_user_function, NULL, NULL, NULL, + -1, K_USER, K_FOREVER); + + /* Add thread to LL memory domain so it can access the partition */ + k_mem_domain_add_thread(zephyr_ll_mem_domain(), &umutex_user_thread); + + k_thread_start(&umutex_user_thread); + k_thread_join(&umutex_user_thread, K_FOREVER); + + k_mem_domain_remove_partition(zephyr_ll_mem_domain(), &umutex_test_part); +} + +ZTEST(sof_boot, user_space_umutex) +{ + test_user_thread_umutex(); + + ztest_test_pass(); +}