Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/include/sof/boot_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#endif
#include <stdbool.h>

struct k_thread;

#if CONFIG_SOF_BOOT_TEST
#define TEST_RUN_ONCE(fn, ...) do { \
static bool once; \
Expand All @@ -36,4 +38,18 @@

void sof_run_boot_tests(void);

/**
* Mark a boot-test thread as expected to trigger a fatal error.
*
* @param thread Thread that is allowed to fault once, or NULL to clear.
*/
#if CONFIG_SOF_BOOT_TEST
void sof_boot_test_set_fault_valid(struct k_thread *thread);
#else
static inline void sof_boot_test_set_fault_valid(struct k_thread *thread)
{
(void)thread;
}
#endif

#endif
1 change: 1 addition & 0 deletions src/include/sof/schedule/ll_schedule_domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ static inline struct ll_schedule_domain *dma_domain_get(void)
#ifdef CONFIG_SOF_USERSPACE_LL
struct task *zephyr_ll_task_alloc(void);
struct k_heap *zephyr_ll_user_heap(void);
bool zephyr_ll_user_heap_verify(struct k_heap *heap);
void zephyr_ll_user_resources_init(void);
#endif /* CONFIG_SOF_USERSPACE_LL */

Expand Down
85 changes: 73 additions & 12 deletions src/schedule/zephyr_ll_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,51 @@ LOG_MODULE_DECLARE(ll_schedule, CONFIG_SOF_LOG_LEVEL);
*
* This structure encapsulates the memory management resources required for the
* low-latency (LL) scheduler in userspace mode. It provides memory isolation
* and heap management for LL scheduler threads.
* and heap management for LL scheduler threads. Only kernel accessible.
*/
struct zephyr_ll_mem_resources {
struct k_mem_domain mem_domain; /**< Memory domain for LL thread isolation */
struct k_heap *heap; /**< Heap allocator for LL scheduler memory */
struct k_heap heap; /**< Heap allocator for LL scheduler memory */
};

static struct zephyr_ll_mem_resources ll_mem_resources;

static struct k_heap *zephyr_ll_heap_init(void)
/**
* Heap allocator for LL scheduler memory (user accessible pointer)
*
* Note: this is also user-writable, so kernel must not rely on this to
* be correct and must always validate it separately.
*/
APP_SYSUSER_DATA static struct k_heap *zephyr_ll_heap;

static struct k_heap *ll_heap_alloc(void)
{
struct k_heap *heap = module_driver_heap_init();
struct k_mem_partition mem_partition;
int ret;
const size_t alloc_size = CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE;

BUILD_ASSERT(CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE % CONFIG_MM_DRV_PAGE_SIZE == 0);

void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, alloc_size,
CONFIG_MM_DRV_PAGE_SIZE);
if (!mem)
return NULL;

k_heap_init(&ll_mem_resources.heap, mem, alloc_size);

/*
* TODO: the size of LL heap should be independently configurable and
* not tied to CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE
* k_heap_init() does not set these, so set the values
* manually here
*/
ll_mem_resources.heap.heap.init_mem = mem;
ll_mem_resources.heap.heap.init_bytes = alloc_size;

return &ll_mem_resources.heap;
}

static void ll_heap_init(void)
{
struct k_heap *heap = ll_heap_alloc();
struct k_mem_partition mem_partition;
int ret;

if (!heap) {
tr_err(&ll_tr, "heap alloc fail");
Expand All @@ -53,32 +79,67 @@ static struct k_heap *zephyr_ll_heap_init(void)
if (ret)
k_panic();

#ifdef CONFIG_CACHE_HAS_MIRRORED_MEMORY_REGIONS
mem_partition.start = (uintptr_t)sys_cache_uncached_ptr_get(heap->heap.init_mem);
mem_partition.attr = K_MEM_PARTITION_P_RW_U_RW;
ret = k_mem_domain_add_partition(&ll_mem_resources.mem_domain, &mem_partition);
tr_dbg(&ll_tr, "init ll heap %p, size %u (uncached), ret %d",
(void *)mem_partition.start, heap->heap.init_bytes, ret);
if (ret)
k_panic();

return heap;
#endif
}

void zephyr_ll_user_resources_init(void)
{
int ret;

k_mem_domain_init(&ll_mem_resources.mem_domain, 0, NULL);

ll_mem_resources.heap = zephyr_ll_heap_init();
ll_heap_init();

/* store a user-accessible pointer */
zephyr_ll_heap = &ll_mem_resources.heap;

/* attach common partition to LL domain */
user_memory_attach_common_partition(zephyr_ll_mem_domain());

ret = user_memory_attach_system_user_partition(zephyr_ll_mem_domain());
if (ret)
k_panic();
}

/**
* Check if 'heap' is a valid heap pointer.
*
* Available only in kernel mode.
*
* @return true if valid
*/
bool zephyr_ll_user_heap_verify(struct k_heap *heap)
{
return heap == &ll_mem_resources.heap;
}

/**
* Returns heap object to use in user-space LL code.
*
* Can be called from user-space.
*
* @return heap pointer that can be passed to sof_heap_alloc()
*/
struct k_heap *zephyr_ll_user_heap(void)
{
return ll_mem_resources.heap;
return zephyr_ll_heap;
}

/**
* Returns pointer to LL user-space memory domain.
*
* Available only in kernel mode.
*
* @return pointer to memory domain
*/
struct k_mem_domain *zephyr_ll_mem_domain(void)
{
return &ll_mem_resources.mem_domain;
Expand Down
2 changes: 2 additions & 0 deletions zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ zephyr_library_sources_ifdef(CONFIG_SHELL

zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/audio/module_adapter/module/generic.h)
zephyr_syscall_header(${SOF_SRC_PATH}/include/sof/lib/fast-get.h)
zephyr_syscall_header(include/rtos/alloc.h)
zephyr_library_sources_ifdef(CONFIG_SOF_USERSPACE_INTERFACE_ALLOC syscall/alloc.c)

zephyr_library_link_libraries(SOF)
target_link_libraries(SOF INTERFACE zephyr_interface)
Expand Down
19 changes: 19 additions & 0 deletions zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@ 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_LL
bool "Run Low-Latency pipelines in userspace threads"
depends on USERSPACE
select SOF_USERSPACE_INTERFACE_ALLOC
select SOF_USERSPACE_INTERFACE_DMA
help
Run Low-Latency (LL) pipelines in userspace threads. This adds
Expand Down Expand Up @@ -116,6 +124,17 @@ 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_ZEPHYR_SYS_USER_HEAP_SIZE
hex "Size of the shared LL user-space heap"
default 0x20000
depends on SOF_USERSPACE_LL
help
The size of the shared heap used by the low-latency (LL) scheduler
user-space thread. This heap is shared across all pipelines running
on the LL thread and must be large enough to hold all host DMA
buffers, chain DMA data, SG elements, and module adapter buffers
for all simultaneously active pipelines.

config SOF_USERSPACE_PROXY
bool "Use userspace proxy to support userspace modules"
select SOF_USERSPACE_USE_DRIVER_HEAP
Expand Down
17 changes: 17 additions & 0 deletions zephyr/include/rtos/alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,26 @@ void rfree(void *ptr);
*/
void l3_heap_save(void);

void *z_impl_sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
size_t alignment);

void z_impl_sof_heap_free(struct k_heap *heap, void *addr);

/*
* This is ugly to define the signatures twice, but this
* is required to support userspace builds that do not export alloc.
*/
#ifdef CONFIG_SOF_USERSPACE_INTERFACE_ALLOC
__syscall void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
size_t alignment);
__syscall void sof_heap_free(struct k_heap *heap, void *addr);
#include <zephyr/syscalls/alloc.h>
#else
void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
size_t alignment);
void sof_heap_free(struct k_heap *heap, void *addr);
#endif

#if CONFIG_SOF_FULL_ZEPHYR_APPLICATION
struct k_heap *sof_sys_heap_get(void);
#else
Expand Down
28 changes: 25 additions & 3 deletions zephyr/lib/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,8 @@ EXPORT_SYMBOL(rfree);
* To match the fall-back SOF main heap all private heaps should also be in the
* uncached address range.
*/
void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
size_t alignment)
void *z_impl_sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
size_t alignment)
{
if (flags & (SOF_MEM_FLAG_LARGE_BUFFER | SOF_MEM_FLAG_USER_SHARED_BUFFER))
return rballoc_align(flags, bytes, alignment);
Expand All @@ -643,14 +643,36 @@ void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
return (__sparse_force void *)heap_alloc_aligned_cached(heap, alignment, bytes);
}

void sof_heap_free(struct k_heap *heap, void *addr)
void z_impl_sof_heap_free(struct k_heap *heap, void *addr)
{
if (heap && addr && is_heap_pointer(heap, addr))
heap_free(heap, addr);
else
rfree(addr);
}

#ifndef CONFIG_SOF_USERSPACE_INTERFACE_ALLOC

/*
* Putting these as inlines in alloc.h breaks Zephyr native
* ztests like sof/test/ztest/unit/fast-get that include rtos/alloc.h
* but do not link lib/alloc.c . To to keep the tests happy,
* implement the functions here.
*/

void *sof_heap_alloc(struct k_heap *heap, uint32_t flags, size_t bytes,
size_t alignment)
{
return z_impl_sof_heap_alloc(heap, flags, bytes, alignment);
}

void sof_heap_free(struct k_heap *heap, void *addr)
{
return z_impl_sof_heap_free(heap, addr);
}

#endif /* CONFIG_SOF_USERSPACE_INTERFACE_ALLOC */

static int heap_init(void)
{
sys_heap_init(&sof_heap.heap, heapmem, HEAPMEM_SIZE - SHARED_BUFFER_HEAP_MEM_SIZE);
Expand Down
36 changes: 36 additions & 0 deletions zephyr/syscall/alloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2026 Intel Corporation.

#include <rtos/alloc.h>
#include <sof/schedule/ll_schedule_domain.h>
#include <zephyr/kernel.h>
#include <zephyr/internal/syscall_handler.h>

static inline void *z_vrfy_sof_heap_alloc(struct k_heap *heap, uint32_t flags,
size_t bytes, size_t alignment)
{
/* reject flags that bypass heap isolation */
K_OOPS(flags & (SOF_MEM_FLAG_LARGE_BUFFER | SOF_MEM_FLAG_USER_SHARED_BUFFER));

/* user-space use of sof_heap_alloc() limited to this single heap */
K_OOPS(!zephyr_ll_user_heap_verify(heap));

return z_impl_sof_heap_alloc(heap, flags, bytes, alignment);
}
#include <zephyr/syscalls/sof_heap_alloc_mrsh.c>

static inline void z_vrfy_sof_heap_free(struct k_heap *heap, void *addr)
{
/* user-space use of sof_heap_alloc() limited to this single heap */
K_OOPS(!zephyr_ll_user_heap_verify(heap));

if (addr) {
uintptr_t start = (uintptr_t)heap->heap.init_mem;
uintptr_t addr_uc = (uintptr_t)sys_cache_uncached_ptr_get(addr);
K_OOPS(addr_uc < start || addr_uc >= start + heap->heap.init_bytes);
K_OOPS(K_SYSCALL_MEMORY_WRITE(addr, 1));
}
z_impl_sof_heap_free(heap, addr);
}
#include <zephyr/syscalls/sof_heap_free_mrsh.c>
3 changes: 3 additions & 0 deletions zephyr/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ 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()
endif()

if(CONFIG_SOF_BOOT_TEST_STANDALONE AND CONFIG_SOF_USERSPACE_INTERFACE_DMA)
Expand Down
Loading
Loading