diff --git a/src/schedule/zephyr_ll_user.c b/src/schedule/zephyr_ll_user.c index aa33807b4aa3..5517ccfa201d 100644 --- a/src/schedule/zephyr_ll_user.c +++ b/src/schedule/zephyr_ll_user.c @@ -17,7 +17,7 @@ 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 */ @@ -26,17 +26,15 @@ struct zephyr_ll_mem_resources { static struct zephyr_ll_mem_resources ll_mem_resources; +/* Heap allocator for LL scheduler memory (user accessible pointer) */ +APP_SYSUSER_DATA static struct k_heap *zephyr_ll_heap; + static struct k_heap *zephyr_ll_heap_init(void) { - struct k_heap *heap = module_driver_heap_init(); + struct k_heap *heap = sys_user_heap_init(); struct k_mem_partition mem_partition; int ret; - /* - * TODO: the size of LL heap should be independently configurable and - * not tied to CONFIG_SOF_ZEPHYR_USERSPACE_MODULE_HEAP_SIZE - */ - if (!heap) { tr_err(&ll_tr, "heap alloc fail"); k_panic(); @@ -53,6 +51,7 @@ 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); @@ -60,6 +59,7 @@ static struct k_heap *zephyr_ll_heap_init(void) (void *)mem_partition.start, heap->heap.init_bytes, ret); if (ret) k_panic(); +#endif return heap; } @@ -70,13 +70,17 @@ void zephyr_ll_user_resources_init(void) ll_mem_resources.heap = zephyr_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()); + user_memory_attach_system_user_partition(zephyr_ll_mem_domain()); } struct k_heap *zephyr_ll_user_heap(void) { - return ll_mem_resources.heap; + return zephyr_ll_heap; } struct k_mem_domain *zephyr_ll_mem_domain(void) diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f1d1896c4234..c93b2411736e 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -116,6 +116,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 diff --git a/zephyr/include/rtos/userspace_helper.h b/zephyr/include/rtos/userspace_helper.h index 7d8f7919d865..6576cd4639f5 100644 --- a/zephyr/include/rtos/userspace_helper.h +++ b/zephyr/include/rtos/userspace_helper.h @@ -50,6 +50,27 @@ struct userspace_context; */ struct k_heap *module_driver_heap_init(void); +/** + * Initialize the shared LL user-space heap (SOF_ZEPHYR_SYS_USER_HEAP_SIZE) + * with embedded k_heap metadata. + * @return pointer to the k_heap structure. + * + * @note + * Unlike module_driver_heap_init(), this function embeds the k_heap + * struct at the start of the page-aligned backing buffer. The + * init_mem / init_bytes fields cover the full allocation, so a + * memory partition derived from them automatically includes the + * k_heap metadata (required for userspace syscall verification). + * Must be freed with sys_user_heap_remove(). + */ +struct k_heap *sys_user_heap_init(void); + +/** + * Free the shared LL user-space heap allocated by sys_user_heap_init(). + * @param heap pointer to the k_heap structure. + */ +void sys_user_heap_remove(struct k_heap *heap); + /** * Attach common userspace memory partition to a module memory domain. * @param dom - memory domain to attach the common partition to. diff --git a/zephyr/lib/userspace_helper.c b/zephyr/lib/userspace_helper.c index c7c361295269..bb17753284f8 100644 --- a/zephyr/lib/userspace_helper.c +++ b/zephyr/lib/userspace_helper.c @@ -69,6 +69,51 @@ void module_driver_heap_remove(struct k_heap *mod_drv_heap) } } +struct k_heap *sys_user_heap_init(void) +{ + const size_t alloc_size = CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE; + const size_t prefix_size = ALIGN_UP(sizeof(struct k_heap), sizeof(uintptr_t)); + const size_t kheap_size = alloc_size - prefix_size; + + BUILD_ASSERT(CONFIG_SOF_ZEPHYR_SYS_USER_HEAP_SIZE % CONFIG_MM_DRV_PAGE_SIZE == 0); + + if (alloc_size <= prefix_size) + return NULL; + + /* + * Allocate a single page-aligned buffer for both the k_heap + * metadata and the heap backing memory. Placing the k_heap + * struct inside the same allocation means the memory partition + * (which uses init_mem / init_bytes) automatically covers the + * k_heap struct, making it accessible from userspace syscall + * verification wrappers such as z_vrfy_mod_free(). + */ + void *mem = rballoc_align(SOF_MEM_FLAG_USER | SOF_MEM_FLAG_COHERENT, alloc_size, + CONFIG_MM_DRV_PAGE_SIZE); + if (!mem) + return NULL; + + struct k_heap *inline_heap = (struct k_heap *)mem; + void *heap_buf = (uint8_t *)mem + prefix_size; + + k_heap_init(inline_heap, heap_buf, kheap_size); + + /* init_mem / init_bytes track the full allocation so that + * partition setup and sys_user_heap_remove() + * cover and free the entire region including the k_heap struct. + */ + inline_heap->heap.init_mem = mem; + inline_heap->heap.init_bytes = alloc_size; + + return inline_heap; +} + +void sys_user_heap_remove(struct k_heap *sys_user_heap) +{ + if (sys_user_heap) + rfree(sys_user_heap->heap.init_mem); +} + void *user_stack_allocate(size_t stack_size, uint32_t options) { return k_thread_stack_alloc(stack_size, options & K_USER);