From 3a1c0e3d478f4cbaee50de84acbae2d8f305215b Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 19 Feb 2026 14:37:07 +0200 Subject: [PATCH 1/4] zephyr: userspace: sof_dma: allow circular SG lists Allow a non-null pointer at the end of the DMA transfer block list, if and only if it points to the first entry in the block list. The SOF DAI module sets the DMA transfers blocks like this and this change is required to use DAI module from user-space. Signed-off-by: Kai Vehmanen --- zephyr/syscall/sof_dma.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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; From 3daef23ba959aa9d0dc6dd6136cfb452e906b556 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 17 Feb 2026 13:45:44 +0200 Subject: [PATCH 2/4] lib: dai: make dai_get() and dai_put() compatible with user-space The dai_get()/dai_put() provide a helper to access DAI devices. When used in user-space, the wrapper struct should be created in user-space memory. Signed-off-by: Kai Vehmanen --- src/lib/dai.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/lib/dai.c b/src/lib/dai.c index e9e07b9202f8..7a8e44087a65 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 @@ -317,6 +318,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) { @@ -325,10 +331,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; @@ -338,7 +346,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; } @@ -349,6 +357,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) { @@ -356,7 +369,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) From ce84b751b9be76e9989a9279805e6118e88b82b2 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 21 May 2026 20:04:03 +0300 Subject: [PATCH 3/4] audio: component: move mod_alloc_ctx definition to new sof/ctx_alloc.h mod_alloc_ctx is needed by many components and also soon by audio components, so defining it in component.h will create circular dependencies. To clean this up, move definition of struct mod_alloc_ctx into the recently added ctx_alloc.h header. Signed-off-by: Kai Vehmanen --- src/include/sof/audio/component.h | 8 +------- src/include/sof/ctx_alloc.h | 6 +++++- src/include/sof/lib/dai-zephyr.h | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 7fc9feb53736..67b0cfcfe61a 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -579,13 +580,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/ctx_alloc.h b/src/include/sof/ctx_alloc.h index b292a73975a1..389c16a27507 100644 --- a/src/include/sof/ctx_alloc.h +++ b/src/include/sof/ctx_alloc.h @@ -6,13 +6,17 @@ #ifndef __SOF_CTX_ALLOC_H__ #define __SOF_CTX_ALLOC_H__ -#include #include #include #include #include #include +struct mod_alloc_ctx { + struct k_heap *heap; + struct vregion *vreg; +}; + /** * Allocate memory from a mod_alloc_ctx context. * diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index d25474c4816d..5f08929f74b0 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include From a62ab15e1cefa2b012c1a3bdd0a35281b7fb7bcb Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 5 May 2026 12:39:26 +0300 Subject: [PATCH 4/4] audio: dai-zephyr: make memory allocations user-space compatible Convert all memory allocations to use the sof_heap_alloc() interface and pass the dai_data specific heap object. This makes dai-zephyr code compatible with use from user-space, but does not affect kernel space use. Signed-off-by: Kai Vehmanen --- src/audio/dai-zephyr.c | 49 +++++++++++++++++++++----------- src/include/sof/lib/dai-zephyr.h | 3 ++ src/ipc/ipc4/dai.c | 6 ++-- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 0a9d92134cf7..e49d3e0f0122 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -537,6 +537,17 @@ __cold int dai_common_new(struct dai_data *dd, struct comp_dev *dev, 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 = sof_sys_user_heap_get(); +#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 */ @@ -589,6 +600,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(); @@ -601,10 +613,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); @@ -618,7 +632,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; @@ -646,7 +660,7 @@ __cold void dai_common_free(struct dai_data *dd) 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) @@ -660,7 +674,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); } @@ -855,7 +870,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, @@ -881,8 +896,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; @@ -911,10 +927,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; } @@ -1048,7 +1065,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) { @@ -1136,8 +1153,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; } @@ -1263,10 +1280,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; } diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index 5f08929f74b0..595d11de9b47 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -169,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 bc8b0043528a..58c08d9ec04b 100644 --- a/src/ipc/ipc4/dai.c +++ b/src/ipc/ipc4/dai.c @@ -386,15 +386,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; }