From ca4853d9140d5513ef2594f08cbfea90e11cb7bd Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 23 Oct 2026 18:17:47 +0300 Subject: [PATCH 1/2] llext_manager: get detached section addresses from the ELF buffer Detached sections, like .cold and .coldrodata, are not copied into their region but stay in the ELF buffer. llext_manager computed their addresses as the region address plus the section offset returned by llext_get_section_info(). This only worked by accident, while the region was used in place. Since Zephyr commit 181077caab27 ("llext: report no region offset for detached sections"), the offset for a detached section is (size_t)-1. The .cold partition is no longer added to the user memory domain and MMU flags are applied to the wrong pages. Calling a .cold function from a user thread then fails with EXCCAUSE 18 (instr fetch privilege), e.g. when the DRC module is loaded. Use llext_peek() at the section's ELF file offset instead, matching what Zephyr llext itself does for detached sections. This also removes the section lookup loop in llext_manager_add_mod_domain(). Signed-off-by: Kai Vehmanen --- src/library_manager/llext_manager.c | 96 ++++++++++------------------- 1 file changed, 31 insertions(+), 65 deletions(-) diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 272191984cfb..bb7b3788c8f6 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -98,7 +98,7 @@ static void llext_manager_detached_update_flags(void __sparse_cache *vma, * memory range, then remap the same area according to the 'flags' parameter. */ static int llext_manager_load_data_from_storage(const struct sys_mm_drv_region *virtual_region, - const struct llext_loader *ldr, + struct llext_loader *ldr, const struct llext *ext, enum llext_mem region, void __sparse_cache *vma, @@ -136,16 +136,18 @@ static int llext_manager_load_data_from_storage(const struct sys_mm_drv_region * if (s_region != region) continue; - /* skip detached sections (will be outside requested VMA area) */ + /* + * skip detached sections (will be outside requested VMA area), + * they stay in the ELF buffer + */ if ((uintptr_t)shdr->sh_addr < (uintptr_t)vma || (uintptr_t)shdr->sh_addr >= (uintptr_t)vma + size) { - llext_manager_detached_update_flags((__sparse_cache void *) - ((uint8_t *)region_addr + s_offset), - shdr->sh_size, flags); + void __sparse_cache *sect_addr = (__sparse_force void __sparse_cache *) + llext_peek(ldr, shdr->sh_offset); + + llext_manager_detached_update_flags(sect_addr, shdr->sh_size, flags); if (flags & SYS_MM_MEM_PERM_EXEC) - icache_invalidate_region((__sparse_cache void *) - ((uint8_t *)region_addr + s_offset), - shdr->sh_size); + icache_invalidate_region(sect_addr, shdr->sh_size); continue; } @@ -200,7 +202,7 @@ static int llext_manager_rm_partition(struct k_mem_domain *domain, } #endif /* CONFIG_USERSPACE */ -static void llext_manager_unmap_detached_sections(const struct llext_loader *ldr, +static void llext_manager_unmap_detached_sections(struct llext_loader *ldr, const struct llext *ext, enum llext_mem region, void __sparse_cache *vma, @@ -208,16 +210,12 @@ static void llext_manager_unmap_detached_sections(const struct llext_loader *ldr { #ifdef CONFIG_MMU unsigned int i; - const void *region_addr; - - llext_get_region_info(ldr, ext, region, NULL, ®ion_addr, NULL); for (i = 0; i < llext_section_count(ext); i++) { const elf_shdr_t *shdr; enum llext_mem s_region = LLEXT_MEM_COUNT; - size_t s_offset = 0; - llext_get_section_info(ldr, ext, i, &shdr, &s_region, &s_offset); + llext_get_section_info(ldr, ext, i, &shdr, &s_region, NULL); /* skip sections not in the requested region */ if (s_region != region) @@ -226,8 +224,8 @@ static void llext_manager_unmap_detached_sections(const struct llext_loader *ldr /* unmap detached sections (will be outside requested VMA area) */ if ((uintptr_t)shdr->sh_addr < (uintptr_t)vma || (uintptr_t)shdr->sh_addr >= (uintptr_t)vma + size) - llext_manager_detached_update_flags((__sparse_force void *) - ((uint8_t *)region_addr + s_offset), + llext_manager_detached_update_flags((__sparse_force void __sparse_cache *) + llext_peek(ldr, shdr->sh_offset), shdr->sh_size, 0); } #endif /* CONFIG_MMU */ @@ -292,7 +290,7 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) mctx->writable_merged.addr = (uintptr_t)va_base_data; mctx->writable_merged.size = data_size; - const struct llext_loader *ldr = &mctx->ebl->loader; + struct llext_loader *ldr = &mctx->ebl->loader; const struct llext *ext = mctx->llext; /* find dedicated virtual memory zone */ @@ -361,7 +359,7 @@ static int llext_manager_load_module(struct lib_manager_module *mctx) static int llext_manager_unload_module(struct lib_manager_module *mctx) { - const struct llext_loader *ldr = &mctx->ebl->loader; + struct llext_loader *ldr = &mctx->ebl->loader; const struct llext *ext = mctx->llext; /* Executable code (.text) */ @@ -851,7 +849,7 @@ uintptr_t llext_manager_allocate_module(const struct comp_ipc_config *ipc_config static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct k_mem_domain *domain) { const struct llext *ext = mctx->llext; - const struct llext_loader *ldr = &mctx->ebl->loader; + struct llext_loader *ldr = &mctx->ebl->loader; /* Executable code (.text) */ uintptr_t va_base_text = mctx->segment[LIB_MANAGER_TEXT].addr; @@ -899,65 +897,33 @@ static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct } elf_shdr_t shdr_cold, shdr_coldrodata; - bool rodata = false, text = false; const void *rodata_addr = NULL, *text_addr = NULL; - size_t text_offset = 0, rodata_offset = 0; shdr_cold.sh_size = 0; shdr_coldrodata.sh_size = 0; + /* .cold and .coldrodata are detached sections, they stay in the ELF buffer */ ret = llext_get_section_header(ldr, ext, ".cold", &shdr_cold); if (ret < 0) tr_warn(&lib_manager_tr, "couldn't get .cold header"); - else - llext_get_region_info(ldr, ext, LLEXT_MEM_TEXT, NULL, &text_addr, NULL); + else if (shdr_cold.sh_size) + text_addr = llext_peek(ldr, shdr_cold.sh_offset); ret = llext_get_section_header(ldr, ext, ".coldrodata", &shdr_coldrodata); if (ret < 0) tr_warn(&lib_manager_tr, "couldn't get .coldrodata header"); - else - llext_get_region_info(ldr, ext, LLEXT_MEM_RODATA, NULL, &rodata_addr, NULL); - - for (unsigned int i = 0; i < llext_section_count(ext) && (!rodata || !text); i++) { - const elf_shdr_t *shdr; - enum llext_mem s_region = LLEXT_MEM_COUNT; - size_t s_offset = 0; - - ret = llext_get_section_info(ldr, ext, i, &shdr, &s_region, &s_offset); - if (ret < 0) - continue; - - switch (s_region) { - case LLEXT_MEM_TEXT: - if (shdr_cold.sh_size && - shdr->sh_name == shdr_cold.sh_name && - shdr->sh_offset == shdr_cold.sh_offset && !text) { - text = true; - text_offset = s_offset; - } - break; - case LLEXT_MEM_RODATA: - if (shdr_coldrodata.sh_size && - shdr->sh_name == shdr_coldrodata.sh_name && - shdr->sh_offset == shdr_coldrodata.sh_offset && !rodata) { - rodata = true; - rodata_offset = s_offset; - } - break; - default: - break; - } - } + else if (shdr_coldrodata.sh_size) + rodata_addr = llext_peek(ldr, shdr_coldrodata.sh_offset); - if (text) { + if (text_addr) { tr_dbg(&lib_manager_tr, ".cold %#x @ %#lx", - shdr_cold.sh_size, (uintptr_t)text_addr + text_offset); - ret = llext_manager_add_partition(domain, (uintptr_t)text_addr + text_offset, + shdr_cold.sh_size, (uintptr_t)text_addr); + ret = llext_manager_add_partition(domain, (uintptr_t)text_addr, shdr_cold.sh_size, K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); if (ret < 0) goto e_data; - mctx->segment[LIB_MANAGER_COLD].addr = (uintptr_t)text_addr + text_offset; + mctx->segment[LIB_MANAGER_COLD].addr = (uintptr_t)text_addr; mctx->segment[LIB_MANAGER_COLD].size = shdr_cold.sh_size; } else { /* @@ -971,15 +937,15 @@ static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct mctx->segment[LIB_MANAGER_COLD].size = 0; } - if (rodata) { + if (rodata_addr) { tr_dbg(&lib_manager_tr, ".coldrodata %#x @ %#lx", - shdr_coldrodata.sh_size, (uintptr_t)rodata_addr + rodata_offset); - ret = llext_manager_add_partition(domain, (uintptr_t)rodata_addr + rodata_offset, + shdr_coldrodata.sh_size, (uintptr_t)rodata_addr); + ret = llext_manager_add_partition(domain, (uintptr_t)rodata_addr, shdr_coldrodata.sh_size, K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB); if (ret < 0) goto e_cold; - mctx->segment[LIB_MANAGER_COLDRODATA].addr = (uintptr_t)rodata_addr + rodata_offset; + mctx->segment[LIB_MANAGER_COLDRODATA].addr = (uintptr_t)rodata_addr; mctx->segment[LIB_MANAGER_COLDRODATA].size = shdr_coldrodata.sh_size; } else { /* No .coldrodata section, see the comment above */ @@ -990,7 +956,7 @@ static int llext_manager_add_mod_domain(struct lib_manager_module *mctx, struct return 0; e_cold: - llext_manager_rm_partition(domain, (uintptr_t)text_addr + text_offset, shdr_cold.sh_size, + llext_manager_rm_partition(domain, (uintptr_t)text_addr, shdr_cold.sh_size, K_MEM_PARTITION_P_RX_U_RX | XTENSA_MMU_CACHED_WB); mctx->segment[LIB_MANAGER_COLD].addr = 0; mctx->segment[LIB_MANAGER_COLD].size = 0; From a33cffdd1317069b65af207f0af28b550464417d Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 23 Oct 2026 15:26:55 +0300 Subject: [PATCH 2/2] west.yml: update zephyr to 76280bce2a05 Update Zephyr to 76280bce2a05 ("release: Zephyr 4.5.0-rc3"). Total of 4333 commits. Changes include: 1c60ac6c1dc6 soc: intel_adsp: ace40: allow userspace to execute cold functions f619c179fc35 arch: xtensa: mmu: skip unmapped L1 entries in region_map_update() 5d0c907ec5d3 drivers: mm: intel_adsp: tlb: map uncached alias in the MMU da29f2545f78 llext: reject an out-of-section plt relocation 979e655d465f xtensa: mask interrupts before leaving the cross-stack call ac806fc4d322 llext: support Xtensa MPU in llext_copy_region() 8fefdcc4e555 xtensa: export symbol xtensa_is_user_context() 16592affe202 arch: xtensa: save the boolean register c4a4a36353ed llext: fix detached section addresses with copied text 6038faef172b drivers: dai: nxp: sai: fix DMA handshake for nxp,mcux-edma controllers 647f444ded62 llext: use the MMU page size only on targets with an MMU 2fc93dcc7e6e arch: xtensa: match z_irq_priority_set() declaration to definition 14a652034b96 arch: xtensa: use unsigned int for xtensa_irq_enable/disable() 4fdaedc7a2a0 soc: intel_adsp/ace: enable CONFIG_IPI_OPTIMIZE for testing 3ba231580276 timer: intel_adsp_timer: tests need timer IRQ for all cores 2271fd46854b soc: intel_adsp: record triple fault breadcrumbs in HP-SRAM window0 f43453290df2 xtensa: add generic triple fault breadcrumb API 43a3e87f390b soc: intel_adsp: record fatal exception breadcrumbs in HP-SRAM window0 1e2b3bc58ff4 xtensa: add generic fatal exception breadcrumb API 07583d726104 soc: mediatek: decouple the mt8xxx family from Xtensa e12a539cd631 soc: mediatek: move the shared audio DSP files into common/adsp 99b2b3da5317 drivers: dma: amd_acp_host: Reject start from an invalid state acf7fc963b01 arch: xtensa: fix the soc pointer check prototype 699c613f0726 arch: xtensa: carry backtrace mask and cause in the frame bf65aa2ff280 boards: intel: adsp: Fix invalid stack size setting 18f4914e6918 drivers: dai: intel: uaol: implement get_properties_copy 52b48529aecc soc: intel_adsp: condition mem-coherence select on cache mgmt c8049431a47d soc: intel_adsp: tools: add mtracetool, a standalone mtrace capture tool fe7066e15495 soc: intel_adsp: tools: cavstool.py: add passive mtrace log monitoring 0c5a0c9e1ddd drivers: audio: codec_dummy: implement the required operations d1352ac2a8e7 drivers: timer: intel_adsp_timer: use the generic timer core 450f80417675 debug: coredump: add AMD ACP panic dump backend 2756f2c863de xtensa: mmu: simplify TLB shootdown 60c928462028 xtensa: mmu: no TLB shootdown if not needed during thread add Signed-off-by: Kai Vehmanen --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index cbbc236cab0f..2b23fe5d34ca 100644 --- a/west.yml +++ b/west.yml @@ -71,7 +71,7 @@ manifest: - name: zephyr repo-path: zephyr - revision: beeac475263ea55a74a7b61274cd4867c53c973d + revision: 76280bce2a050376d57e14240687b5c283dea0e7 remote: zephyrproject # Import some projects listed in zephyr/west.yml@revision