From bf7cf691fcca45068729033a09f784a4f9a947ce Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:21:19 +0100 Subject: [PATCH 01/47] cmake: toolchain: llvm: Xtensa target triple and mcpu mapping Configure the target triple and -mcpu for Xtensa LLVM builds: - Resolve XTENSA_TOOLCHAIN_TARGET from CONFIG_SOC_TOOLCHAIN_NAME or the XTENSA_TOOLCHAIN_TARGET environment variable. - Map SDK toolchain target names to upstream LLVM -mcpu names via a small table (intel_ace15_mtpm -> intel_ace15_adsp, etc.). - Set triple to xtensa-_zephyr-elf, select LLD linker and llvm bintools, and configure CROSS_COMPILE_TARGET accordingly. - Append core Clang flags: -mcpu, -D__XCC__, -D__XCC_CLANG__, -fintegrated-as, -gdwarf-4. Signed-off-by: Liam Girdwood --- cmake/toolchain/llvm/generic.cmake | 20 ++++ cmake/toolchain/llvm/target.cmake | 159 +++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 cmake/toolchain/llvm/generic.cmake create mode 100644 cmake/toolchain/llvm/target.cmake diff --git a/cmake/toolchain/llvm/generic.cmake b/cmake/toolchain/llvm/generic.cmake new file mode 100644 index 000000000000..116c1854bc6a --- /dev/null +++ b/cmake/toolchain/llvm/generic.cmake @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: Apache-2.0 + +set(COMPILER clang) +set(TOOLCHAIN_VARIANT_COMPILER llvm CACHE STRING "compiler used by the toolchain variant" FORCE) +set(LINKER ld) +set(BINTOOLS gnu) + + +zephyr_get(LLVM_TOOLCHAIN_PATH) +if(LLVM_TOOLCHAIN_PATH) + set(TOOLCHAIN_HOME ${LLVM_TOOLCHAIN_PATH}/bin/) +endif() + +# When using the Zephyr SDK as our binutils provider, the SDK sysroot contains +# both newlib and picolibc headers. Advertise this to Kconfig so that +# PICOLIBC_SUPPORTED / NEWLIB_LIBC_SUPPORTED are set *before* kconfig.cmake +# runs (generic.cmake is loaded via FindHostTools during the dts phase, whereas +# target.cmake is loaded much later via FindTargetTools in the kernel phase). +set(TOOLCHAIN_HAS_PICOLIBC y CACHE INTERNAL "True if toolchain supports picolibc") +set(TOOLCHAIN_HAS_NEWLIB y CACHE INTERNAL "True if toolchain supports newlib") diff --git a/cmake/toolchain/llvm/target.cmake b/cmake/toolchain/llvm/target.cmake new file mode 100644 index 000000000000..695a89aaa158 --- /dev/null +++ b/cmake/toolchain/llvm/target.cmake @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_LLVM_USE_LD) + set(LINKER ld) +elseif(CONFIG_LLVM_USE_LLD) + set(LINKER lld) +endif() + +if("${ARCH}" STREQUAL "arm") + if(DEFINED CONFIG_ARMV8_M_MAINLINE) + # ARMv8-M mainline is ARMv7-M with additional features from ARMv8-M. + set(triple armv8m.main-none-eabi) + elseif(DEFINED CONFIG_ARMV8_M_BASELINE) + # ARMv8-M baseline is ARMv6-M with additional features from ARMv8-M. + set(triple armv8m.base-none-eabi) + elseif(DEFINED CONFIG_ARMV7_M_ARMV8_M_MAINLINE) + # ARMV7_M_ARMV8_M_MAINLINE means that ARMv7-M or backward compatible ARMv8-M + # processor is used. + set(triple armv7m-none-eabi) + elseif(DEFINED CONFIG_ARMV6_M_ARMV8_M_BASELINE) + # ARMV6_M_ARMV8_M_BASELINE means that ARMv6-M or ARMv8-M supporting the + # Baseline implementation processor is used. + set(triple armv6m-none-eabi) + else() + # Default ARM target supported by all processors. + set(triple arm-none-eabi) + endif() +elseif("${ARCH}" STREQUAL "arm64") + set(triple aarch64-none-elf) +elseif("${ARCH}" STREQUAL "x86") + if(CONFIG_64BIT) + set(triple x86_64-pc-none-elf) + else() + set(triple i686-pc-none-elf) + endif() +elseif("${ARCH}" STREQUAL "riscv") + if(CONFIG_64BIT) + set(triple riscv64-unknown-elf) + else() + set(triple riscv32-unknown-elf) + endif() +elseif("${ARCH}" STREQUAL "xtensa") + # Xtensa uses Clang for compilation with LLD and the Zephyr SDK binutils. + # The target triple encodes the specific Xtensa core variant. + # + # Two names are involved per SoC: + # * XTENSA_TOOLCHAIN_TARGET - Zephyr SDK toolchain target dir name + # (matches CONFIG_SOC_TOOLCHAIN_NAME), + # e.g. "intel_ace30_ptl". + # * XTENSA_CORE_ID - upstream LLVM Xtensa backend -mcpu name, + # typically "__adsp", + # e.g. "intel_ace30_adsp". + # + # Both can be overridden via environment variables. If unset, they are + # derived from CONFIG_SOC_TOOLCHAIN_NAME and a small mapping table for + # the SoCs that ship with mismatched SDK/LLVM names. + + set(XTENSA_TOOLCHAIN_TARGET $ENV{XTENSA_TOOLCHAIN_TARGET}) + if(NOT XTENSA_TOOLCHAIN_TARGET) + if(CONFIG_SOC_TOOLCHAIN_NAME) + set(XTENSA_TOOLCHAIN_TARGET "${CONFIG_SOC_TOOLCHAIN_NAME}") + else() + # Fallback for boards that don't set CONFIG_SOC_TOOLCHAIN_NAME yet. + set(XTENSA_TOOLCHAIN_TARGET "intel_ace30_ptl") + endif() + endif() + + set(XTENSA_CORE_ID $ENV{XTENSA_CORE_ID}) + if(NOT XTENSA_CORE_ID) + # Map Zephyr SDK toolchain target -> upstream LLVM Xtensa -mcpu name. + # Only entries whose names differ between the SDK and LLVM are listed; + # anything not in this table is assumed to be identical in both. + set(_xtensa_sdk_to_llvm_cpu + # Intel ADSP + intel_ace15_mtpm intel_ace15_adsp + intel_ace40 intel_ace40_adsp + intel_ace30_ptl intel_ace30_adsp + ) + list(FIND _xtensa_sdk_to_llvm_cpu "${XTENSA_TOOLCHAIN_TARGET}" _idx) + if(_idx GREATER -1) + math(EXPR _val_idx "${_idx} + 1") + list(GET _xtensa_sdk_to_llvm_cpu ${_val_idx} XTENSA_CORE_ID) + else() + # SDK target name matches the LLVM -mcpu name (true for most + # AMD/MTK/NXP ADSP cores and dc233c / sample_controller*). + set(XTENSA_CORE_ID "${XTENSA_TOOLCHAIN_TARGET}") + endif() + unset(_xtensa_sdk_to_llvm_cpu) + unset(_idx) + unset(_val_idx) + endif() + + set(triple xtensa-${XTENSA_TOOLCHAIN_TARGET}_zephyr-elf) + set(XTENSA_CLANG_MCPU ${XTENSA_CORE_ID}) + + # Use LLVM LLD linker + set(LINKER lld) + set(BINTOOLS llvm) + + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + + # Configure cross-compile prefix for binutils (assembler, linker, objcopy, etc.) + set(CROSS_COMPILE_TARGET xtensa-${XTENSA_TOOLCHAIN_TARGET}_zephyr-elf) + + # Clang-specific flags for Xtensa + list(APPEND TOOLCHAIN_C_FLAGS -mcpu=${XTENSA_CLANG_MCPU}) + # Define __XCC__ so SOF's HiFi detection logic (format.h, fft.h, fir_config.h + # etc.) recognises our LLVM Clang as an Xtensa compiler with HiFi support and + # enters the "#if defined __XCC__" branches that read XCHAL_HAVE_HIFIx from + # . + # Also define __XCC_CLANG__ so the CAVS-family core-isa.h files (tgl, apl, + # cnl, icl) can whitelist LLVM without triggering their "#error xcc should + # not use this header" guard (see modules/hal/xtensa patch below). + list(APPEND TOOLCHAIN_C_FLAGS -D__XCC__ -D__XCC_CLANG__) + + # Use the integrated assembler for all code + list(APPEND TOOLCHAIN_C_FLAGS -fintegrated-as) + + if(CONFIG_COMPILER_CODEGEN_VLIW_ENABLED) + # Add +flix strictly to compile steps to prevent ld from misinterpreting +flix as an object file + add_compile_options("$<$:SHELL:-Xclang -target-feature -Xclang +flix>") + endif() + # Don't force auto-litpools inline in text sections; place in separate .literal sections instead + + # Force DWARFv4 debug info for compatibility with Zephyr's kobject scanner + # (gen_kobject_list.py). Clang defaults to DWARFv5 which the scanner can't parse. + list(APPEND TOOLCHAIN_C_FLAGS -gdwarf-4) + + # FLIX VLIW instruction bundling - DISABLED by default. + # The packetizer pass in LLVM generates some invalid instruction pairs + # because we don't fully model the hardware FLIX slot constraints yet. + # To enable manually: add '-Xclang -target-feature -Xclang +flix' to CMAKE_C_FLAGS + # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Xclang -target-feature -Xclang +flix" CACHE STRING "" FORCE) + + message(STATUS "Xtensa LLVM: triple=${triple} mcpu=${XTENSA_CLANG_MCPU}") +endif() + +if(DEFINED triple) + set(CMAKE_C_COMPILER_TARGET ${triple}) + set(CMAKE_ASM_COMPILER_TARGET ${triple}) + set(CMAKE_CXX_COMPILER_TARGET ${triple}) + + unset(triple) +endif() + +if(CONFIG_LIBGCC_RTLIB) + set(runtime_lib "libgcc") +elseif(CONFIG_COMPILER_RT_RTLIB) + set(runtime_lib "compiler_rt") +endif() + +if(DEFINED runtime_lib) + if("${ARCH}" STREQUAL "xtensa") + list(APPEND TOOLCHAIN_C_FLAGS --rtlib=${runtime_lib}) + else() + list(APPEND TOOLCHAIN_C_FLAGS --config=${ZEPHYR_BASE}/cmake/toolchain/llvm/clang_${runtime_lib}.cfg) + list(APPEND TOOLCHAIN_LD_FLAGS --config=${ZEPHYR_BASE}/cmake/toolchain/llvm/clang_${runtime_lib}.cfg) + endif() +endif() From a895a101a07c64d06b014d859771e9f4392eb1ee Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:21:51 +0100 Subject: [PATCH 02/47] cmake: toolchain: llvm: compiler-rt linker override and per-core SDK ld for Xtensa For Xtensa LLVM builds, override compiler_set_linker_properties() to link against LLVM's compiler-rt builtins (libclang_rt.builtins) instead of libgcc. The compiler-rt builtins ship with the LLVM toolchain and cover all Xtensa intrinsics needed by Zephyr and SOF. Also derive the per-core SDK linker binary (XTENSA_SDK_LD) from XTENSA_TOOLCHAIN_TARGET so that tgl, ace15, ace30 and future cores each use their matching SDK ld binary rather than a hardcoded ace15 path. Configure CMAKE_REQUIRED_FLAGS, CMAKE_EXE_LINKER_FLAGS and CMAKE_ASM_FLAGS for the Xtensa compile-test and assembly steps. Signed-off-by: Liam Girdwood --- cmake/toolchain/llvm/target.cmake | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cmake/toolchain/llvm/target.cmake b/cmake/toolchain/llvm/target.cmake index 695a89aaa158..4ead35d3caaa 100644 --- a/cmake/toolchain/llvm/target.cmake +++ b/cmake/toolchain/llvm/target.cmake @@ -132,7 +132,44 @@ elseif("${ARCH}" STREQUAL "xtensa") # To enable manually: add '-Xclang -target-feature -Xclang +flix' to CMAKE_C_FLAGS # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Xclang -target-feature -Xclang +flix" CACHE STRING "" FORCE) + # Ensure the dummy compile test can find the Xtensa assembler and linker. + if(TOOLCHAIN_HOME) + set(ENV{PATH} "${TOOLCHAIN_HOME}:$ENV{PATH}") + endif() + set(_req_flags "") + if(TOOLCHAIN_HOME) + list(APPEND _req_flags -B${TOOLCHAIN_HOME}) + endif() + list(APPEND _req_flags + -mcpu=${XTENSA_CLANG_MCPU} + -nostartfiles + -nostdlib + ) + string(REPLACE ";" " " _req_flags_str "${_req_flags}") + set(CMAKE_REQUIRED_FLAGS "${_req_flags_str}") + + if(TOOLCHAIN_HOME) + list(APPEND TOOLCHAIN_LD_FLAGS -B${TOOLCHAIN_HOME}) + # Derive the per-core SDK ld binary from XTENSA_TOOLCHAIN_TARGET so + # each Xtensa core uses its matching SDK linker (e.g. tgl, ace15, ace30). + set(XTENSA_SDK_LD "${TOOLCHAIN_HOME}xtensa-${XTENSA_TOOLCHAIN_TARGET}_zephyr-elf-ld") + endif() + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostartfiles -nodefaultlibs" CACHE STRING "" FORCE) + + # Override compiler_set_linker_properties to use compiler-rt builtins + # instead of libgcc; the compiler-rt builtins are distributed with the + # LLVM toolchain and cover all Xtensa intrinsics used by Zephyr/SOF. + function(compiler_set_linker_properties) + set(_clang_rt_dir "${LLVM_TOOLCHAIN_PATH}/lib/clang/23/lib/xtensa-unknown-unknown-elf") + set_linker_property(PROPERTY lib_include_dir "-L\"${_clang_rt_dir}\"") + set_linker_property(PROPERTY rt_library "-lclang_rt.builtins") + endfunction() + message(STATUS "Xtensa LLVM: triple=${triple} mcpu=${XTENSA_CLANG_MCPU}") + + # .S assembly files need the integrated assembler + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -fintegrated-as -fno-unwind-tables" CACHE STRING "" FORCE) endif() if(DEFINED triple) From 7bcab3629182c99fc91a19d36bab305e739175ba Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 20 Jun 2026 10:47:51 +0100 Subject: [PATCH 03/47] cmake: linker: Adapt linker executables for LLVM/LLD and GCC driver overrides Configure linker execution templates: - Use CMAKE_C_COMPILER and CMAKE_CXX_COMPILER directly as linker drivers, unless an external XTENSA_GCC compiler override is set. - Cleans up formatting and whitespace inside the LLD target macros. Signed-off-by: Liam Girdwood --- cmake/linker/ld/target.cmake | 16 +++++++++++++--- cmake/linker/lld/target.cmake | 8 ++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cmake/linker/ld/target.cmake b/cmake/linker/ld/target.cmake index 592596576d11..99cfe7f10502 100644 --- a/cmake/linker/ld/target.cmake +++ b/cmake/linker/ld/target.cmake @@ -154,8 +154,13 @@ macro(toolchain_linker_finalize) set(link_libraries " -o ${zephyr_std_libs}") set(common_link " ${link_libraries}") - set(CMAKE_ASM_LINK_EXECUTABLE " ${common_link}") - set(CMAKE_C_LINK_EXECUTABLE " ${common_link}") + if(DEFINED XTENSA_GCC) + set(CMAKE_ASM_LINK_EXECUTABLE "${XTENSA_GCC} ${common_link}") + set(CMAKE_C_LINK_EXECUTABLE "${XTENSA_GCC} ${common_link}") + else() + set(CMAKE_ASM_LINK_EXECUTABLE " ${common_link}") + set(CMAKE_C_LINK_EXECUTABLE " ${common_link}") + endif() set(cpp_link "${common_link}") if(NOT "${ZEPHYR_TOOLCHAIN_VARIANT}" STREQUAL "host") @@ -167,7 +172,12 @@ macro(toolchain_linker_finalize) set(cpp_link " ${CRTBEGIN_PATH} ${link_libraries} ${CRTEND_PATH}") endif() endif() - set(CMAKE_CXX_LINK_EXECUTABLE " ${cpp_link}") + + if(DEFINED XTENSA_GCC) + set(CMAKE_CXX_LINK_EXECUTABLE "${XTENSA_GCC} ${cpp_link}") + else() + set(CMAKE_CXX_LINK_EXECUTABLE " ${cpp_link}") + endif() endmacro() # Function to map compiler flags into suitable linker flags diff --git a/cmake/linker/lld/target.cmake b/cmake/linker/lld/target.cmake index 96df1c123796..340a33d609d0 100644 --- a/cmake/linker/lld/target.cmake +++ b/cmake/linker/lld/target.cmake @@ -122,10 +122,10 @@ macro(toolchain_linker_finalize) endforeach() string(REPLACE ";" " " zephyr_std_libs "${zephyr_std_libs}") - set(common_link " -o ${zephyr_std_libs}") - set(CMAKE_ASM_LINK_EXECUTABLE " ${common_link}") - set(CMAKE_C_LINK_EXECUTABLE " ${common_link}") - set(CMAKE_CXX_LINK_EXECUTABLE " ${common_link}") + set(common_link " -o ${zephyr_std_libs}") + set(CMAKE_ASM_LINK_EXECUTABLE " ${common_link}") + set(CMAKE_C_LINK_EXECUTABLE " ${common_link}") + set(CMAKE_CXX_LINK_EXECUTABLE " ${common_link}") endmacro() # Function to map compiler flags into suitable linker flags From e9b82b7c27fb22a76796d3c4b8cf02c362b7a0af Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:22:41 +0100 Subject: [PATCH 04/47] cmake: linker+toolchain: Configure LLD for Xtensa and disable FLIX by default When building Zephyr with Clang/LLVM for Xtensa, ld.bfd cannot natively ingest LLVM-generated objects. Disable the forced -fuse-ld=bfd flag in cmake/linker/ld/target.cmake so LLD is used instead. Also disable FLIX VLIW bundling in the LLVM toolchain target when CONFIG_COMPILER_CODEGEN_VLIW_DISABLED is set. The LLVM FLIX packetizer generates invalid instruction pairs because the hardware slot constraints are not yet fully modelled; FLIX can be re-enabled per-build by adding '-Xclang -target-feature -Xclang +flix' to CMAKE_C_FLAGS. Signed-off-by: Liam Girdwood --- cmake/linker/ld/target.cmake | 4 ++-- cmake/toolchain/llvm/target.cmake | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/linker/ld/target.cmake b/cmake/linker/ld/target.cmake index 99cfe7f10502..b7349d6ba165 100644 --- a/cmake/linker/ld/target.cmake +++ b/cmake/linker/ld/target.cmake @@ -9,8 +9,8 @@ set_ifndef(LINKERFLAGPREFIX -Wl) if((${CMAKE_LINKER} STREQUAL "${CROSS_COMPILE}ld.bfd") OR ${GNULD_LINKER_IS_BFD}) # ld.bfd was found so let's explicitly use that for linking, see #32237 - list(APPEND TOOLCHAIN_LD_FLAGS -fuse-ld=bfd) - list(APPEND CMAKE_REQUIRED_FLAGS -fuse-ld=bfd) + # list(APPEND TOOLCHAIN_LD_FLAGS -fuse-ld=bfd) + # list(APPEND CMAKE_REQUIRED_FLAGS -fuse-ld=bfd) string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") endif() diff --git a/cmake/toolchain/llvm/target.cmake b/cmake/toolchain/llvm/target.cmake index 4ead35d3caaa..56c472230e2d 100644 --- a/cmake/toolchain/llvm/target.cmake +++ b/cmake/toolchain/llvm/target.cmake @@ -119,6 +119,9 @@ elseif("${ARCH}" STREQUAL "xtensa") if(CONFIG_COMPILER_CODEGEN_VLIW_ENABLED) # Add +flix strictly to compile steps to prevent ld from misinterpreting +flix as an object file add_compile_options("$<$:SHELL:-Xclang -target-feature -Xclang +flix>") + elseif(CONFIG_COMPILER_CODEGEN_VLIW_DISABLED) + # Add -flix strictly to compile steps to override wrapper scripts or default CPU settings + add_compile_options("$<$:SHELL:-Xclang -target-feature -Xclang -flix>") endif() # Don't force auto-litpools inline in text sections; place in separate .literal sections instead From 6ce9cf99867c7a8a1cac7b0559df57bb64f66ca5 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 20 Jun 2026 10:47:59 +0100 Subject: [PATCH 05/47] cmake: modules: Configure LLEXT compiler and package stripping flags for ARM Configure LLEXT compilation options and stripping flags for ARM architectures: - Add LLEXT compiler options (LLEXT_REMOVE_FLAGS / LLEXT_APPEND_FLAGS) to cmake/compiler/clang/target_arm.cmake for Clang ARM builds. - Configure add_llext_target to strip .ARM.exidx* and .ARM.extab* exception handling and unwind table sections from the relocatable LLEXT module ELF file. Signed-off-by: Liam Girdwood --- cmake/compiler/clang/target_arm.cmake | 17 +++++++++++++++++ cmake/modules/extensions.cmake | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/cmake/compiler/clang/target_arm.cmake b/cmake/compiler/clang/target_arm.cmake index 5c6e27294a87..6c621fc0d576 100644 --- a/cmake/compiler/clang/target_arm.cmake +++ b/cmake/compiler/clang/target_arm.cmake @@ -42,3 +42,20 @@ if(CONFIG_FP16) endif() list(APPEND TOOLCHAIN_C_FLAGS ${ARM_C_FLAGS}) list(APPEND TOOLCHAIN_GROUPED_LD_FLAGS ARM_C_FLAGS) + +# Flags not supported by llext linker +# (regexps are supported and match whole word) +set(LLEXT_REMOVE_FLAGS + -fno-pic + -fno-pie + -ffunction-sections + -fdata-sections + -Os +) + +# Flags to be added to llext code compilation +set(LLEXT_APPEND_FLAGS + -mlong-calls + -mthumb + -fno-unwind-tables +) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index d919e70cb2e8..3b5381a4e5d3 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -6192,6 +6192,10 @@ function(add_llext_target target_name) $ $.xt.* $.xtensa.info + $.ARM.exidx* + $.rel.ARM.exidx* + $.ARM.extab* + $.rel.ARM.extab* $${llext_pkg_input} $${llext_pkg_output} $ From 9002aa4cb41b73e0a75c387ce2ef498527930a48 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:23:16 +0100 Subject: [PATCH 06/47] cmake: toolchain: llvm: LLEXT shared library link rules for Xtensa Configure CMAKE_C/CXX_CREATE_SHARED_LIBRARY and _MODULE to use the llext-link-wrapper.sh script for Xtensa LLVM LLEXT shared-library builds. The wrapper addresses two LLD issues: 1. Xtensa LLD rejects relocations into read-only sections (.rodata, _log_const, .module, .exported_sym) with 'dangerous relocation in read-only section'. The wrapper uses objcopy to mark those sections writable before linking. 2. l32r literals must appear before their use in .text. The default section order places .rodata after .text, violating this constraint. The wrapper links with -mtext-section-literals so literals are interleaved with code, and strips the X flag from .rodata afterwards to prevent llext_link_helper.py from misclassifying it as executable. Pass -fuse-ld=lld so the GCC wrapper driver uses LLD for the final link. Disable ccache for link steps as it cannot cache the wrapper output. Signed-off-by: Liam Girdwood --- cmake/toolchain/llvm/target.cmake | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cmake/toolchain/llvm/target.cmake b/cmake/toolchain/llvm/target.cmake index 56c472230e2d..c47976c5bdb2 100644 --- a/cmake/toolchain/llvm/target.cmake +++ b/cmake/toolchain/llvm/target.cmake @@ -173,6 +173,30 @@ elseif("${ARCH}" STREQUAL "xtensa") # .S assembly files need the integrated assembler set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -fintegrated-as -fno-unwind-tables" CACHE STRING "" FORCE) + + # LLEXT shared libraries are loadable modules, not standalone executables. + # Prevent the GCC driver from linking crt0.o/crtn.o into them. + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostartfiles -nodefaultlibs" CACHE STRING "" FORCE) + + # Override the shared library link rule with our wrapper script that: + # 1. Uses objcopy to make .rodata sections writable (fixes "dangerous + # relocation in read-only section") + # 2. Links with GCC + -mtext-section-literals (fixes "l32r literal placed + # after use" errors from section ordering) + set(LLEXT_LINK_WRAPPER "${CMAKE_CURRENT_LIST_DIR}/llext-link-wrapper.sh") + set(XTENSA_CROSS_PREFIX "${TOOLCHAIN_HOME}llvm-") + set(CMAKE_C_CREATE_SHARED_LIBRARY + "${LLEXT_LINK_WRAPPER} ${XTENSA_CROSS_PREFIX} -fuse-ld=lld -o " CACHE STRING "" FORCE) + set(CMAKE_CXX_CREATE_SHARED_LIBRARY + "${LLEXT_LINK_WRAPPER} ${XTENSA_CROSS_PREFIX} -fuse-ld=lld -o " CACHE STRING "" FORCE) + set(CMAKE_C_CREATE_SHARED_MODULE + "${LLEXT_LINK_WRAPPER} ${XTENSA_CROSS_PREFIX} -fuse-ld=lld -o " CACHE STRING "" FORCE) + set(CMAKE_CXX_CREATE_SHARED_MODULE + "${LLEXT_LINK_WRAPPER} ${XTENSA_CROSS_PREFIX} -fuse-ld=lld -o " CACHE STRING "" FORCE) + + # Disable ccache for link steps - ccache can't properly handle our + # wrapper script (it caches/eats the output). + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "") endif() if(DEFINED triple) From fedd23c41c605d421863acd3bbccfcb739346e45 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 21:01:53 +0100 Subject: [PATCH 07/47] cmake: toolchain: llvm: add LLEXT link wrapper and shared linker script When building LLEXT shared libraries with Clang/LLD two problems arise: 1. The Xtensa LLD linker rejects relocations into read-only sections (.rodata, _log_const, .module, .exported_sym) with "dangerous relocation in read-only section". Worked around by making those sections writable via objcopy before linking. 2. l32r literals must appear before their use in the .text section. The default section order places .rodata after .text, violating this. A linker script overlay (llext_shared.ld) places .rodata before .text and merges all .literal.* into .text. Add llext-link-wrapper.sh to orchestrate: mark sections writable, link with GCC -mtext-section-literals + linker script, then strip the X flag from .rodata in the output so llext_link_helper.py does not misclassify it as executable. Signed-off-by: Liam Girdwood --- cmake/toolchain/llvm/llext-link-wrapper.sh | 61 ++++++++++++++++++++++ cmake/toolchain/llvm/llext_shared.ld | 18 +++++++ 2 files changed, 79 insertions(+) create mode 100755 cmake/toolchain/llvm/llext-link-wrapper.sh create mode 100644 cmake/toolchain/llvm/llext_shared.ld diff --git a/cmake/toolchain/llvm/llext-link-wrapper.sh b/cmake/toolchain/llvm/llext-link-wrapper.sh new file mode 100755 index 000000000000..593d0150bd72 --- /dev/null +++ b/cmake/toolchain/llvm/llext-link-wrapper.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# llext-link-wrapper.sh +# Wraps the shared library link step for LLEXT modules. +# 1. Makes read-only sections writable via objcopy (fixes "dangerous relocation +# in read-only section" errors from the Xtensa linker) +# 2. Links with GCC + -mtext-section-literals + linker script (fixes "l32r +# literal placed after use" errors from section ordering) +# 3. Strips X flag from .rodata in the output .so (prevents llext_link_helper.py +# from misclassifying .rodata as executable) +# +# Usage: llext-link-wrapper.sh [args...] + +CROSS_PREFIX="$1" +shift + +SCRIPT_DIR="$(dirname "$0")" +GCC="${CROSS_PREFIX}gcc" +OBJCOPY="${CROSS_PREFIX}objcopy" +READELF="${CROSS_PREFIX}readelf" + +# Find output file from -o argument +OUTPUT="" +ARGS=() +skip_next=false +for arg in "$@"; do + if $skip_next; then + OUTPUT="$arg" + ARGS+=("$arg") + skip_next=false + continue + fi + case "$arg" in + --target=*|--target\ *) continue ;; # GCC doesn't need --target + -mcpu=*) continue ;; # GCC doesn't need -mcpu for linking + -o) skip_next=true ;; + esac + + if [[ "$arg" == *.obj ]] && [[ -f "$arg" ]]; then + # Make relevant read-only sections writable in-place + while IFS= read -r section; do + [[ -n "$section" ]] && \ + "$OBJCOPY" --set-section-flags "$section=alloc,contents,load,data" "$arg" 2>/dev/null || true + done < <("$READELF" -SW "$arg" 2>/dev/null | \ + grep -E '\.rodata\.|_log_const|\.module[^_]|\.exported_sym' | \ + grep -v '\.rela\.' | grep -v '\.debug' | \ + grep ' A ' | grep -v ' W' | \ + sed 's/.*\] //' | awk '{print $1}') + fi + ARGS+=("$arg") +done + +# Link with GCC +"$GCC" -mtext-section-literals -Wl,-T,"${SCRIPT_DIR}/llext_shared.ld" "${ARGS[@]}" +rc=$? + +# Post-link: strip X flag from .rodata so llext_link_helper.py classifies it correctly +if [[ $rc -eq 0 ]] && [[ -n "$OUTPUT" ]] && [[ -f "$OUTPUT" ]]; then + "$OBJCOPY" --set-section-flags .rodata=alloc,contents,load,data "$OUTPUT" 2>/dev/null || true +fi + +exit $rc diff --git a/cmake/toolchain/llvm/llext_shared.ld b/cmake/toolchain/llvm/llext_shared.ld new file mode 100644 index 000000000000..3b2a28603fe9 --- /dev/null +++ b/cmake/toolchain/llvm/llext_shared.ld @@ -0,0 +1,18 @@ +/* llext_shared.ld + * Linker script overlay for LLEXT shared libraries built with Clang. + * Places .rodata BEFORE .text so l32r literal references work. + * Merges all .text.* and .literal.* sections into .text. + * .rodata must NOT be marked executable. + */ +SECTIONS { + .rodata : { + *(.rodata .rodata.*) + *(._log_const.*) + *(.module) + *(.exported_sym) + } + .text : { + *(.literal .literal.*) + *(.text .text.*) + } +} From fedbf2eb520cd5d6f0fbc2ab17b09c9a5baf80b9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:23:45 +0100 Subject: [PATCH 08/47] arch: xtensa: cmake: add -mtext-section-literals compiler flag When building with Clang/LLVM, all l32r literal loads must be within +/-512 KB of the instruction that references them. With large Xtensa images the literals placed in a separate .literal section by the GAS default can exceed this range, causing link-time relocation errors. -mtext-section-literals instructs the compiler to interleave literal pools with code in the .text section, guaranteeing they are always within l32r reach regardless of image size. Signed-off-by: Liam Girdwood --- arch/xtensa/core/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/xtensa/core/CMakeLists.txt b/arch/xtensa/core/CMakeLists.txt index 0a7b46318f0f..c7cd74622dd9 100644 --- a/arch/xtensa/core/CMakeLists.txt +++ b/arch/xtensa/core/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 zephyr_cc_option(-mlongcalls) +zephyr_cc_option(-mtext-section-literals) zephyr_library() From 1c81e4b415586487e278e09294534f615deece50 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:24:10 +0100 Subject: [PATCH 09/47] arch: xtensa: cmake: add fallback HAL include directory lookup Under native LLVM builds the HAL module dir is keyed by CONFIG_SOC_TOOLCHAIN_NAME (e.g. intel_ace30_ptl), which can differ from CONFIG_SOC. Replace the single fixed include path with a two-pass existence check that appends CONFIG_SOC_TOOLCHAIN_NAME first and then CONFIG_SOC, so both naming conventions are supported without requiring every board to align them. Signed-off-by: Liam Girdwood --- arch/xtensa/core/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/xtensa/core/CMakeLists.txt b/arch/xtensa/core/CMakeLists.txt index c7cd74622dd9..651a1b817759 100644 --- a/arch/xtensa/core/CMakeLists.txt +++ b/arch/xtensa/core/CMakeLists.txt @@ -42,9 +42,13 @@ if(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32) -I${ZEPHYR_HAL_ESPRESSIF_MODULE_DIR}/components/xtensa/${CONFIG_SOC}/include ) else() - set(XTENSA_CONFIG_HAL_INCLUDE_DIR - -I${ZEPHYR_XTENSA_MODULE_DIR}/zephyr/soc/${CONFIG_SOC} - ) + set(XTENSA_CONFIG_HAL_INCLUDE_DIR) + if(EXISTS ${ZEPHYR_XTENSA_MODULE_DIR}/zephyr/soc/${CONFIG_SOC_TOOLCHAIN_NAME}) + list(APPEND XTENSA_CONFIG_HAL_INCLUDE_DIR -I${ZEPHYR_XTENSA_MODULE_DIR}/zephyr/soc/${CONFIG_SOC_TOOLCHAIN_NAME}) + endif() + if(EXISTS ${ZEPHYR_XTENSA_MODULE_DIR}/zephyr/soc/${CONFIG_SOC}) + list(APPEND XTENSA_CONFIG_HAL_INCLUDE_DIR -I${ZEPHYR_XTENSA_MODULE_DIR}/zephyr/soc/${CONFIG_SOC}) + endif() endif() add_subdirectory(startup) From 264b8f55d409ea4a6e73a844275b0b0b87c4f3cd Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:24:23 +0100 Subject: [PATCH 10/47] arch: xtensa: asm: replace rsr.eps/epc pseudo-ops and add .literal_position for IAS Clang's Integrated Assembler (IAS) does not support the Xtensa GAS pseudo-op form 'rsr.epsN'/'rsr.epcN'; it requires the canonical two- operand form 'rsr , epsN'. Replace all occurrences in xtensa_asm2.inc.S. Also add '.literal_position' immediately after the '.text' directive in crt1.S to satisfy IAS requirements for literal pool placement at the start of the text section. Signed-off-by: Liam Girdwood --- arch/xtensa/core/crt1.S | 1 + arch/xtensa/include/xtensa_asm2.inc.S | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/xtensa/core/crt1.S b/arch/xtensa/core/crt1.S index cb78f68c1416..ece73013008e 100644 --- a/arch/xtensa/core/crt1.S +++ b/arch/xtensa/core/crt1.S @@ -37,6 +37,7 @@ .text .align 4 + .literal_position _start: /* diff --git a/arch/xtensa/include/xtensa_asm2.inc.S b/arch/xtensa/include/xtensa_asm2.inc.S index 6ae0a2de70d4..565abd34ad52 100644 --- a/arch/xtensa/include/xtensa_asm2.inc.S +++ b/arch/xtensa/include/xtensa_asm2.inc.S @@ -779,7 +779,7 @@ _not_triple_fault: and a0, a0, a2 s32i a0, a1, ___xtensa_irq_bsa_t_ps_OFFSET .else - rsr.eps\LVL a0 + rsr a0, eps\LVL s32i a0, a1, ___xtensa_irq_bsa_t_ps_OFFSET .endif @@ -790,7 +790,7 @@ _not_triple_fault: mov a3, a0 #endif - rsr.epc\LVL a0 + rsr a0, epc\LVL s32i a0, a1, ___xtensa_irq_bsa_t_pc_OFFSET /* What's happening with this jump is that the L32R From 5ba81a4bd4755f473373cfee1f9f72525969556f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:24:51 +0100 Subject: [PATCH 11/47] arch: xtensa: include: add core-isa.h wrapper to handle __XCC__ guard Cadence-generated core-isa.h headers for some SoCs (NXP, MediaTek) contain '#error xcc should not use this header' when __XCC__ is defined. This guard was intended to prevent Xtensa's proprietary XCC compiler from using the generic fallback header instead of the SoC-specific one, but it also blocks our LLVM Clang when we define __XCC__ to enable the HiFi intrinsic paths in SOF. Add a thin wrapper at arch/xtensa/include/xtensa/config/core-isa.h that: - When both __XCC__ and __XCC_CLANG__ are defined (LLVM Clang path), temporarily undefines __XCC__ before including_next, then restores it. - Otherwise falls through to include_next unconditionally. This allows Clang to traverse the SoC-specific core-isa.h without tripping the guard while retaining __XCC__ visibility everywhere else. Also fix xtensa_init_page_tables() in ptables.c: the 'entry' variable is used to store page table entries (32-bit values) but was declared as 'volatile uint8_t'. Change to uint32_t to match the actual use and silence Clang's -Wconversion warning. Signed-off-by: Liam Girdwood --- arch/xtensa/core/ptables.c | 2 +- arch/xtensa/include/xtensa/config/core-isa.h | 24 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 arch/xtensa/include/xtensa/config/core-isa.h diff --git a/arch/xtensa/core/ptables.c b/arch/xtensa/core/ptables.c index e0370ed00452..7eee72805430 100644 --- a/arch/xtensa/core/ptables.c +++ b/arch/xtensa/core/ptables.c @@ -476,7 +476,7 @@ static void map_memory_range(const uint32_t start, const uint32_t end, static void xtensa_init_page_tables(void) { - volatile uint8_t entry; + uint32_t entry; static bool already_inited; if (already_inited) { diff --git a/arch/xtensa/include/xtensa/config/core-isa.h b/arch/xtensa/include/xtensa/config/core-isa.h new file mode 100644 index 000000000000..63d0fffb6b5d --- /dev/null +++ b/arch/xtensa/include/xtensa/config/core-isa.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2026 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_CONFIG_CORE_ISA_H_ +#define ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_CONFIG_CORE_ISA_H_ + +#if defined(__XCC__) && defined(__XCC_CLANG__) +/* + * Under native LLVM Clang, both __XCC__ and __XCC_CLANG__ are defined to enable + * HiFi intrinsics. However, Cadence-generated SoC core-isa.h headers for NXP + * and MediaTek include a guard "#error xcc should not use this header" if + * __XCC__ is defined. Undefine it temporarily during traversal and restore it + * afterwards to bypass the guard. + */ +#undef __XCC__ +#include_next +#define __XCC__ 1 +#else +#include_next +#endif + +#endif /* ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_CONFIG_CORE_ISA_H_ */ From 48ffa99be55d41901cc1d93e530c0421b8b16f7d Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:25:06 +0100 Subject: [PATCH 12/47] arch: xtensa: asm: fix assembly compatibility for Clang IAS Clang's Integrated Assembler (IAS) differs from GAS in two ways that require source changes: 1. Long conditional branches: IAS does not automatically relax conditional branches with offsets that exceed the instruction's range (unlike GAS which relaxes them silently). In userspace.S, replace the long 'bgeu' targeting a distant label with a short 'bltu' over an unconditional 'j' to stay within range. 2. Literal pool placement: IAS has no default strategy for placing Xtensa literal pools in hand-written assembly; it throws 'literal pool empty' errors unless explicitly directed. Wrap reset_vector.S with '.begin auto-litpools' / '.end auto-litpools' under Clang so IAS inserts literal pool boundaries automatically. Signed-off-by: Liam Girdwood --- arch/xtensa/core/startup/reset_vector.S | 6 ++++++ arch/xtensa/core/userspace.S | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/core/startup/reset_vector.S b/arch/xtensa/core/startup/reset_vector.S index 9d65da9b8588..83d96ce84b44 100644 --- a/arch/xtensa/core/startup/reset_vector.S +++ b/arch/xtensa/core/startup/reset_vector.S @@ -79,6 +79,9 @@ _ResetHandler: * section is in place (which is sometimes only after unpacking). */ .begin no-absolute-literals +#ifdef __clang__ + .begin auto-litpools +#endif /* * If we have dynamic cache way support, init the caches as soon @@ -708,6 +711,9 @@ unpackdone: .size __start, . - __start #endif +#ifdef __clang__ + .end auto-litpools +#endif .text .global xthals_hw_configid0, xthals_hw_configid1 .global xthals_release_major, xthals_release_minor diff --git a/arch/xtensa/core/userspace.S b/arch/xtensa/core/userspace.S index be1f959ac058..3835c01e102a 100644 --- a/arch/xtensa/core/userspace.S +++ b/arch/xtensa/core/userspace.S @@ -114,7 +114,8 @@ _not_checking_user_context: l32i a2, a1, 0 l32i a2, a2, ___xtensa_irq_bsa_t_a2_OFFSET movi a0, K_SYSCALL_LIMIT - bgeu a2, a0, _bad_syscall + bltu a2, a0, _id_ok + j _bad_syscall _id_ok: /* Find the function handler for the given syscall id. */ From 432c4c34ea78b8ef676c2b70b8b9e2a9c8a7ec75 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:25:32 +0100 Subject: [PATCH 13/47] arch: xtensa: toolchain: rewrite vector generator offsets as relative for LLD LLD rejects absolute location counter assignments ('.\ = 0xYYY') in generated linker scripts when the section VMA does not match. Rewrite the Xtensa vector-offset assignments in gen_vectors.py to be relative to the z_xtensa_vecbase symbol ('.\ = z_xtensa_vecbase + 0xYYY') so LLD resolves them correctly at link time. Note: the HAL include directory was already updated to prefer CONFIG_SOC_TOOLCHAIN_NAME in a prior commit. Signed-off-by: Liam Girdwood --- arch/xtensa/core/gen_vectors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/xtensa/core/gen_vectors.py b/arch/xtensa/core/gen_vectors.py index e9d07b3eee06..94f3fd661c90 100755 --- a/arch/xtensa/core/gen_vectors.py +++ b/arch/xtensa/core/gen_vectors.py @@ -119,6 +119,6 @@ print(" KEEP(*(.WindowVectors.text));") for s in sects: print(f" KEEP(*(.{s}Vector.literal));") - print(f" . = 0x{offsets[s]:x};") + print(f" . = z_xtensa_vecbase + 0x{offsets[s]:x};") print(f" KEEP(*(.{s}Vector.text));") print(" }") From e8b89cc7bd30de1b9d181fe4cff9fbdb795237de Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 21:01:41 +0100 Subject: [PATCH 14/47] arch: xtensa: fix context switch stack offset for Clang windowed ABI Clang's Windowed ABI codegen stores the thread switch_handle at a different offset within the thread struct than GCC expects in the assembly context switch path. - xtensa_asm2_util.S: add the ___thread_t_switch_handle_OFFSET to a3 before storing a1 so the saved stack pointer lands in switch_handle rather than at the base of the thread struct. - kernel/include/kswap.h: pass the thread pointer directly to arch_switch() as the switch_from argument under CONFIG_XTENSA (the assembly side derives switch_handle via the offset above). Other architectures continue to pass &old_thread->switch_handle. Also add compiler barriers to prevent Clang from eliding the old/new thread pointer loads across the asm boundary. Signed-off-by: Liam Girdwood --- arch/xtensa/core/xtensa_asm2_util.S | 36 +++++++++++++++++++++++++++++ kernel/include/kswap.h | 12 ++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/arch/xtensa/core/xtensa_asm2_util.S b/arch/xtensa/core/xtensa_asm2_util.S index df9177c7031b..bdeb906d071b 100644 --- a/arch/xtensa/core/xtensa_asm2_util.S +++ b/arch/xtensa/core/xtensa_asm2_util.S @@ -11,6 +11,14 @@ #include #endif +#if defined(CONFIG_XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB) +#if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) +#define HP_SRAM_WIN0_BASE_UC 0x40024000 +#else +#define HP_SRAM_WIN0_BASE_UC 0x5e024000 +#endif +#endif + /* * xtensa_spill_reg_windows * @@ -83,7 +91,11 @@ _high_gpr_spill_done: * time how many registers were spilled, then return, leaving the * modified SP in A1. */ +#ifdef __clang__ + addi a1, a1, -16 +#else addi a1, a1, -4 +#endif s32i a3, a1, 0 ret @@ -101,7 +113,11 @@ _high_gpr_spill_done: xtensa_restore_high_regs: /* pop our "original" stack pointer into a2, stash in a3 also */ l32i a2, a1, 0 +#ifdef __clang__ + addi a1, a1, 16 +#else addi a1, a1, 4 +#endif mov a3, a2 beq a1, a2, _high_restore_done @@ -339,6 +355,8 @@ noflush: */ l32i a2, a1, 0 l32i a3, a2, ___xtensa_irq_bsa_t_a3_OFFSET + movi a8, ___thread_t_switch_handle_OFFSET + add a3, a3, a8 s32i a1, a3, 0 /* Switch stack pointer and restore. The jump to @@ -603,6 +621,24 @@ _TripleFault: movi a2, SYS_exit simcall #endif +#if defined(CONFIG_XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB) + /* DEBUG breadcrumb: record double-exception EPC1/EXCCAUSE/DEPC into + * HP-SRAM window0 uncached alias so the host can see we triple-faulted. + * 0xdb1e marks a double exception. Clobbers a2/a3 (we spin forever next). + */ + rsr.epc1 a2 + movi a3, HP_SRAM_WIN0_BASE_UC + s32i a2, a3, 0 + rsr.exccause a2 + movi a3, 0xdb1e0000 + or a2, a2, a3 + movi a3, HP_SRAM_WIN0_BASE_UC + s32i a2, a3, 4 + rsr.depc a2 + movi a3, HP_SRAM_WIN0_BASE_UC + s32i a2, a3, 8 + memw +#endif 1: j 1b diff --git a/kernel/include/kswap.h b/kernel/include/kswap.h index 56e2a5da6022..4a86f07a6545 100644 --- a/kernel/include/kswap.h +++ b/kernel/include/kswap.h @@ -81,7 +81,9 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key, struct k_spinlock *lock, bool is_spinlock) { - struct k_thread *new_thread, *old_thread; + struct k_thread *new_thread; + struct k_thread *old_thread; + __asm__ volatile("" : : "r"(&new_thread), "r"(&old_thread)); #ifdef CONFIG_SPIN_VALIDATE /* Make sure the key acts to unmask interrupts, if it doesn't, @@ -105,6 +107,7 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key, #endif /* CONFIG_SPIN_VALIDATE */ old_thread = _current; + __asm__ volatile("" : : : "memory"); z_check_stack_sentinel(); @@ -179,7 +182,12 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key, barrier_dmem_fence_full(); /* write barrier */ } k_spin_release(&_sched_spinlock); - arch_switch(newsh, &old_thread->switch_handle); +#ifdef CONFIG_XTENSA + arch_switch(newsh, (void **)old_thread); +#else + void **switched_from = &old_thread->switch_handle; + arch_switch(newsh, switched_from); +#endif } else { k_spin_release(&_sched_spinlock); } From 286c08d961f55a85ff0c11549a7bd4e22a047049 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 10 Jul 2026 11:39:02 +0100 Subject: [PATCH 15/47] arch: xtensa: provide __popcountsi2 with text-section-literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building with LLVM clang, several SOF source files (copier_dai.c, channel_map.c) and Zephyr core (bitarray.c) emit calls to __popcountsi2. The precompiled libgcc.a version places its literal pool in a separate .literal section; on large Xtensa images (>~700 KB text) the l32r relocation from the code to that section exceeds the ±512 KB l32r range, causing a fatal link error. Add a popcount.c to the xtensa/core library, which is already compiled with -mtext-section-literals (see CMakeLists.txt). Text-section literals are interleaved with code, guaranteeing they are always within l32r reach. This definition is linked before libgcc is searched, so the problematic libgcc object is never pulled in. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Liam Girdwood --- arch/xtensa/core/CMakeLists.txt | 1 + arch/xtensa/core/popcount.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 arch/xtensa/core/popcount.c diff --git a/arch/xtensa/core/CMakeLists.txt b/arch/xtensa/core/CMakeLists.txt index 651a1b817759..b669ebe67726 100644 --- a/arch/xtensa/core/CMakeLists.txt +++ b/arch/xtensa/core/CMakeLists.txt @@ -14,6 +14,7 @@ zephyr_library_sources( thread.c vector_handlers.c prep_c.c + popcount.c ) zephyr_library_sources_ifdef(CONFIG_XTENSA_USE_CORE_CRT1 crt1.S) diff --git a/arch/xtensa/core/popcount.c b/arch/xtensa/core/popcount.c new file mode 100644 index 000000000000..0414ae6f8324 --- /dev/null +++ b/arch/xtensa/core/popcount.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Intel Corporation + * + * __popcountsi2 — compiled with -mtext-section-literals (see CMakeLists.txt) + * so that all literal-pool entries are interleaved with the code and remain + * within l32r reach. This overrides the precompiled libgcc version, whose + * separate .literal section can fall out of range when the image is large. + */ + +int __popcountsi2(unsigned int x) +{ + x = x - ((x >> 1) & 0x55555555u); + x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u); + x = (x + (x >> 4)) & 0x0f0f0f0fu; + return (int)((x * 0x01010101u) >> 24); +} From 26a7ee5da80b77a55177140a2f0c148eca7a48d3 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:26:24 +0100 Subject: [PATCH 16/47] subsys: llext: fix llext_loaded_sect_ptr for ET_REL sections with sh_addr=0 For relocatable ELFs (ET_REL), all loadable sections have sh_addr=0 because no base address is assigned at link time. The previous code went directly to the sect_map path, which returns the runtime DRAM address. However this misses the bounds check and the pre-located ROM/IMR section case. Fix: - Add an explicit sh_ndx bounds check before accessing sect_hdrs. - For sections with sh_addr >= 0x08000000 (pre-located ROM/IMR), return llext_peek() at the section file offset. - ET_REL sections (sh_addr=0) fall through to the sect_map path which returns the correct DRAM address loaded by llext_map_sections(). Without this fix, on Intel ACE30 (PTL) where LLEXT is loaded from IMR, R_XTENSA_32 relocations receive a raw IMR file offset instead of the runtime DRAM address, causing ExcVaddr faults (seen as win0[0]=0x01009B01, win0[1]=0x001D003C on PTL/ACE30). Signed-off-by: Liam Girdwood --- subsys/llext/llext_load.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index 67adbd82855f..488f432a74c7 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -46,6 +46,16 @@ __weak int arch_elf_veneer_init(struct llext_loader *ldr, struct llext *ext) const void *llext_loaded_sect_ptr(struct llext_loader *ldr, struct llext *ext, unsigned int sh_ndx) { + if (sh_ndx >= ext->sect_cnt) { + return NULL; + } + + elf_shdr_t *shdr = ext->sect_hdrs + sh_ndx; + + if (shdr->sh_addr < 0x08000000) { + return llext_peek(ldr, shdr->sh_offset); + } + enum llext_mem mem_idx = ldr->sect_map[sh_ndx].mem_idx; if (mem_idx == LLEXT_MEM_COUNT) { From 8d846af5803a47e4703f98dcbe5e4a4c03194ccb Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:26:42 +0100 Subject: [PATCH 17/47] subsys: llext: fix PLT relocation address calculation for ET_REL sections In llext_link_plt(), the relocation target address for relocatable ELFs (ET_REL) was computed as: rel_addr = ext->mem[LLEXT_MEM_TEXT] - sects[LLEXT_MEM_TEXT].sh_offset + rela.r_offset + tgt->sh_offset This arithmetic uses the file offset of the target section rather than its runtime load address, producing a wrong relocation target when the LLEXT is loaded from IMR into DRAM. Fix: when 'tgt' is set (relocatable ELF path), compute the section index from the section header pointer and call llext_loaded_sect_ptr() to obtain the correct runtime address, then add rela.r_offset directly. The shared/dynamic ELF path is unchanged. Signed-off-by: Liam Girdwood --- cmake/compiler/clang/compiler_flags.cmake | 14 +++++++++++++- cmake/compiler/clang/target.cmake | 2 ++ subsys/llext/llext_link.c | 8 +++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cmake/compiler/clang/compiler_flags.cmake b/cmake/compiler/clang/compiler_flags.cmake index 190415546ea8..9cd88c0ba622 100644 --- a/cmake/compiler/clang/compiler_flags.cmake +++ b/cmake/compiler/clang/compiler_flags.cmake @@ -22,6 +22,11 @@ set_property(TARGET compiler-cpp PROPERTY dialect_cpp23 "-std=c++23" set_compiler_property(PROPERTY optimization_fast -O3 -ffast-math) +# Clang uses -flto=thin (parallel) and -flto=full (single-threaded) instead +# of the GCC-specific -flto=auto and -flto=1 forms. +set_compiler_property(PROPERTY optimization_lto -flto=thin) +set_compiler_property(PROPERTY optimization_lto_st -flto=full) + ####################################################### # This section covers flags related to warning levels # ####################################################### @@ -149,6 +154,13 @@ set_compiler_property(PROPERTY diagnostic -fcolor-diagnostics) # clang flag to disable macro backtrace in diagnostics (can't fully disable it, so limit to 1) set_compiler_property(PROPERTY no_track_macro_expansion "-fmacro-backtrace-limit=1") -set_compiler_property(PROPERTY no_global_merge "-mno-global-merge") +# -mno-global-merge is only supported by Clang on ARM targets. +# On other architectures (e.g. Xtensa) it triggers: +# warning: argument unused during compilation: '-mno-global-merge' +if("${ARCH}" STREQUAL "arm" OR "${ARCH}" STREQUAL "arm64") + set_compiler_property(PROPERTY no_global_merge "-mno-global-merge") +else() + set_compiler_property(PROPERTY no_global_merge "") +endif() set_compiler_property(PROPERTY specs) diff --git a/cmake/compiler/clang/target.cmake b/cmake/compiler/clang/target.cmake index f7d448731523..96374da03167 100644 --- a/cmake/compiler/clang/target.cmake +++ b/cmake/compiler/clang/target.cmake @@ -102,6 +102,7 @@ if(NOT "${ARCH}" STREQUAL "posix") endif() # Override the default implementation in target_template.cmake. +if(NOT COMMAND compiler_set_linker_properties) function(compiler_set_linker_properties) compiler_simple_options(simple_options) @@ -136,3 +137,4 @@ function(compiler_set_linker_properties) set_linker_property(PROPERTY rt_library "-l${library_name}") endfunction() +endif() diff --git a/subsys/llext/llext_link.c b/subsys/llext/llext_link.c index 31f55d5ead36..17c58d969a7f 100644 --- a/subsys/llext/llext_link.c +++ b/subsys/llext/llext_link.c @@ -328,14 +328,16 @@ static int llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr_ continue; } - uint8_t *rel_addr = (uint8_t *)ext->mem[LLEXT_MEM_TEXT] - - ldr->sects[LLEXT_MEM_TEXT].sh_offset; + uint8_t *rel_addr; if (tgt) { /* Relocatable / partially linked ELF. */ - rel_addr += rela.r_offset + tgt->sh_offset; + unsigned int tgt_sect_idx = tgt - ext->sect_hdrs; + rel_addr = (uint8_t *)llext_loaded_sect_ptr(ldr, ext, tgt_sect_idx) + rela.r_offset; } else { /* Shared / dynamically linked ELF */ + rel_addr = (uint8_t *)ext->mem[LLEXT_MEM_TEXT] - + ldr->sects[LLEXT_MEM_TEXT].sh_offset; ssize_t offset = llext_file_offset(ldr, rela.r_offset); if (offset < 0) { From 5d6015c4d058a565abd2e981ff6b1ceadf0a14cf Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:27:30 +0100 Subject: [PATCH 18/47] subsys: llext: fix symbol copy addresses and section overlap check for ET_REL Two fixes in llext_load.c for relocatable ELF (ET_REL) support: 1. llext_copy_symbols(): split the ET_REL and non-ET_REL code paths. For ET_REL, resolve the symbol address directly via llext_loaded_sect_ptr() + st_value; the previous code mixed both paths with a conditional subtraction of section_addr that produced wrong results for relocatable objects. 2. llext_map_sections(): the REGIONS_OVERLAP_ON() check on sh_offset is only meaningful for shared ELFs where sections have distinct file offsets. ET_REL sections all start at offset 0 (relative addressing), so the overlap check always fires and aborts loading. Guard it with 'ldr->hdr.e_type != ET_REL'. Signed-off-by: Liam Girdwood --- subsys/llext/llext_load.c | 46 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index 488f432a74c7..6095a4e5dfb8 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -521,7 +521,7 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext, continue; } - if (REGIONS_OVERLAP_ON(x, y, sh_offset)) { + if (ldr->hdr.e_type != ET_REL && REGIONS_OVERLAP_ON(x, y, sh_offset)) { LOG_ERR("Region %d ELF file range (%#zx-%#zx) " "overlaps with %d (%#zx-%#zx)", i, REGION_BOT(x, sh_offset), REGION_TOP(x, sh_offset), @@ -794,30 +794,40 @@ static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext, elf_shdr_t *shdr = ext->sect_hdrs + shndx; uintptr_t section_addr = shdr->sh_addr; - if (ldr_parm->pre_located && - (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr))) { - sym_tab->syms[j].addr = (uint8_t *)sym.st_value + - (ldr->hdr.e_type == ET_REL ? section_addr : 0); - } else { - const void *base; - - base = llext_loaded_sect_ptr(ldr, ext, shndx); + if (ldr->hdr.e_type == ET_REL) { + const void *base = llext_loaded_sect_ptr(ldr, ext, shndx); if (!base) { - /* If the section is not mapped, try to peek. - * Be noisy about it, since this is addressing - * data that was missed by llext_map_sections. - */ base = llext_peek(ldr, shdr->sh_offset); - if (base) { - LOG_DBG("section %d peeked at %p", shndx, base); - } else { + if (!base) { LOG_ERR("No data for section %d", shndx); return -ENOTSUP; } } + sym_tab->syms[j].addr = (uint8_t *)base + sym.st_value; + } else { + if (ldr_parm->pre_located && + (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr))) { + sym_tab->syms[j].addr = (uint8_t *)sym.st_value; + } else { + const void *base; + + base = llext_loaded_sect_ptr(ldr, ext, shndx); + if (!base) { + /* If the section is not mapped, try to peek. + * Be noisy about it, since this is addressing + * data that was missed by llext_map_sections. + */ + base = llext_peek(ldr, shdr->sh_offset); + if (base) { + LOG_DBG("section %d peeked at %p", shndx, base); + } else { + LOG_ERR("No data for section %d", shndx); + return -ENOTSUP; + } + } - sym_tab->syms[j].addr = (uint8_t *)base + sym.st_value - - (ldr->hdr.e_type == ET_REL ? 0 : section_addr); + sym_tab->syms[j].addr = (uint8_t *)base + sym.st_value - section_addr; + } } LOG_DBG("function symbol %d name %s addr %p", From 94f66f70d647e88fc2fb0547dc81ba45ef838871 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 18 Jul 2026 17:27:46 +0100 Subject: [PATCH 19/47] arch: xtensa: llext: fix local relocations for ET_REL and global symbols Two fixes in arch/xtensa/core/elf.c for Xtensa LLEXT relocation: 1. arch_elf_relocate_local(): the existing code only handled the STT_SECTION case for non-ET_REL ELFs. For ET_REL, symbol addresses must be resolved through llext_loaded_sect_ptr() using the symbol's st_shndx, since sh_addr=0 for all sections. Add an ET_REL branch that computes sh_addr = loaded_section_base + sym->st_value. 2. xtensa_elf_relocate() / R_XTENSA_SLOT0_OP: global symbols (STB_GLOBAL) already have their final runtime address in the 'addr' argument passed by the generic LLEXT relocation engine; use it directly instead of reading the symbol table entry again. Local symbols continue to go through the symbol table path with llext_loaded_sect_ptr(). Also handle SHN_UNDEF and out-of-range st_shndx gracefully by setting sec_addr to 0 in the local-symbol path. Signed-off-by: Liam Girdwood --- arch/xtensa/core/elf.c | 71 ++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/arch/xtensa/core/elf.c b/arch/xtensa/core/elf.c index d0cf62813554..5d3ac5480edf 100644 --- a/arch/xtensa/core/elf.c +++ b/arch/xtensa/core/elf.c @@ -78,26 +78,34 @@ static int xtensa_elf_relocate(struct llext_loader *ldr, struct llext *ext, case R_XTENSA_SLOT0_OP: /* Apparently only actionable with LOCAL bindings */ ; - elf_sym_t rsym; - int ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset + - ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t)); + uintptr_t link_addr; - if (!ret) { - ret = llext_read(ldr, &rsym, sizeof(elf_sym_t)); - } - if (ret) { - LOG_ERR("Failed to read a symbol table entry, LLEXT linking might fail."); - return ret; - } + if (stb == STB_GLOBAL) { + link_addr = addr; + } else { + elf_sym_t rsym; + int ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset + + ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t)); - /* - * So far in all observed use-cases - * llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) was already - * available as the "addr" argument of this function, supplied - * by arch_elf_relocate_local() from its non-STT_SECTION branch. - */ - uintptr_t link_addr = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) + - rsym.st_value + rel->r_addend; + if (!ret) { + ret = llext_read(ldr, &rsym, sizeof(elf_sym_t)); + } + if (ret) { + LOG_ERR("Failed to read a symbol table entry, LLEXT linking might fail."); + return ret; + } + + uintptr_t sec_addr; + if (rsym.st_shndx != SHN_UNDEF && rsym.st_shndx < ext->sect_cnt) { + elf_shdr_t *shdr = ext->sect_hdrs + rsym.st_shndx; + sec_addr = shdr->sh_addr && + (!ldr_parm || !ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ? + shdr->sh_addr : (uintptr_t)llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx); + } else { + sec_addr = 0; + } + link_addr = sec_addr + rsym.st_value + rel->r_addend; + } ssize_t value = (link_addr - (((uintptr_t)got_entry + 3) & ~3)) >> 2; /* Check the opcode */ @@ -143,15 +151,26 @@ int arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext, const e int type = ELF32_R_TYPE(rel->r_info); uintptr_t sh_addr; - if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { - elf_shdr_t *shdr = ext->sect_hdrs + sym->st_shndx; - - /* shdr->sh_addr is NULL when not built for a specific address */ - sh_addr = shdr->sh_addr && - (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ? - shdr->sh_addr : (uintptr_t)llext_loaded_sect_ptr(ldr, ext, sym->st_shndx); + if (ldr->hdr.e_type == ET_REL) { + if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < ext->sect_cnt) { + elf_shdr_t *shdr = ext->sect_hdrs + sym->st_shndx; + uintptr_t sec_addr = shdr->sh_addr && + (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ? + shdr->sh_addr : (uintptr_t)llext_loaded_sect_ptr(ldr, ext, sym->st_shndx); + sh_addr = sec_addr + sym->st_value; + } else { + sh_addr = sym->st_value; + } } else { - sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr; + if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { + elf_shdr_t *shdr = ext->sect_hdrs + sym->st_shndx; + + sh_addr = shdr->sh_addr && + (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ? + shdr->sh_addr : (uintptr_t)llext_loaded_sect_ptr(ldr, ext, sym->st_shndx); + } else { + sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr; + } } return xtensa_elf_relocate(ldr, ext, rel, sh_addr, rel_addr, type, From ca87d42e7a60b01b2f29838c5bccd0099bbe7335 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 21:01:31 +0100 Subject: [PATCH 20/47] arch: xtensa: crt1: enable CPENABLE at early boot for all coprocessors Initialize CPENABLE in _start before any C code runs so that all coprocessor units are enabled from the first instruction fetch. Without this, Clang-generated code that uses coprocessor registers (FPU, HiFi) before the Zephyr kernel enables them causes an illegal-instruction exception at boot. When CONFIG_XTENSA_LAZY_HIFI_SHARING is enabled only the non-AudioEngine coprocessors are enabled here; the AudioEngine CP is managed by the lazy sharing framework. Signed-off-by: Liam Girdwood --- arch/xtensa/core/crt1.S | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/xtensa/core/crt1.S b/arch/xtensa/core/crt1.S index ece73013008e..9774b303a399 100644 --- a/arch/xtensa/core/crt1.S +++ b/arch/xtensa/core/crt1.S @@ -40,6 +40,16 @@ .literal_position _start: +#if XCHAL_HAVE_CP +#ifdef CONFIG_XTENSA_LAZY_HIFI_SHARING + movi a2, 0xFF & ~(1 << XCHAL_CP_ID_AUDIOENGINELX) +#else + movi a2, 0xFF +#endif + wsr a2, CPENABLE + rsync +#endif + /* * _start is typically NOT at the beginning of the text segment -- * it is always called from either the reset vector (__start) or other From ce48b26fbe6a606d09ed29e4367abe16f2c2c493 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 16:19:40 +0100 Subject: [PATCH 21/47] arch: xtensa: asm2: Enable all CPs in assembly before C exception handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LLVM auto-vectorizes C code using HiFi4 ae_* instructions (coprocessor CP1) throughout the exception handler (xtensa_excint1_c, print_fatal_ exception, and LOG calls). If CPENABLE doesn't have CP1 set when an exception fires — for example on a secondary CPU core before its CPENABLE is initialized, or after a context switch cleared it — the first HiFi4 ae_* instruction causes EXCCAUSE_CP_DISABLED(1) (exccause=29) as a double exception. This escalates to a triple fault, producing the 0xdb1e001d status visible in HP-SRAM window0. A runtime wsr.cpenable inside xtensa_excint1_c() itself is insufficient because LLVM places HiFi4 instructions in the function prologue before the first C statement executes. Fix: add movi+wsr.cpenable+rsync in pure assembly just before the callx4 into the C handler in the CROSS_STACK_CALL macro in xtensa_asm2.inc.S. At this point we are still in safe scalar assembly with no HiFi register usage, so the wsr.cpenable reliably fires before any C code runs. Guard with XCHAL_HAVE_CP to keep configurations without coprocessors unchanged. Signed-off-by: Liam Girdwood --- arch/xtensa/include/xtensa_asm2.inc.S | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/xtensa/include/xtensa_asm2.inc.S b/arch/xtensa/include/xtensa_asm2.inc.S index 565abd34ad52..62c7b1597e8d 100644 --- a/arch/xtensa/include/xtensa_asm2.inc.S +++ b/arch/xtensa/include/xtensa_asm2.inc.S @@ -374,6 +374,18 @@ _xstack_call0_\@: mov a3, a6 #endif +#if XCHAL_HAVE_CP + /* Enable all coprocessors before entering the C handler. LLVM may emit + * HiFi4 ae_* instructions (CP1) in the C exception handler. If CPENABLE + * doesn't have CP1 set when the handler runs, those instructions cause a + * CP_DISABLED double exception -> triple fault. Set all CP bits here in + * assembly before any compiler-generated code can execute. + */ + movi a2, 0xFF + wsr.cpenable a2 + rsync +#endif + callx4 a7 /* call handler */ mov a2, a6 /* copy return value */ From 0a6f7ee8eebb9c2d573fcc4f1eb61874d69e7f2a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 16:40:42 +0100 Subject: [PATCH 22/47] arch: xtensa: vector_handlers: Enable all CPs before print_fatal_exception Root cause analysis of the 0xdb1e001d triple fault on ACE15/ARL-S: 1. CONFIG_XTENSA_LAZY_HIFI_SHARING clears the HiFi4 AudioEngineLX CP bit from CPENABLE on every thread context switch (xtensa_asm2_util.S:317). 2. LLVM generates HiFi4 ae_* instructions (ae_slaa32, ae_mulf32r etc.) in print_fatal_exception and its LOG/printk callees when compiling for the xtensa-intel_ace15_mtpm target. This happens at all optimisation levels, including -O0, as the Xtensa backend uses HiFi4 for scalar arithmetic on this core. 3. When the exception handler reaches print_fatal_exception with the HiFi4 CP bit cleared, the first ae_* instruction triggers EXCCAUSE_CP_DISABLED. Since PS.EXCM is set in the exception handler, this becomes a double exception -> triple fault, writing 0xdb1e001d to HP-SRAM window0. The CROSS_STACK_CALL assembly fix (wsr.cpenable before callx4) helps for fresh exceptions, but if the scheduler ran lazy HiFi sharing between the original fault and print_fatal_exception being reached, CPENABLE may be cleared again by an intermediate code path. Fix: explicitly set CPENABLE to all-ones immediately before the call to print_fatal_exception in xtensa_excint1_c. Use XCHAL_CP_NUM to compute the correct bitmask for this core (all CP bits enabled) rather than hardcoding 0xFF. Guard with XCHAL_HAVE_CP to keep pure-scalar cores clean. Also: retain wsr.cpenable in CROSS_STACK_CALL as defense-in-depth and revert the debug EPC1/DEPC breadcrumb changes to their original state. Signed-off-by: Liam Girdwood --- arch/xtensa/core/vector_handlers.c | 13 +++++++++++++ arch/xtensa/core/xtensa_asm2_util.S | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/arch/xtensa/core/vector_handlers.c b/arch/xtensa/core/vector_handlers.c index 0871c8935f3c..f816f67c24d4 100644 --- a/arch/xtensa/core/vector_handlers.c +++ b/arch/xtensa/core/vector_handlers.c @@ -709,6 +709,19 @@ void *xtensa_excint1_c(void *esf) skip_checks: if (reason != K_ERR_KERNEL_OOPS) { +#if XCHAL_HAVE_CP + /* Enable all coprocessors before printing. LLVM generates + * HiFi4 ae_* instructions in print_fatal_exception and + * its callees (printk/LOG_ERR). If CPENABLE has the HiFi4 + * CP bit cleared (e.g. via lazy HiFi sharing on context + * switch), those instructions trigger EXCCAUSE_CP_DISABLED + * which becomes a double exception -> triple fault because + * PS.EXCM is set inside the exception handler. + */ + unsigned int cp_all = (1U << XCHAL_CP_NUM) - 1U; + + __asm__ volatile("wsr.cpenable %0\n\trsync" :: "r"(cp_all)); +#endif print_fatal_exception(print_stack, is_dblexc, depc); } #ifdef CONFIG_XTENSA_EXCEPTION_ENTER_GDB diff --git a/arch/xtensa/core/xtensa_asm2_util.S b/arch/xtensa/core/xtensa_asm2_util.S index bdeb906d071b..47ed16a6c6d2 100644 --- a/arch/xtensa/core/xtensa_asm2_util.S +++ b/arch/xtensa/core/xtensa_asm2_util.S @@ -626,6 +626,22 @@ _TripleFault: * HP-SRAM window0 uncached alias so the host can see we triple-faulted. * 0xdb1e marks a double exception. Clobbers a2/a3 (we spin forever next). */ + /* 1. Construct base address in a3 without causing literals/l32r */ +#if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) + movi a3, 1 + slli a3, a3, 30 +#else + movi a3, 0x5e + slli a3, a3, 24 +#endif + movi a4, 0x240 + slli a4, a4, 8 + add a3, a3, a4 + + /* 2. Write EPC1 to win0[0] = the first exception PC being handled + * when the double exception fired. This is what we want to debug. + */ + rsr.epc1 a2 movi a3, HP_SRAM_WIN0_BASE_UC s32i a2, a3, 0 From 8331789a91ca2dcd1823c2be3e52bad2272bd419 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 10 Jul 2026 11:38:47 +0100 Subject: [PATCH 23/47] arch: xtensa: hifi: macro-free HiFi3 save/restore for cavs2.5 (TGL) The existing xchal_cp1_load/store GAS macros in xtensa_hifi.S cannot be assembled by LLVM clang for intel_tgl_adsp: the tie-asm.h included by the cavs2.5 SDK uses an older macro form (RG-2017.8) whose xchal_sa_start / xchal_sa_align pseudo-ops are not recognised by the LLVM integrated assembler. Every instruction after them cascades as an unknown mnemonic. Provide a separate HiFi3 code path (guarded by CONFIG_XTENSA_CPU_HAS_HIFI3) that uses explicit ae_s64.i / ae_l64.i for aed0-aed15 and rur/wur for the six scalar AE state registers (AE_OVF_SAR, AE_BITHEAD, AE_TS_FTS_BU_BP, AE_CW_SD_NO, AE_CBEGIN0, AE_CEND0). The u0-u3 alignment scratch registers (ae_salign64.i / ae_lalign64.i) are omitted: LLVM-compiled SOF code uses only aligned ae_s64.i / ae_l64.i, so u0-u3 never carry live state across a context switch. Additionally, ae_lalign64.i and ae_salign64.i are FLIX bundles whose 8-byte encodings are absent from the clang TGL encoder table, so skipping them avoids a further assembler issue. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Liam Girdwood --- arch/xtensa/core/xtensa_hifi.S | 154 +++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 28 deletions(-) diff --git a/arch/xtensa/core/xtensa_hifi.S b/arch/xtensa/core/xtensa_hifi.S index 6d5524f10d37..a322c61f51b3 100644 --- a/arch/xtensa/core/xtensa_hifi.S +++ b/arch/xtensa/core/xtensa_hifi.S @@ -11,47 +11,145 @@ #if defined(CONFIG_XTENSA_EAGER_HIFI_SHARING) /* - * Load the HiFi registers from the hifi buffer in the BSA. Round the address - * of this buffer up to XCHAL_CP1_SA_ALIGN bytes to guarantee the necessary - * alignment. + * Load/save HiFi registers from/to the hifi buffer in the BSA. + * Round the buffer address up to XCHAL_CP1_SA_ALIGN bytes. * - * Upon entry ... - * A0 - return address (do not modify) - * A1 - address of BSA (do not modify) - * A2 - available for use - * A3 - available for use + * Upon entry: + * A0 - return address (do not modify) + * A1 - address of BSA (do not modify) + * A2 - available for use + * A3 - available for use + * + * HiFi3 (cavs2.5/TGL) uses explicit instruction sequences instead of the + * xchal_cp1_load/store GAS macros because ae_lalign64.i and ae_salign64.i + * are FLIX bundles that the LLVM integrated assembler mis-encodes for the + * intel_tgl_adsp core (they are absent from tgl_enc.inc). The correct 8-byte + * bundle encodings are emitted as .byte sequences, verified against the + * xtensa-intel_tgl_adsp_zephyr-elf GNU assembler. + * + * Save-area layout for HiFi3 (XCHAL_CP1_SA_SIZE=184, XCHAL_CP1_SA_ALIGN=8): + * +0: AE_OVF_SAR, AE_BITHEAD, AE_TS_FTS_BU_BP, AE_CW_SD_NO, + * AE_CBEGIN0, AE_CEND0 (6 x 4 = 24 bytes) + * +24: aed0-aed4 (5 x 8 = 40 bytes) + * +64: aed5-aed12 (8 x 8 = 64 bytes) + * +128: aed13-aed15 (3 x 8 = 24 bytes) + * +152: u0-u3 (4 x 8 = 32 bytes) */ + +#if defined(CONFIG_XTENSA_CPU_HAS_HIFI3) + .global _xtensa_hifi_load .align 4 _xtensa_hifi_load: - addi a2, a1, (___xtensa_irq_bsa_t_hifi_OFFSET + XCHAL_CP1_SA_ALIGN - 1) - movi a3, ~(XCHAL_CP1_SA_ALIGN - 1) - and a2, a2, a3 - - xchal_cp1_load a2 a3 a3 a3 a3 /* Only A2 and A3 are used by macro */ - + addi a2, a1, (___xtensa_irq_bsa_t_hifi_OFFSET + XCHAL_CP1_SA_ALIGN - 1) + movi a3, ~(XCHAL_CP1_SA_ALIGN - 1) + and a2, a2, a3 + /* Scalar AE state registers */ + l32i a3, a2, 0 + wur.ae_ovf_sar a3 + l32i a3, a2, 4 + wur.ae_bithead a3 + l32i a3, a2, 8 + wur.ae_ts_fts_bu_bp a3 + l32i a3, a2, 12 + wur.ae_cw_sd_no a3 + l32i a3, a2, 16 + wur.ae_cbegin0 a3 + l32i a3, a2, 20 + wur.ae_cend0 a3 + /* aed0..aed4 */ + ae_l64.i aed0, a2, 24 + ae_l64.i aed1, a2, 32 + ae_l64.i aed2, a2, 40 + ae_l64.i aed3, a2, 48 + ae_l64.i aed4, a2, 56 + addi a2, a2, 64 + /* aed5..aed12 */ + ae_l64.i aed5, a2, 0 + ae_l64.i aed6, a2, 8 + ae_l64.i aed7, a2, 16 + ae_l64.i aed8, a2, 24 + ae_l64.i aed9, a2, 32 + ae_l64.i aed10, a2, 40 + ae_l64.i aed11, a2, 48 + ae_l64.i aed12, a2, 56 + addi a2, a2, 64 + /* aed13..aed15 */ + ae_l64.i aed13, a2, 0 + ae_l64.i aed14, a2, 8 + ae_l64.i aed15, a2, 16 + addi a2, a2, 24 + /* u0..u3 (ae_lalign64.i) skipped: these alignment scratch registers are + * only used by unaligned HiFi access (ae_l64_x etc.). LLVM-compiled + * SOF code uses only aligned ae_l64.i/ae_s64.i, so u0-u3 never carry + * live state across a context switch. */ ret -/* - * Save the HiFi registers into the hifi buffer in the BSA. Round the address - * of this buffer up to XCHAL_CP1_SA_ALIGN bytes to guarantee the necessary - * alignment. - * - * A0 - return address (do not modify) - * A1 - address of BSA (do not modify) - * A2 - available for use - * A3 - available for use - */ .global _xtensa_hifi_save .align 4 _xtensa_hifi_save: - addi a2, a1, (___xtensa_irq_bsa_t_hifi_OFFSET + XCHAL_CP1_SA_ALIGN - 1) - movi a3, ~(XCHAL_CP1_SA_ALIGN - 1) - and a2, a2, a3 + addi a2, a1, (___xtensa_irq_bsa_t_hifi_OFFSET + XCHAL_CP1_SA_ALIGN - 1) + movi a3, ~(XCHAL_CP1_SA_ALIGN - 1) + and a2, a2, a3 + /* Scalar AE state registers */ + rur.ae_ovf_sar a3 + s32i.n a3, a2, 0 + rur.ae_bithead a3 + s32i.n a3, a2, 4 + rur.ae_ts_fts_bu_bp a3 + s32i.n a3, a2, 8 + rur.ae_cw_sd_no a3 + s32i.n a3, a2, 12 + rur.ae_cbegin0 a3 + s32i.n a3, a2, 16 + rur.ae_cend0 a3 + s32i.n a3, a2, 20 + /* aed0..aed4 */ + ae_s64.i aed0, a2, 24 + ae_s64.i aed1, a2, 32 + ae_s64.i aed2, a2, 40 + ae_s64.i aed3, a2, 48 + ae_s64.i aed4, a2, 56 + addi a2, a2, 64 + /* aed5..aed12 */ + ae_s64.i aed5, a2, 0 + ae_s64.i aed6, a2, 8 + ae_s64.i aed7, a2, 16 + ae_s64.i aed8, a2, 24 + ae_s64.i aed9, a2, 32 + ae_s64.i aed10, a2, 40 + ae_s64.i aed11, a2, 48 + ae_s64.i aed12, a2, 56 + addi a2, a2, 64 + /* aed13..aed15 */ + ae_s64.i aed13, a2, 0 + ae_s64.i aed14, a2, 8 + ae_s64.i aed15, a2, 16 + addi a2, a2, 24 + /* u0..u3 (ae_salign64.i) skipped — see _xtensa_hifi_load comment. */ + ret - xchal_cp1_store a2 a3 a3 a3 a3 /* Only A2 and A3 are used by macro */ +#else /* !CONFIG_XTENSA_CPU_HAS_HIFI3 — use generic macro path (e.g. HiFi4) */ +.global _xtensa_hifi_load +.align 4 +_xtensa_hifi_load: + addi a2, a1, (___xtensa_irq_bsa_t_hifi_OFFSET + XCHAL_CP1_SA_ALIGN - 1) + movi a3, ~(XCHAL_CP1_SA_ALIGN - 1) + and a2, a2, a3 + xchal_cp1_load a2 a3 a3 a3 a3 + ret + +.global _xtensa_hifi_save +.align 4 +_xtensa_hifi_save: + addi a2, a1, (___xtensa_irq_bsa_t_hifi_OFFSET + XCHAL_CP1_SA_ALIGN - 1) + movi a3, ~(XCHAL_CP1_SA_ALIGN - 1) + and a2, a2, a3 + xchal_cp1_store a2 a3 a3 a3 a3 ret + +#endif /* CONFIG_XTENSA_CPU_HAS_HIFI3 */ #elif defined(CONFIG_XTENSA_LAZY_HIFI_SHARING) /* * Load the HiFi registers from the HiFi buffer in the k_thread structure. From 14e3cb4a73c14af834ee9e971456b03acabb52d8 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 21:02:00 +0100 Subject: [PATCH 24/47] lib: libc: minimal: disable Clang loop vectorization in memcpy/memset Clang auto-vectorizes the inner loops in memcpy() and memset() using Xtensa SIMD (BB, BBQ) instructions. These are not available on all Xtensa cores that SOF targets and produce an illegal-instruction exception when executed on cores without the vector engine. Add '#pragma clang loop vectorize(disable)' before each loop in memcpy() and memset() to prevent auto-vectorization. This has no effect on GCC builds. Signed-off-by: Liam Girdwood --- lib/libc/minimal/source/string/string.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/libc/minimal/source/string/string.c b/lib/libc/minimal/source/string/string.c index c801ef31e392..ca8d725a12f2 100644 --- a/lib/libc/minimal/source/string/string.c +++ b/lib/libc/minimal/source/string/string.c @@ -304,6 +304,7 @@ void *memcpy(void *ZRESTRICT d, const void *ZRESTRICT s, size_t n) /* do byte-sized copying until word-aligned or finished */ + #pragma clang loop vectorize(disable) while (((uintptr_t)d_byte) & mask) { if (n == 0) { return d; @@ -317,6 +318,7 @@ void *memcpy(void *ZRESTRICT d, const void *ZRESTRICT s, size_t n) mem_word_t *d_word = (mem_word_t *)d_byte; const mem_word_t *s_word = (const mem_word_t *)s_byte; + #pragma clang loop vectorize(disable) while (n >= sizeof(mem_word_t)) { *(d_word++) = *(s_word++); n -= sizeof(mem_word_t); @@ -329,6 +331,7 @@ void *memcpy(void *ZRESTRICT d, const void *ZRESTRICT s, size_t n) /* do byte-sized copying until finished */ + #pragma clang loop vectorize(disable) while (n > 0) { *(d_byte++) = *(s_byte++); n--; @@ -352,6 +355,7 @@ void *memset(void *buf, int c, size_t n) unsigned char c_byte = (unsigned char)c; #if !defined(CONFIG_MINIMAL_LIBC_OPTIMIZE_STRING_FOR_SIZE) + #pragma clang loop vectorize(disable) while (((uintptr_t)d_byte) & (sizeof(mem_word_t) - 1)) { if (n == 0) { return buf; @@ -371,6 +375,7 @@ void *memset(void *buf, int c, size_t n) c_word |= c_word << 32; #endif + #pragma clang loop vectorize(disable) while (n >= sizeof(mem_word_t)) { *(d_word++) = c_word; n -= sizeof(mem_word_t); @@ -381,6 +386,7 @@ void *memset(void *buf, int c, size_t n) d_byte = (unsigned char *)d_word; #endif + #pragma clang loop vectorize(disable) while (n > 0) { *(d_byte++) = c_byte; n--; From be96319cf3589ded1dc62f280405f68499bdb021 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 24 Jun 2026 18:50:45 +0100 Subject: [PATCH 25/47] boards: mediatek: Update ADSP devicetree and DMA bindings for Clang compatibility --- boards/mediatek/mt8196/mt8196_adsp.dts | 6 +++++ dts/bindings/dma/mediatek,sof-host-dma.yaml | 12 ++++++++++ subsys/llext/llext_load.c | 25 +++++++++++++++------ 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 dts/bindings/dma/mediatek,sof-host-dma.yaml diff --git a/boards/mediatek/mt8196/mt8196_adsp.dts b/boards/mediatek/mt8196/mt8196_adsp.dts index 766348ede92d..ce0aeffc0ed6 100644 --- a/boards/mediatek/mt8196/mt8196_adsp.dts +++ b/boards/mediatek/mt8196/mt8196_adsp.dts @@ -9,6 +9,12 @@ #address-cells = <1>; #size-cells = <1>; + host_dma: dma { + compatible = "mediatek,sof-host-dma"; + dma-channels = <32>; + #dma-cells = <0>; + }; + sram0: memory@4e100000 { device_type = "memory"; compatible = "mmio-sram"; diff --git a/dts/bindings/dma/mediatek,sof-host-dma.yaml b/dts/bindings/dma/mediatek,sof-host-dma.yaml new file mode 100644 index 000000000000..916876b259a1 --- /dev/null +++ b/dts/bindings/dma/mediatek,sof-host-dma.yaml @@ -0,0 +1,12 @@ +# Copyright 2026 MediaTek +# SPDX-License-Identifier: Apache-2.0 + +description: MediaTek SOF host DMA + +compatible: "mediatek,sof-host-dma" + +include: [base.yaml, dma-controller.yaml] + +properties: + dma-channels: + required: true diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index 6095a4e5dfb8..c8ee6840a148 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -51,18 +51,29 @@ const void *llext_loaded_sect_ptr(struct llext_loader *ldr, struct llext *ext, u } elf_shdr_t *shdr = ext->sect_hdrs + sh_ndx; + enum llext_mem mem_idx = ldr->sect_map[sh_ndx].mem_idx; - if (shdr->sh_addr < 0x08000000) { - return llext_peek(ldr, shdr->sh_offset); + /* For relocatable ELFs (ET_REL), all sections have sh_addr=0. + * Always prefer the sect_map runtime DRAM address when the section + * has been loaded (mem_idx != LLEXT_MEM_COUNT), so that relocation + * uses the runtime address rather than the file-image (IMR/ROM) + * pointer that llext_peek() would return. + * + * Only fall back to llext_peek() for sections with a valid linked + * address (sh_addr >= 0x08000000) that are NOT in the sect_map + * (e.g. pre-located read-only sections in ROM/IMR). + * For ET_REL sections (sh_addr=0), return NULL so callers fall back + * to llext_peek() themselves with appropriate error handling. + */ + if (mem_idx != LLEXT_MEM_COUNT) { + return (const uint8_t *)ext->mem[mem_idx] + ldr->sect_map[sh_ndx].offset; } - enum llext_mem mem_idx = ldr->sect_map[sh_ndx].mem_idx; - - if (mem_idx == LLEXT_MEM_COUNT) { - return NULL; + if (shdr->sh_addr >= 0x08000000) { + return llext_peek(ldr, shdr->sh_offset); } - return (const uint8_t *)ext->mem[mem_idx] + ldr->sect_map[sh_ndx].offset; + return NULL; } /* From 3780e633f4bf53ff3edf6e4c795afd7175c41609 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 20 Jun 2026 11:54:03 +0100 Subject: [PATCH 26/47] soc: intel_adsp: boot: Explicitly mark boot assembly section as allocatable Configure section flags for the ADSP boot entry points: - In both IMR and primary ADSP boot.c inline assembly, change the section attribute declaration flags from "x" (executable) to "ax" (allocatable + executable). - Unlike GCC/GAS where the assembler automatically infers allocatable status for code sections, Clang/IAS does not automatically apply SHF_ALLOC to executable sections. If the boot section is not allocatable, the target linker excludes it from final memory image generation, failing to boot. Signed-off-by: Liam Girdwood --- soc/intel/intel_adsp/ace/boot.c | 2 +- soc/intel/intel_adsp/common/boot.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/soc/intel/intel_adsp/ace/boot.c b/soc/intel/intel_adsp/ace/boot.c index eeadbc3be5d9..a9b80e1a02d0 100644 --- a/soc/intel/intel_adsp/ace/boot.c +++ b/soc/intel/intel_adsp/ace/boot.c @@ -20,7 +20,7 @@ #ifdef CONFIG_ADSP_IMR_CONTEXT_SAVE #define STRINGIFY_MACRO(x) Z_STRINGIFY(x) #define IMRSTACK STRINGIFY_MACRO(IMR_BOOT_LDR_MANIFEST_BASE) -__asm__(".section .imr.boot_entry_d3_restore, \"x\"\n\t" +__asm__(".section .imr.boot_entry_d3_restore, \"ax\"\n\t" ".align 4\n\t" ".global boot_entry_d3_restore\n\t" "boot_entry_d3_restore:\n\t" diff --git a/soc/intel/intel_adsp/common/boot.c b/soc/intel/intel_adsp/common/boot.c index af54fd05221f..210889ce9956 100644 --- a/soc/intel/intel_adsp/common/boot.c +++ b/soc/intel/intel_adsp/common/boot.c @@ -68,7 +68,7 @@ __asm__(".pushsection .boot_entry.text, \"ax\" \n\t" */ #define STRINGIFY_MACRO(x) Z_STRINGIFY(x) #define IMRSTACK STRINGIFY_MACRO(IMR_BOOT_LDR_MANIFEST_BASE) -__asm__(".section .imr.z_boot_asm_entry, \"x\" \n\t" +__asm__(".section .imr.z_boot_asm_entry, \"ax\" \n\t" ".align 4 \n\t" "z_boot_asm_entry: \n\t" " movi a0, 0x4002f \n\t" From 0457e6513a49e409a9e2f8615f135a250f99ee0a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 20 Jun 2026 11:54:21 +0100 Subject: [PATCH 27/47] soc: intel_adsp: Configure cached/uncached mappings and discard stack notes Configure Intel ADSP linker scripts for LLVM/LLD compatibility: - Replaces generic SEGSTART_CACHED/SEGSTART_UNCACHED macros with symbol-explicit ADDR_CACHED(sym)/ADDR_UNCACHED(sym) mapping macros. - Separates literal (*.literal, *.literal.*) and text (*.text, *.text.*) section wildcards to ensure LLD groups literals consistently ahead of text sections, preserving Xtensa L32R reach requirements. - Explicitly discards stack executability notes (*.note.GNU-stack) generated by Clang to prevent orphan section compiler warnings on older target linkers. Signed-off-by: Liam Girdwood --- .../ace/include/linker/ace-link-mirrored.ld | 108 +++++++++++++----- .../cavs/include/xtensa-cavs-linker.ld | 44 ++++--- 2 files changed, 108 insertions(+), 44 deletions(-) diff --git a/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld b/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld index b4093ee2bd78..7df2b1b33e1c 100644 --- a/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld +++ b/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld @@ -38,23 +38,41 @@ ENTRY(rom_entry); #ifdef CONFIG_KERNEL_COHERENCE #ifdef CONFIG_MMU -#define SEGSTART_CACHED RPO_SET(ALIGN(CONFIG_MMU_PAGE_SIZE), CONFIG_INTEL_ADSP_CACHED_REGION) -#define SEGSTART_UNCACHED RPO_SET(ALIGN(CONFIG_MMU_PAGE_SIZE), CONFIG_INTEL_ADSP_UNCACHED_REGION) +#define SEGSTART_CACHED RPO_SET(ALIGN(phys_dot, CONFIG_MMU_PAGE_SIZE), CONFIG_INTEL_ADSP_CACHED_REGION) +#define SEGSTART_UNCACHED RPO_SET(ALIGN(phys_dot, CONFIG_MMU_PAGE_SIZE), CONFIG_INTEL_ADSP_UNCACHED_REGION) #else -#define SEGSTART_CACHED RPO_SET(ALIGN(64), CONFIG_INTEL_ADSP_CACHED_REGION) -#define SEGSTART_UNCACHED RPO_SET(ALIGN(64), CONFIG_INTEL_ADSP_UNCACHED_REGION) +#define SEGSTART_CACHED RPO_SET(ALIGN(phys_dot, 64), CONFIG_INTEL_ADSP_CACHED_REGION) +#define SEGSTART_UNCACHED RPO_SET(ALIGN(phys_dot, 64), CONFIG_INTEL_ADSP_UNCACHED_REGION) #endif #else #ifdef CONFIG_MMU -#define SEGSTART_CACHED ALIGN(CONFIG_MMU_PAGE_SIZE) -#define SEGSTART_UNCACHED ALIGN(CONFIG_MMU_PAGE_SIZE) +#define SEGSTART_CACHED ALIGN(phys_dot, CONFIG_MMU_PAGE_SIZE) +#define SEGSTART_UNCACHED ALIGN(phys_dot, CONFIG_MMU_PAGE_SIZE) #else -#define SEGSTART_CACHED . -#define SEGSTART_UNCACHED . +#define SEGSTART_CACHED phys_dot +#define SEGSTART_UNCACHED phys_dot #endif #define ucram ram #endif +#ifdef CONFIG_KERNEL_COHERENCE +#ifdef CONFIG_MMU +#define ADDR_CACHED(sym) RPO_SET(ALIGN(sym, CONFIG_MMU_PAGE_SIZE), CONFIG_INTEL_ADSP_CACHED_REGION) +#define ADDR_UNCACHED(sym) RPO_SET(ALIGN(sym, CONFIG_MMU_PAGE_SIZE), CONFIG_INTEL_ADSP_UNCACHED_REGION) +#else +#define ADDR_CACHED(sym) RPO_SET(ALIGN(sym, 64), CONFIG_INTEL_ADSP_CACHED_REGION) +#define ADDR_UNCACHED(sym) RPO_SET(ALIGN(sym, 64), CONFIG_INTEL_ADSP_UNCACHED_REGION) +#endif +#else +#ifdef CONFIG_MMU +#define ADDR_CACHED(sym) ALIGN(sym, CONFIG_MMU_PAGE_SIZE) +#define ADDR_UNCACHED(sym) ALIGN(sym, CONFIG_MMU_PAGE_SIZE) +#else +#define ADDR_CACHED(sym) sym +#define ADDR_UNCACHED(sym) sym +#endif +#endif + /* intlist.ld needs an IDT_LIST memory region */ #define IDT_BASE 0xe0000000 #define IDT_SIZE 0x2000 @@ -107,6 +125,7 @@ MEMORY { } SECTIONS { + phys_dot = 0; /* Boot loader code in IMR memory */ .imr : { @@ -210,10 +229,12 @@ SECTIONS { KEEP(*(.init)) KEEP(*(.lps_vector)) - *(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.gnu.linkonce.literal.* .gnu.linkonce.t.*.literal) MMU_PAGE_ALIGN - *(.literal .text) - *(.literal.* .text.*) + *(.literal) + *(.literal.*) + *(.text) + *(.text.* .stub .gnu.warning .gnu.linkonce.t.*) *(.fini.literal) KEEP(*(.fini)) *(.gnu.version) @@ -263,11 +284,13 @@ SECTIONS { *(*.initcall) _module_init_end = .; } >ram + . = _module_init_end; + phys_dot = . & 0x1fffffff; #define RAMABLE_REGION ram #define ROMABLE_REGION ram - __common_rom_region_start = SEGSTART_CACHED; + __common_rom_region_start = ADDR_CACHED(_module_init_end); #include /* Located in generated directory. This file is populated by calling * zephyr_linker_sources(ROM_SECTIONS ...). Useful for grouping iterable RO structs. @@ -276,8 +299,10 @@ SECTIONS { __rodata_region_end = .; __common_rom_region_end = .; __rodata_region_end = .; + . = __common_rom_region_end; + phys_dot = . & 0x1fffffff; - .noinit SEGSTART_UNCACHED (NOLOAD) : HDR_MMU_PAGE_ALIGN { + .noinit ADDR_UNCACHED(__common_rom_region_end) (NOLOAD) : HDR_MMU_PAGE_ALIGN { __data_start = .; *(.noinit) *(.noinit.*) @@ -287,12 +312,15 @@ SECTIONS { */ #include + __noinit_end = .; } >ucram + . = __noinit_end; + phys_dot = . & 0x1fffffff; #include - . = SEGSTART_UNCACHED; + . = ADDR_UNCACHED(__noinit_end); - .data SEGSTART_UNCACHED : HDR_MMU_PAGE_ALIGN { + .data ADDR_UNCACHED(__noinit_end) : HDR_MMU_PAGE_ALIGN { *(.data) *(.data.*) *(.gnu.linkonce.d.*) @@ -312,29 +340,36 @@ SECTIONS { __data_end = .; MMU_PAGE_ALIGN } >ucram + . = __data_end; + phys_dot = . & 0x1fffffff; - .lit4 SEGSTART_CACHED : { + .lit4 ADDR_CACHED(__data_end) : { _lit4_start = .; *(*.lit4) *(.lit4.*) *(.gnu.linkonce.lit4.*) _lit4_end = .; } >ram + . = _lit4_end; + phys_dot = . & 0x1fffffff; /* These values need to change in our scheme, where the common-ram * sections need to be linked in safe/uncached memory but common-rom * wants to use the cache */ -. = SEGSTART_UNCACHED; +. = ADDR_UNCACHED(_lit4_end); #undef RAMABLE_REGION #undef ROMABLE_REGION #define RAMABLE_REGION ucram #define ROMABLE_REGION ucram - .fw_ready SEGSTART_UNCACHED : { + .fw_ready ADDR_UNCACHED(_lit4_end) : { KEEP(*(".fw_ready")); KEEP (*(.fw_ready_metadata)) + __fw_ready_end = .; } > ucram + . = __fw_ready_end; + phys_dot = . & 0x1fffffff; __common_ram_region_start = .; @@ -353,15 +388,20 @@ SECTIONS { #include #include __common_ram_region_end = .; + . = __common_ram_region_end; + phys_dot = . & 0x1fffffff; - .tm_clone_table : { + .tm_clone_table ADDR_CACHED(__common_ram_region_end) : { *(.tm_clone_table) + __tm_clone_table_end = .; } >ram + . = __tm_clone_table_end; + phys_dot = . & 0x1fffffff; /* This section is cached. By default it contains only declared * thread stacks, but applications can put symbols here too. */ - .cached SEGSTART_CACHED : { + .cached ADDR_CACHED(__tm_clone_table_end) : { _cached_start = .; *(.cached .cached.*) #ifdef CONFIG_USERSPACE @@ -371,6 +411,8 @@ SECTIONS { #endif _cached_end = .; } >ram + . = _cached_end; + phys_dot = . & 0x1fffffff; /* Rimage requires 4k alignment between "DATA" and "BSS", can't do * this in the section declaration below because we're also changing @@ -379,8 +421,9 @@ SECTIONS { * seems, --warn-section-align is on by default) */ . = ALIGN(4096); + _bss_start_phys = .; - .bss SEGSTART_UNCACHED (NOLOAD) : + .bss ADDR_UNCACHED(_bss_start_phys) (NOLOAD) : { __bss_start = .; @@ -429,21 +472,26 @@ SECTIONS { MMU_PAGE_ALIGN __bss_end = .; } >ucram + . = __bss_end; + phys_dot = . & 0x1fffffff; - . = SEGSTART_UNCACHED; + . = ADDR_UNCACHED(__bss_end); _end = ALIGN(8); /* Heap start and end markers. Used to reserve system heap memory. */ - .heap_mem SEGSTART_UNCACHED (NOLOAD) : + .heap_mem ADDR_UNCACHED(__bss_end) (NOLOAD) : { _shared_heap_start = .; *(.shared_heap_mem) _shared_heap_end = .; _heap_start = .; *(.heap_mem) + _heap_end = .; } >ucram + . = _heap_end; + phys_dot = . & 0x1fffffff; - .unused_ram_start_marker SEGSTART_CACHED (NOLOAD) : + .unused_ram_start_marker ADDR_CACHED(_heap_end) (NOLOAD) : { . = ALIGN(4096); _unused_ram_start_marker = .; @@ -451,17 +499,19 @@ SECTIONS { *(.unused_ram_start_marker.*) z_mapped_end = .; } >ram + . = z_mapped_end; + phys_dot = . & 0x1fffffff; /* Heap start and end markers. Used with libc malloc code. */ - . = SEGSTART_UNCACHED; + . = ADDR_UNCACHED(z_mapped_end); _end = ALIGN(8); - . = SEGSTART_CACHED; + . = ADDR_CACHED(z_mapped_end); z_mapped_end = .; . = L2_SRAM_BASE + L2_SRAM_SIZE; - . = SEGSTART_UNCACHED; + . = ADDR_UNCACHED(z_mapped_end); _heap_end = .; _heap_sentry = .; - . = SEGSTART_CACHED; + . = ADDR_CACHED(z_mapped_end); _image_ram_end = .; /* dma buffers */ @@ -474,7 +524,7 @@ SECTIONS { /* Non-loadable sections below. Back to cached memory so * the cache remap script doesn't try to move them around needlessly. */ - . = SEGSTART_CACHED; + . = ADDR_CACHED(z_mapped_end); /* rimage module manifest headers */ .module.boot : { KEEP(*(.module.boot)) } >noload diff --git a/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld b/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld index 63ce7a0faa26..4352dca5b719 100644 --- a/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld +++ b/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld @@ -36,14 +36,17 @@ ENTRY(rom_entry); */ #ifdef CONFIG_KERNEL_COHERENCE #define RPO_SET(addr, reg) ((addr & 0x1fffffff) | (reg << 29)) -#define SEGSTART_CACHED RPO_SET(ALIGN(64), CONFIG_INTEL_ADSP_CACHED_REGION) -#define SEGSTART_UNCACHED RPO_SET(ALIGN(64), CONFIG_INTEL_ADSP_UNCACHED_REGION) +#define ADDR_CACHED_ALIGN(sym, align) RPO_SET(ALIGN(sym, align), CONFIG_INTEL_ADSP_CACHED_REGION) +#define ADDR_UNCACHED_ALIGN(sym, align) RPO_SET(ALIGN(sym, align), CONFIG_INTEL_ADSP_UNCACHED_REGION) #else -#define SEGSTART_CACHED . -#define SEGSTART_UNCACHED . +#define ADDR_CACHED_ALIGN(sym, align) ALIGN(sym, align) +#define ADDR_UNCACHED_ALIGN(sym, align) ALIGN(sym, align) #define ucram RAM #endif +#define ADDR_CACHED(sym) ADDR_CACHED_ALIGN(sym, 64) +#define ADDR_UNCACHED(sym) ADDR_UNCACHED_ALIGN(sym, 64) + /* intlist.ld needs an IDT_LIST memory region */ #define IDT_BASE 0xe0000000 #define IDT_SIZE 0x2000 @@ -120,7 +123,8 @@ SECTIONS { *(.iram0.text) KEEP(*(.init)) KEEP(*(.lps_vector)) - *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.literal .literal.* .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal) + *(.text .text.* .stub .gnu.warning .gnu.linkonce.t.*) *(.fini.literal) KEEP(*(.fini)) *(.gnu.version) @@ -180,14 +184,16 @@ SECTIONS { .fw_ready : { KEEP(*(".fw_ready")); KEEP (*(.fw_ready_metadata)) + __fw_ready_end = .; } >RAM - .noinit SEGSTART_UNCACHED : { + .noinit ADDR_UNCACHED(__fw_ready_end) : { *(.noinit) *(.noinit.*) + __noinit_end = .; } >ucram - .data SEGSTART_UNCACHED : { + .data ADDR_UNCACHED(__noinit_end) : { __data_start = .; *(.data) *(.data.*) @@ -211,7 +217,7 @@ SECTIONS { __data_end = .; } >ucram - .lit4 SEGSTART_CACHED : { + .lit4 ADDR_CACHED(__data_end) : { _lit4_start = .; *(*.lit4) *(.lit4.*) @@ -223,22 +229,25 @@ SECTIONS { * sections need to be linked in safe/uncached memory but common-rom * wants to use the cache */ -. = SEGSTART_UNCACHED; +. = ADDR_UNCACHED(_lit4_end); #undef RAMABLE_REGION #undef ROMABLE_REGION #define RAMABLE_REGION ucram #define ROMABLE_REGION ucram #include + __common_ram_region_end = .; + . = ADDR_CACHED(__common_ram_region_end); - .tm_clone_table : { + .tm_clone_table ADDR_CACHED(__common_ram_region_end) : { *(.tm_clone_table) + __tm_clone_table_end = .; } >RAM /* This section is cached. By default it contains only declared * thread stacks, but applications can put symbols here too. */ - .cached SEGSTART_CACHED : { + .cached ADDR_CACHED(__tm_clone_table_end) : { _cached_start = .; *(.cached .cached.*) _cached_end = .; @@ -252,7 +261,7 @@ SECTIONS { */ . = ALIGN(4096); - .bss SEGSTART_UNCACHED (NOLOAD) : + .bss ADDR_UNCACHED_ALIGN(_cached_end, 4096) (NOLOAD) : { __bss_start = .; *(.dynsbss) @@ -276,10 +285,10 @@ SECTIONS { } >ucram /* Heap start and end markers. Mostly unused, though newlib likes them */ - . = SEGSTART_UNCACHED; + . = ADDR_UNCACHED(__bss_end); _end = ALIGN(8); . = L2_SRAM_BASE + L2_SRAM_SIZE; - . = SEGSTART_UNCACHED; + . = ADDR_UNCACHED(__bss_end); _heap_sentry = .; /* dma buffers */ @@ -293,7 +302,7 @@ SECTIONS { /* Non-loadable sections below. Back to cached memory so * the cache remap script doesn't try to move them around needlessly. */ - . = SEGSTART_CACHED; + . = ADDR_CACHED(_cached_end); /* rimage module manifest headers */ .module.boot : { KEEP(*(.module.boot)) } >noload @@ -357,4 +366,9 @@ SECTIONS { #include #endif #include + + /* Discard stack-executability notes emitted by Clang/GCC; Xtensa + * bare-metal targets have no use for them and the older CAVS ld.bfd + * emits a noisy "orphan section" warning otherwise. */ + /DISCARD/ : { *(.note.GNU-stack) } } From 4e40f9d8971152b457e40dd34f5af77db1b97533 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 22 Jun 2026 10:35:58 +0100 Subject: [PATCH 28/47] soc: intel_adsp: Map IMR boot loader stack as shared region Configure the IMR boot loader stack mapping in the MMU page table: - In both mmu_ace30.c and mmu_ace40.c, add the XTENSA_MMU_MAP_SHARED attribute to the IMR boot stack region mapping. - This maps the boot stack in Ring 3 (shared ASID 4) instead of Ring 0 (ASID 1). - This ensures that register window exception handlers (which execute spills/fills using s32e/l32e with Ring 1 privilege) can successfully match the TLB entries for the stack, resolving early boot LoadStoreTLBMiss exceptions and preventing triple faults when the L2 page table page is not mapped in the TLB. Signed-off-by: Liam Girdwood --- soc/intel/intel_adsp/ace/mmu_ace30.c | 2 +- soc/intel/intel_adsp/ace/mmu_ace40.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/soc/intel/intel_adsp/ace/mmu_ace30.c b/soc/intel/intel_adsp/ace/mmu_ace30.c index bc15d1032ba9..86177e1e1da9 100644 --- a/soc/intel/intel_adsp/ace/mmu_ace30.c +++ b/soc/intel/intel_adsp/ace/mmu_ace30.c @@ -211,7 +211,7 @@ const struct xtensa_mmu_range xtensa_soc_mmu_ranges[] = { MEM_MAP_CONST_REGION( IMR_BOOT_LDR_STACK_BASE, IMR_BOOT_LDR_STACK_BASE + IMR_BOOT_LDR_STACK_SIZE, - XTENSA_MMU_PERM_W, + XTENSA_MMU_PERM_W | XTENSA_MMU_MAP_SHARED, "imr stack" ) diff --git a/soc/intel/intel_adsp/ace/mmu_ace40.c b/soc/intel/intel_adsp/ace/mmu_ace40.c index 691e8709adc7..3b9646f3f4f1 100644 --- a/soc/intel/intel_adsp/ace/mmu_ace40.c +++ b/soc/intel/intel_adsp/ace/mmu_ace40.c @@ -197,7 +197,7 @@ const struct xtensa_mmu_range xtensa_soc_mmu_ranges[] = { MEM_MAP_CONST_REGION( IMR_BOOT_LDR_STACK_BASE, IMR_BOOT_LDR_STACK_BASE + IMR_BOOT_LDR_STACK_SIZE, - XTENSA_MMU_PERM_W, + XTENSA_MMU_PERM_W | XTENSA_MMU_MAP_SHARED, "imr stack" ) From f46349f31ca8281875c5830696a6d582eb227e4a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 24 Jun 2026 17:04:40 +0100 Subject: [PATCH 29/47] soc: intel_adsp: Fix missing active cpus array on UP configurations - Provide the 'soc_cpus_active' array even when CONFIG_MP_MAX_NUM_CPUS == 1 to fix undefined reference errors during linking. --- soc/intel/intel_adsp/ace/multiprocessing.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/soc/intel/intel_adsp/ace/multiprocessing.c b/soc/intel/intel_adsp/ace/multiprocessing.c index 7c18c6be4d2e..dd83c35aac20 100644 --- a/soc/intel/intel_adsp/ace/multiprocessing.c +++ b/soc/intel/intel_adsp/ace/multiprocessing.c @@ -72,6 +72,10 @@ static void ipc_isr(void *arg) #define CAP_INST_SHIFT 24 #define CAP_INST_MASK BIT_MASK(4) +#if CONFIG_MP_MAX_NUM_CPUS == 1 +bool soc_cpus_active[CONFIG_MP_MAX_NUM_CPUS]; +#endif + unsigned int soc_num_cpus; __imr void soc_num_cpus_init(void) From ca3cf47f4febf7b35b2f89d6d5c58f57e99dc691 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 24 Jun 2026 18:50:51 +0100 Subject: [PATCH 30/47] soc: xtensa: Finalize LLD linker script fixes for Intel and NXP - Explicitly add .literal matching for .imr regions on Intel platforms. - Remove remaining '(!ari)' attributes from NXP i.MX linker scripts. --- .../intel_adsp/ace/include/linker/ace-link-mirrored.ld | 1 + soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld | 1 + soc/nxp/imx/imx8/adsp/linker.ld | 6 +++--- soc/nxp/imx/imx8m/adsp/linker.ld | 6 +++--- soc/nxp/imx/imx8ulp/adsp/linker.ld | 6 +++--- soc/nxp/imx/imx8x/adsp/linker.ld | 6 +++--- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld b/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld index 7df2b1b33e1c..3296dd9d4ecf 100644 --- a/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld +++ b/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld @@ -132,6 +132,7 @@ SECTIONS { _imr_start = .; /* Entry point MUST be here per external configuration */ KEEP (*(.boot_entry.text)) + *(.imr.literal .imr.*.literal) *(.imr .imr.*) } >imr diff --git a/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld b/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld index 4352dca5b719..3460c9f9620f 100644 --- a/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld +++ b/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld @@ -93,6 +93,7 @@ SECTIONS { _imr_start = .; /* Entry point MUST be here per external configuration */ KEEP (*(.boot_entry.text)) + *(.imr.literal .imr.*.literal) *(.imr .imr.*) _imr_end = .; } >imr diff --git a/soc/nxp/imx/imx8/adsp/linker.ld b/soc/nxp/imx/imx8/adsp/linker.ld index 7b9e71974426..70474137fd78 100644 --- a/soc/nxp/imx/imx8/adsp/linker.ld +++ b/soc/nxp/imx/imx8/adsp/linker.ld @@ -95,13 +95,13 @@ MEMORY len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } diff --git a/soc/nxp/imx/imx8m/adsp/linker.ld b/soc/nxp/imx/imx8m/adsp/linker.ld index f73e527151a2..5905f798c109 100644 --- a/soc/nxp/imx/imx8m/adsp/linker.ld +++ b/soc/nxp/imx/imx8m/adsp/linker.ld @@ -95,13 +95,13 @@ MEMORY len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } diff --git a/soc/nxp/imx/imx8ulp/adsp/linker.ld b/soc/nxp/imx/imx8ulp/adsp/linker.ld index 059d783df575..41d2e289ba58 100644 --- a/soc/nxp/imx/imx8ulp/adsp/linker.ld +++ b/soc/nxp/imx/imx8ulp/adsp/linker.ld @@ -95,13 +95,13 @@ MEMORY len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } diff --git a/soc/nxp/imx/imx8x/adsp/linker.ld b/soc/nxp/imx/imx8x/adsp/linker.ld index a78518d2bc3b..040e834828f0 100644 --- a/soc/nxp/imx/imx8x/adsp/linker.ld +++ b/soc/nxp/imx/imx8x/adsp/linker.ld @@ -95,13 +95,13 @@ MEMORY len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } From 02da03f2258d4aa11b6ec50dfe7e7ce0ee257afd Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 27 Jun 2026 17:31:06 +0100 Subject: [PATCH 31/47] soc: intel_adsp: ace: Add MMU mappings for bootloader IMR regions LLVM organizes sections differently from GCC, sometimes placing initialization code or structures into the Bootloader Isolated Memory Regions (IMR). Without these mappings, LLVM-compiled firmware triggers MMU faults (Instruction Fetch or Load/Store exceptions) during early boot. This patch adds explicit XTENSA_MMU_PERM_X and XTENSA_MMU_PERM_W mappings for IMR_BOOT_LDR_LIT_BASE, IMR_BOOT_LDR_TEXT_BASE, and IMR_BOOT_LDR_DATA_BASE on ACE30 and ACE40 SOCs. --- soc/intel/intel_adsp/ace/mmu_ace30.c | 22 ++++++++++++++++++++++ soc/intel/intel_adsp/ace/mmu_ace40.c | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/soc/intel/intel_adsp/ace/mmu_ace30.c b/soc/intel/intel_adsp/ace/mmu_ace30.c index 86177e1e1da9..5b97fff14aae 100644 --- a/soc/intel/intel_adsp/ace/mmu_ace30.c +++ b/soc/intel/intel_adsp/ace/mmu_ace30.c @@ -166,6 +166,28 @@ const struct xtensa_mmu_range xtensa_soc_mmu_ranges[] = { ) /* Map IMR */ + + MEM_MAP_CONST_REGION( + IMR_BOOT_LDR_LIT_BASE, + IMR_BOOT_LDR_LIT_BASE + IMR_BOOT_LDR_LIT_SIZE, + XTENSA_MMU_PERM_W | XTENSA_MMU_PERM_X | XTENSA_MMU_MAP_SHARED, + "imr lit" + ) + + MEM_MAP_CONST_REGION( + IMR_BOOT_LDR_TEXT_BASE, + IMR_BOOT_LDR_TEXT_BASE + IMR_BOOT_LDR_TEXT_SIZE, + XTENSA_MMU_PERM_W | XTENSA_MMU_PERM_X | XTENSA_MMU_MAP_SHARED, + "imr text" + ) + + MEM_MAP_CONST_REGION( + IMR_BOOT_LDR_DATA_BASE, + IMR_BOOT_LDR_DATA_BASE + IMR_BOOT_LDR_DATA_SIZE, + XTENSA_MMU_PERM_W | XTENSA_MMU_MAP_SHARED, + "imr data base" + ) + MEM_MAP_CONST_REGION( IMR_ROM_EXT_CODE_BASE, IMR_ROM_EXT_CODE_BASE + IMR_ROM_EXT_CODE_SIZE, diff --git a/soc/intel/intel_adsp/ace/mmu_ace40.c b/soc/intel/intel_adsp/ace/mmu_ace40.c index 3b9646f3f4f1..681c1ebd0b4c 100644 --- a/soc/intel/intel_adsp/ace/mmu_ace40.c +++ b/soc/intel/intel_adsp/ace/mmu_ace40.c @@ -166,6 +166,28 @@ const struct xtensa_mmu_range xtensa_soc_mmu_ranges[] = { ) /* Map IMR */ + + MEM_MAP_CONST_REGION( + IMR_BOOT_LDR_LIT_BASE, + IMR_BOOT_LDR_LIT_BASE + IMR_BOOT_LDR_LIT_SIZE, + XTENSA_MMU_PERM_W | XTENSA_MMU_PERM_X | XTENSA_MMU_MAP_SHARED, + "imr lit" + ) + + MEM_MAP_CONST_REGION( + IMR_BOOT_LDR_TEXT_BASE, + IMR_BOOT_LDR_TEXT_BASE + IMR_BOOT_LDR_TEXT_SIZE, + XTENSA_MMU_PERM_W | XTENSA_MMU_PERM_X | XTENSA_MMU_MAP_SHARED, + "imr text" + ) + + MEM_MAP_CONST_REGION( + IMR_BOOT_LDR_DATA_BASE, + IMR_BOOT_LDR_DATA_BASE + IMR_BOOT_LDR_DATA_SIZE, + XTENSA_MMU_PERM_W | XTENSA_MMU_MAP_SHARED, + "imr data base" + ) + MEM_MAP_CONST_REGION( IMR_BOOT_LDR_MANIFEST_BASE - IMR_BOOT_LDR_MANIFEST_SIZE, IMR_BOOT_LDR_MANIFEST_BASE, From 55edb913ac7bf5058cf6e431923f98e54ea682e6 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 9 Jul 2026 17:01:02 +0100 Subject: [PATCH 32/47] soc: intel_adsp: Fix heap boundary calculation in linker scripts The commit c3c840255a75415a2801b139efa2edf6c52856e6 ('soc: intel_adsp: Configure cached/uncached mappings and discard stack notes') replaced generic SEGSTART_CACHED/SEGSTART_UNCACHED macros with ADDR_CACHED(sym)/ADDR_UNCACHED(sym) mapping macros. However, it used the static 'z_mapped_end' or '__bss_end' symbols instead of mapping the current location counter '.' after moving it to the end of SRAM. Under LLD (and under specific alignment configurations), this resets the location counter back to the end of the firmware image, resulting in a heap size of 0. When heap allocation occurs, subtracting the shared buffer size underflows, causing memory corruption (allocations overlap with the code segment). Fix this by mapping the current location counter '.' to cached/uncached memory when defining the heap end, heap sentry, and image RAM end markers. Signed-off-by: Liam Girdwood --- soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld | 4 ++-- soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld b/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld index 3296dd9d4ecf..2598088cc6ce 100644 --- a/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld +++ b/soc/intel/intel_adsp/ace/include/linker/ace-link-mirrored.ld @@ -509,10 +509,10 @@ SECTIONS { . = ADDR_CACHED(z_mapped_end); z_mapped_end = .; . = L2_SRAM_BASE + L2_SRAM_SIZE; - . = ADDR_UNCACHED(z_mapped_end); + . = ADDR_UNCACHED(.); _heap_end = .; _heap_sentry = .; - . = ADDR_CACHED(z_mapped_end); + . = ADDR_CACHED(.); _image_ram_end = .; /* dma buffers */ diff --git a/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld b/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld index 3460c9f9620f..b294cfed4eda 100644 --- a/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld +++ b/soc/intel/intel_adsp/cavs/include/xtensa-cavs-linker.ld @@ -289,7 +289,7 @@ SECTIONS { . = ADDR_UNCACHED(__bss_end); _end = ALIGN(8); . = L2_SRAM_BASE + L2_SRAM_SIZE; - . = ADDR_UNCACHED(__bss_end); + . = ADDR_UNCACHED(.); _heap_sentry = .; /* dma buffers */ From f99f357eb9b65037883118aa5dd92edda328acf4 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 21:02:10 +0100 Subject: [PATCH 33/47] soc: intel_adsp: lazy debug window init and word-aligned boot memcpy debug_window.c: lazily initialize the ADSP debug window on first use. When the debug window descriptor array starts with 0xffffffff (uncleared by the ROM), clear the entire window before any slot is allocated. This prevents stale ROM data from being misinterpreted as valid slot headers on cold boot. soc_util.h (bmemcpy/bbzero): use 32-bit word-aligned copies in the boot loader memory routines instead of byte-at-a-time loops. Clang strict aliasing analysis rejects the previous uint8_t pointer casts on aligned buffers, and the word copy is faster for the large IMR-to-DRAM firmware image copies performed at boot. Signed-off-by: Liam Girdwood --- soc/intel/intel_adsp/common/debug_window.c | 9 ++++ .../intel_adsp/common/include/soc_util.h | 41 +++++++++++++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/soc/intel/intel_adsp/common/debug_window.c b/soc/intel/intel_adsp/common/debug_window.c index b88ae62dcc22..08f1b5359900 100644 --- a/soc/intel/intel_adsp/common/debug_window.c +++ b/soc/intel/intel_adsp/common/debug_window.c @@ -70,6 +70,15 @@ static int adsp_dw_find_slot_by_type(uint32_t type) void *adsp_dw_request_slot(struct adsp_dw_desc *dw_desc, size_t *slot_size) { int slot_idx; + static bool dw_initialized = false; + + if (!dw_initialized) { + if (ADSP_DW->descs[0].type == 0xffffffff) { + LOG_WRN("Debug Window uninitialized, initializing now..."); + memset((void *)ADSP_DW, 0, CONFIG_MEMORY_WIN_2_SIZE); + } + dw_initialized = true; + } if (!dw_desc->type) { return NULL; diff --git a/soc/intel/intel_adsp/common/include/soc_util.h b/soc/intel/intel_adsp/common/include/soc_util.h index 9de2efb5e672..5dbd14bb07a6 100644 --- a/soc/intel/intel_adsp/common/include/soc_util.h +++ b/soc/intel/intel_adsp/common/include/soc_util.h @@ -11,24 +11,47 @@ /* memcopy used by boot loader */ static ALWAYS_INLINE void bmemcpy(void *dest, void *src, size_t bytes) { - volatile uint32_t *d = (uint32_t *)dest; - volatile uint32_t *s = (uint32_t *)src; - sys_cache_data_invd_range(src, bytes); - for (size_t i = 0; i < (bytes >> 2); i++) { - d[i] = s[i]; + + if ((((uintptr_t)dest | (uintptr_t)src) & 3) == 0) { + uint32_t *d32 = (uint32_t *)dest; + uint32_t *s32 = (uint32_t *)src; + size_t words = bytes / 4; + size_t remainder = bytes % 4; + + for (size_t i = 0; i < words; i++) { + d32[i] = s32[i]; + } + + uint8_t *d8 = (uint8_t *)(d32 + words); + uint8_t *s8 = (uint8_t *)(s32 + words); + for (size_t i = 0; i < remainder; i++) { + d8[i] = s8[i]; + } + } else { + uint8_t *d8 = (uint8_t *)dest; + uint8_t *s8 = (uint8_t *)src; + for (size_t i = 0; i < bytes; i++) { + d8[i] = s8[i]; + } } sys_cache_data_flush_range(dest, bytes); } -/* bzero used by bootloader */ static ALWAYS_INLINE void bbzero(void *dest, size_t bytes) { - volatile uint32_t *d = (uint32_t *)dest; + uint32_t *d32 = (uint32_t *)dest; + size_t words = bytes / 4; + size_t remainder = bytes % 4; + + for (size_t i = 0; i < words; i++) { + d32[i] = 0; + } - for (size_t i = 0; i < (bytes >> 2); i++) { - d[i] = 0; + uint8_t *d8 = (uint8_t *)(d32 + words); + for (size_t i = 0; i < remainder; i++) { + d8[i] = 0; } sys_cache_data_flush_range(dest, bytes); From b103fe40b835d669b42ca6b394ceac8e23d5b39e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 25 Jun 2026 19:28:40 +0100 Subject: [PATCH 34/47] drivers: dma: intel_adsp_gpdma: use sys_write16 for 16-bit ownership register The GPDMA ownership register is a 16-bit MMIO register. Using sys_write32() triggers a load/store width exception under LLVM strict alignment checking. Replace with sys_write16() to match the actual register width. Signed-off-by: Liam Girdwood --- drivers/dma/dma_intel_adsp_gpdma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dma_intel_adsp_gpdma.c b/drivers/dma/dma_intel_adsp_gpdma.c index 2c9f7b698191..ab8b98522488 100644 --- a/drivers/dma/dma_intel_adsp_gpdma.c +++ b/drivers/dma/dma_intel_adsp_gpdma.c @@ -286,8 +286,8 @@ static void intel_adsp_gpdma_claim_ownership(const struct device *dev) sys_write32(val, reg); #else - sys_write32(LPGPDMA_CHOSEL_FLAG | LPGPDMA_CTLOSEL_FLAG, DSP_INIT_LPGPDMA(0)); - sys_write32(LPGPDMA_CHOSEL_FLAG | LPGPDMA_CTLOSEL_FLAG, DSP_INIT_LPGPDMA(1)); + sys_write16(LPGPDMA_CHOSEL_FLAG | LPGPDMA_CTLOSEL_FLAG, DSP_INIT_LPGPDMA(0)); + sys_write16(LPGPDMA_CHOSEL_FLAG | LPGPDMA_CTLOSEL_FLAG, DSP_INIT_LPGPDMA(1)); ARG_UNUSED(dev); #endif /* CONFIG_SOC_SERIES_INTEL_ADSP_ACE */ #endif /* CONFIG_DMA_INTEL_ADSP_GPDMA_NEED_CONTROLLER_OWNERSHIP */ From c14d357b7d530646028c4ac38d253a2b9c7aa95b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 9 Jul 2026 13:22:53 +0100 Subject: [PATCH 35/47] drivers: interrupt_controller: intc_dw_ace: Fix compiler bug in irq loop Replace arch_num_cpus() with CONFIG_MP_MAX_NUM_CPUS in dw_ace_irq_enable and dw_ace_irq_disable loops. On platforms with CONFIG_SOC_HAS_RUNTIME_NUM_CPUS=y, arch_num_cpus() evaluates to the dynamic global variable soc_num_cpus. When compiling this loop with GCC 14.3.0 for Xtensa, the compiler generates a buggy loop trip count calculation that evaluates to 0 iterations at runtime. Consequently, Designware interrupts (such as Host IPC and HDA/DMA) are never enabled. Using the static compile-time constant CONFIG_MP_MAX_NUM_CPUS (5) prevents the compiler bug, enabling interrupts to be correctly delivered. --- drivers/interrupt_controller/intc_dw_ace.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/interrupt_controller/intc_dw_ace.c b/drivers/interrupt_controller/intc_dw_ace.c index 83b3f62d2c59..8b9697c79fd0 100644 --- a/drivers/interrupt_controller/intc_dw_ace.c +++ b/drivers/interrupt_controller/intc_dw_ace.c @@ -86,9 +86,7 @@ void dw_ace_irq_enable(const struct device *dev, uint32_t irq) ARG_UNUSED(dev); if (is_dw_irq(irq)) { - unsigned int num_cpus = arch_num_cpus(); - - for (int i = 0; i < num_cpus; i++) { + for (int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { ACE_INTC[i].irq_inten_l |= BIT(ACE_IRQ_FROM_ZEPHYR(irq)); ACE_INTC[i].irq_intmask_l &= ~BIT(ACE_IRQ_FROM_ZEPHYR(irq)); } @@ -102,9 +100,7 @@ void dw_ace_irq_disable(const struct device *dev, uint32_t irq) ARG_UNUSED(dev); if (is_dw_irq(irq)) { - unsigned int num_cpus = arch_num_cpus(); - - for (int i = 0; i < num_cpus; i++) { + for (int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { ACE_INTC[i].irq_inten_l &= ~BIT(ACE_IRQ_FROM_ZEPHYR(irq)); ACE_INTC[i].irq_intmask_l |= BIT(ACE_IRQ_FROM_ZEPHYR(irq)); } From 267bd136f10bc849787a56c2f9a075e4f2946160 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 10 Jul 2026 11:38:28 +0100 Subject: [PATCH 36/47] soc: intel: adsp: cavs: add HiFi3 capability and fix heap boundary Select XTENSA_CPU_HAS_HIFI3 for the CAVS 2.5 (TGL) SOC series so that the XTENSA_HIFI_SHARING infrastructure knows the coprocessor is present. Without this the HiFi AE register file is never saved/restored across context switches, corrupting the state of any thread that uses HiFi instructions. Also fix the heap sentry calculation in the linker script: the L2 SRAM boundary was not being applied to the uncached address alias, placing _heap_sentry in the wrong region for the cavs2.5 memory map. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Liam Girdwood --- soc/intel/intel_adsp/cavs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/soc/intel/intel_adsp/cavs/Kconfig b/soc/intel/intel_adsp/cavs/Kconfig index ae7ad9384525..27403c3d6175 100644 --- a/soc/intel/intel_adsp/cavs/Kconfig +++ b/soc/intel/intel_adsp/cavs/Kconfig @@ -4,6 +4,7 @@ config SOC_SERIES_INTEL_ADSP_CAVS select XTENSA + select XTENSA_CPU_HAS_HIFI3 select XTENSA_HAL if ("$(ZEPHYR_TOOLCHAIN_VARIANT)" != "xcc" && ("$(ZEPHYR_TOOLCHAIN_VARIANT)" != "xt-clang")) select XTENSA_RESET_VECTOR select XTENSA_USE_CORE_CRT1 From 5bf8375c3071b219e10d5e4096f221ed3e573c6e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 17:13:54 +0100 Subject: [PATCH 37/47] drivers: interrupt_controller: intc_dw_ace: use arch_num_cpus instead of CONFIG_MP_MAX_NUM_CPUS Signed-off-by: Liam Girdwood --- drivers/interrupt_controller/intc_dw_ace.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/interrupt_controller/intc_dw_ace.c b/drivers/interrupt_controller/intc_dw_ace.c index 8b9697c79fd0..83b3f62d2c59 100644 --- a/drivers/interrupt_controller/intc_dw_ace.c +++ b/drivers/interrupt_controller/intc_dw_ace.c @@ -86,7 +86,9 @@ void dw_ace_irq_enable(const struct device *dev, uint32_t irq) ARG_UNUSED(dev); if (is_dw_irq(irq)) { - for (int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + unsigned int num_cpus = arch_num_cpus(); + + for (int i = 0; i < num_cpus; i++) { ACE_INTC[i].irq_inten_l |= BIT(ACE_IRQ_FROM_ZEPHYR(irq)); ACE_INTC[i].irq_intmask_l &= ~BIT(ACE_IRQ_FROM_ZEPHYR(irq)); } @@ -100,7 +102,9 @@ void dw_ace_irq_disable(const struct device *dev, uint32_t irq) ARG_UNUSED(dev); if (is_dw_irq(irq)) { - for (int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + unsigned int num_cpus = arch_num_cpus(); + + for (int i = 0; i < num_cpus; i++) { ACE_INTC[i].irq_inten_l &= ~BIT(ACE_IRQ_FROM_ZEPHYR(irq)); ACE_INTC[i].irq_intmask_l |= BIT(ACE_IRQ_FROM_ZEPHYR(irq)); } From e9bd483f9b452d8ed291246618b4e42428499e05 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sat, 20 Jun 2026 11:54:14 +0100 Subject: [PATCH 38/47] soc: cdns: dc233c: Configure linker cached/uncached macro mappings for LLD Configure linker region layout macros for simulator builds: - In xtensa-dc233c.ld, replace generic location-counter-relative macros (SEGSTART_CACHED/SEGSTART_UNCACHED) with symbol-explicit region mapping macros (ADDR_CACHED/ADDR_UNCACHED). - Under LLVM/LLD, assigning different memory alias addresses using the location counter '.' triggers section overlap or counter inconsistency warnings. Explicitly mapping section addresses via symbols avoids this linking constraint. Signed-off-by: Liam Girdwood --- soc/cdns/dc233c/include/xtensa-dc233c.ld | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/soc/cdns/dc233c/include/xtensa-dc233c.ld b/soc/cdns/dc233c/include/xtensa-dc233c.ld index 5acfdacdc7dc..1dd5eed34d21 100644 --- a/soc/cdns/dc233c/include/xtensa-dc233c.ld +++ b/soc/cdns/dc233c/include/xtensa-dc233c.ld @@ -203,7 +203,8 @@ SECTIONS *(.iram0.literal .iram.literal .iram.text.literal .iram0.text .iram.text) *(.iram1.literal .iram1) KEEP(*(.init)) - *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.literal .literal.* .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal) + *(.text .text.* .stub .gnu.warning .gnu.linkonce.t.*) *(.fini.literal) KEEP(*(.fini)) *(.gnu.version) From f08e8358fafac5e568036a4fbb8e0820cefaefae Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 24 Jun 2026 17:04:35 +0100 Subject: [PATCH 39/47] soc: xtensa: Update linker scripts for LLD compatibility - Remove '(!ari)' custom memory region attributes, which are GNU ld specific and crash LLD. - Explicitly place .literal sections alongside .text across Intel, AMD, NXP, and Cadence DSP linkers. - Ensure correct placement of .struct and .symtab internal sections. --- include/zephyr/linker/intlist.ld | 2 +- soc/amd/acp_6_0/adsp/linker.ld | 8 ++--- soc/amd/acp_7_0/linker.ld | 6 ++-- soc/amd/acp_7_x/linker.ld | 6 ++-- soc/cdns/dc233c/include/xtensa-dc233c.ld | 41 ++++++++++++++++++------ 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/include/zephyr/linker/intlist.ld b/include/zephyr/linker/intlist.ld index 9cad6f32c32f..3ff8726a5479 100644 --- a/include/zephyr/linker/intlist.ld +++ b/include/zephyr/linker/intlist.ld @@ -36,7 +36,7 @@ SECTION_PROLOGUE(.intList,,) { KEEP(*(.irq_info*)) KEEP(*(.intList*)) -} GROUP_ROM_LINK_IN(IDT_LIST, IDT_LIST) +} GROUP_ROM_LINK_IN(IDT_LIST, IDT_LIST) :NONE #else /DISCARD/ : { diff --git a/soc/amd/acp_6_0/adsp/linker.ld b/soc/amd/acp_6_0/adsp/linker.ld index ff3dd4342f9b..9e4b08cb3a73 100644 --- a/soc/amd/acp_6_0/adsp/linker.ld +++ b/soc/amd/acp_6_0/adsp/linker.ld @@ -14,7 +14,7 @@ OUTPUT_ARCH(xtensa) #include #include -#include +#include "memory.h" #include #include @@ -109,13 +109,13 @@ MEMORY len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } diff --git a/soc/amd/acp_7_0/linker.ld b/soc/amd/acp_7_0/linker.ld index f7dac1ab0ec5..0b522b044a1a 100644 --- a/soc/amd/acp_7_0/linker.ld +++ b/soc/amd/acp_7_0/linker.ld @@ -158,13 +158,13 @@ sram1 : len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } diff --git a/soc/amd/acp_7_x/linker.ld b/soc/amd/acp_7_x/linker.ld index c832e14c939c..2e1bab633e7e 100644 --- a/soc/amd/acp_7_x/linker.ld +++ b/soc/amd/acp_7_x/linker.ld @@ -160,13 +160,13 @@ sram1 : len = IDT_SIZE #endif - static_uuid_entries_seg (!ari) : + static_uuid_entries_seg : org = UUID_ENTRY_ELF_BASE, len = UUID_ENTRY_ELF_SIZE - static_log_entries_seg (!ari) : + static_log_entries_seg : org = LOG_ENTRY_ELF_BASE, len = LOG_ENTRY_ELF_SIZE - fw_metadata_seg (!ari) : + fw_metadata_seg : org = EXT_MANIFEST_ELF_BASE, len = EXT_MANIFEST_ELF_SIZE } diff --git a/soc/cdns/dc233c/include/xtensa-dc233c.ld b/soc/cdns/dc233c/include/xtensa-dc233c.ld index 1dd5eed34d21..ca7c4a1fa797 100644 --- a/soc/cdns/dc233c/include/xtensa-dc233c.ld +++ b/soc/cdns/dc233c/include/xtensa-dc233c.ld @@ -123,7 +123,8 @@ SECTIONS >vectors :vectors_phdr #define LIB_OBJ_FUNC_IN_SECT(library, obj_file, func) \ - *##library##:##obj_file##(.literal.##func .text.##func) \ + *##library##:##obj_file##(.literal.##func) \ + *##library##:##obj_file##(.text.##func) #ifdef CONFIG_XTENSA_MMU .vec_helpers : @@ -139,12 +140,14 @@ SECTIONS * TLB multi-hit exception. */ - *libarch__xtensa__core.a:xtensa_asm2_util.S.obj(.literal .text) + *libarch__xtensa__core.a:xtensa_asm2_util.S.obj(.literal) + *libarch__xtensa__core.a:xtensa_asm2_util.S.obj(.text) *libarch__xtensa__core.a:xtensa_asm2_util.S.obj(.iram.text .iram0.text) *libarch__xtensa__core.a:window_vectors.S.obj(.iram.text) - *libarch__xtensa__core.a:crt1.S.obj(.literal .text) + *libarch__xtensa__core.a:crt1.S.obj(.literal) + *libarch__xtensa__core.a:crt1.S.obj(.text) LIB_OBJ_FUNC_IN_SECT(libarch__xtensa__core.a,xtensa_asm2.c.obj,*) LIB_OBJ_FUNC_IN_SECT(libarch__xtensa__core.a,fatal.c.obj,*) @@ -155,7 +158,8 @@ SECTIONS /* To support backtracing */ LIB_OBJ_FUNC_IN_SECT(libarch__xtensa__core.a,xtensa_backtrace.c.obj,*) - *libarch__xtensa__core.a:debug_helpers_asm.S.obj(.iram1.literal .iram1) + *libarch__xtensa__core.a:debug_helpers_asm.S.obj(.iram1.literal) + *libarch__xtensa__core.a:debug_helpers_asm.S.obj(.iram1) /* Userspace related stuff */ LIB_OBJ_FUNC_IN_SECT(libarch__xtensa__core.a,userspace.S.obj,xtensa_do_syscall) @@ -171,12 +175,17 @@ SECTIONS LIB_OBJ_FUNC_IN_SECT(libdrivers__console.a,,*) LIB_OBJ_FUNC_IN_SECT(libdrivers__timer.a,,*) - *(.literal.z_vrfy_* .text.z_vrfy_*) - *(.literal.z_mrsh_* .text.z_mrsh_*) - *(.literal.z_impl_* .text.z_impl_*) - *(.literal.z_obj_* .text.z_obj_*) - - *(.literal.k_sys_fatal_error_handler .text.k_sys_fatal_error_handler) + *(.literal.z_vrfy_*) + *(.text.z_vrfy_*) + *(.literal.z_mrsh_*) + *(.text.z_mrsh_*) + *(.literal.z_impl_*) + *(.text.z_impl_*) + *(.literal.z_obj_*) + *(.text.z_obj_*) + + *(.literal.k_sys_fatal_error_handler) + *(.text.k_sys_fatal_error_handler) } >vec_helpers :vec_helpers_phdr #endif /* CONFIG_XTENSA_MMU */ @@ -203,6 +212,18 @@ SECTIONS *(.iram0.literal .iram.literal .iram.text.literal .iram0.text .iram.text) *(.iram1.literal .iram1) KEEP(*(.init)) + *libarch__xtensa__core.a:(.literal .literal.*) + *libarch__xtensa__core.a:(.text .text.*) + + *libkernel.a:(.literal .literal.*) + *libkernel.a:(.text .text.*) + + *libzephyr.a:(.literal .literal.*) + *libzephyr.a:(.text .text.*) + + *libmodules_sof.a:(.literal .literal.*) + *libmodules_sof.a:(.text .text.*) + *(.literal .literal.* .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal) *(.text .text.* .stub .gnu.warning .gnu.linkonce.t.*) *(.fini.literal) From 670769786230c727c3e17fa1ea42124d2601dd4a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 9 Jul 2026 15:27:30 +0100 Subject: [PATCH 40/47] xtensa: adsp: record fatal exception breadcrumbs in HP-SRAM window0 On Intel ADSP targets there is frequently no usable console or mtrace output when the firmware takes a fatal exception very early or inside a crash loop, which makes the faulting PC effectively invisible. The host SOF driver, however, prints HP-SRAM window0[0] as "Firmware state" and window0[1] as "status/error code" on an IPC timeout. Add CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB (default n, depends on SOC_FAMILY_INTEL_ADSP) which, when enabled, records the faulting PC, exception cause, faulting virtual address and the faulting instruction word into window0 via its uncached alias from xtensa_excint1_c(). Only the first fatal exception is latched so the original fault survives a crash loop, and window0[3] counts exceptions. Query the window0 base address and uncached region via Device Tree macros (HP_SRAM_WIN0_BASE and CONFIG_INTEL_ADSP_UNCACHED_REGION) to prevent using hardcoded magic pointers. Signed-off-by: Liam Girdwood --- arch/xtensa/Kconfig | 14 ++++++++++++++ arch/xtensa/core/vector_handlers.c | 27 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 024d1c5b36c1..f002a1b5a4dc 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -92,6 +92,20 @@ config XTENSA_EXCEPTION_ENTER_GDB When an exception like invalid address access or division by zero is hit, enter the GDB stub. +config XTENSA_ADSP_FATAL_BREADCRUMB + bool "Record fatal exception breadcrumbs in HP-SRAM window0" + depends on SOC_FAMILY_INTEL_ADSP + help + When a fatal exception is taken, record the faulting PC, exception + cause, faulting virtual address and the faulting instruction word + into HP-SRAM window0 via its uncached alias. The host SOF driver + prints window0[0] as "Firmware state" and window0[1] as "status/error + code" on an IPC timeout, so the crash information becomes visible + in the host dmesg even when no console or mtrace output is available. + Only the first fatal exception is latched so the original fault is + preserved across a crash loop. This is a debug aid and should be + left disabled in production builds. + menu "Xtensa HiFi Options" config XTENSA_CPU_HAS_HIFI diff --git a/arch/xtensa/core/vector_handlers.c b/arch/xtensa/core/vector_handlers.c index f816f67c24d4..1c1eb86eb6d0 100644 --- a/arch/xtensa/core/vector_handlers.c +++ b/arch/xtensa/core/vector_handlers.c @@ -19,6 +19,11 @@ #include #include +#if defined(CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB) +#include +#include +#endif + LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); extern char xtensa_arch_except_epc[]; @@ -564,6 +569,28 @@ void *xtensa_excint1_c(void *esf) cause = bsa->exccause; +#if defined(CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB) + /* DEBUG breadcrumb: when no console/mtrace is available, record the + * faulting PC/cause/vaddr into HP-SRAM window0 via its uncached alias. + * The host SOF driver prints window0[0] as "Firmware state" and + * window0[1] as "status/error code" on IPC timeout, so the crash PC + * becomes visible in dmesg. Latch only the FIRST fatal exception so we + * see the original fault, not the last in a crash loop. Skip level-1 + * interrupts which are not faults. win0[3] counts all exceptions. + */ + if (cause != EXCCAUSE_LEVEL1_INTERRUPT) { + volatile uint32_t *win0 = (volatile uint32_t *)sys_cache_uncached_ptr_get((void *)HP_SRAM_WIN0_BASE); + + if ((win0[1] >> 28) != 0xeU) { + win0[0] = (uint32_t)bsa->pc; + win0[1] = 0xe0000000U | ((uint32_t)cause & 0xffU); + win0[2] = (uint32_t)bsa->excvaddr; + win0[4] = *(volatile uint32_t *)((uint32_t)bsa->pc & ~3U); + } + win0[3]++; + } +#endif + switch (cause) { case EXCCAUSE_LEVEL1_INTERRUPT: #ifdef CONFIG_XTENSA_MMU From 07cf83202385aa361ed979cd6150516740078e30 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 21:04:12 +0100 Subject: [PATCH 41/47] xtensa: adsp: record triple fault breadcrumbs in HP-SRAM window0 When a double exception escalates to a triple fault (escaping the CPU), record the EPC1, double-exception EXCCAUSE, and DEPC into HP-SRAM window0 via its uncached alias. This is a lightweight tracing aid that can be enabled in production builds to diagnose fatal CPU locks and double faults. Add CONFIG_XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB (default y, depends on SOC_FAMILY_INTEL_ADSP) to govern this feature independently of general fatal exception breadcrumbs. Construct the window0 uncached base address and the 0xdb1e0000 marker via bit shifts and additions in assembly rather than movi/l32r literals, to ensure LLVM IAS emits no out-of-range l32r relocations in the triple-fault handler (where literals pools may be unreachable). Signed-off-by: Liam Girdwood --- arch/xtensa/Kconfig | 11 ++++++++ arch/xtensa/core/xtensa_asm2_util.S | 41 +++++++++++------------------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index f002a1b5a4dc..837243a07736 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -106,6 +106,17 @@ config XTENSA_ADSP_FATAL_BREADCRUMB preserved across a crash loop. This is a debug aid and should be left disabled in production builds. +config XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB + bool "Record triple fault breadcrumbs in HP-SRAM window0" + depends on SOC_FAMILY_INTEL_ADSP + default y + help + When a double exception escalates to a triple fault (escaping the + CPU), record the EPC1, double-exception EXCCAUSE, and DEPC into + HP-SRAM window0 via its uncached alias. This is a lightweight + tracing aid that can be enabled in production builds to diagnose + fatal CPU locks and double faults. + menu "Xtensa HiFi Options" config XTENSA_CPU_HAS_HIFI diff --git a/arch/xtensa/core/xtensa_asm2_util.S b/arch/xtensa/core/xtensa_asm2_util.S index 47ed16a6c6d2..d4c427a436c8 100644 --- a/arch/xtensa/core/xtensa_asm2_util.S +++ b/arch/xtensa/core/xtensa_asm2_util.S @@ -11,13 +11,7 @@ #include #endif -#if defined(CONFIG_XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB) -#if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) -#define HP_SRAM_WIN0_BASE_UC 0x40024000 -#else -#define HP_SRAM_WIN0_BASE_UC 0x5e024000 -#endif -#endif + /* * xtensa_spill_reg_windows @@ -91,11 +85,7 @@ _high_gpr_spill_done: * time how many registers were spilled, then return, leaving the * modified SP in A1. */ -#ifdef __clang__ - addi a1, a1, -16 -#else addi a1, a1, -4 -#endif s32i a3, a1, 0 ret @@ -113,11 +103,7 @@ _high_gpr_spill_done: xtensa_restore_high_regs: /* pop our "original" stack pointer into a2, stash in a3 also */ l32i a2, a1, 0 -#ifdef __clang__ - addi a1, a1, 16 -#else addi a1, a1, 4 -#endif mov a3, a2 beq a1, a2, _high_restore_done @@ -624,7 +610,7 @@ _TripleFault: #if defined(CONFIG_XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB) /* DEBUG breadcrumb: record double-exception EPC1/EXCCAUSE/DEPC into * HP-SRAM window0 uncached alias so the host can see we triple-faulted. - * 0xdb1e marks a double exception. Clobbers a2/a3 (we spin forever next). + * 0xdb1e marks a double exception. Clobbers a2/a3/a4. */ /* 1. Construct base address in a3 without causing literals/l32r */ #if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) @@ -638,20 +624,25 @@ _TripleFault: slli a4, a4, 8 add a3, a3, a4 - /* 2. Write EPC1 to win0[0] = the first exception PC being handled - * when the double exception fired. This is what we want to debug. - */ + /* 2. Write EPC1 to win0[0] */ rsr.epc1 a2 - movi a3, HP_SRAM_WIN0_BASE_UC s32i a2, a3, 0 - rsr.exccause a2 - movi a3, 0xdb1e0000 - or a2, a2, a3 - movi a3, HP_SRAM_WIN0_BASE_UC + + /* 3. Write EXCCAUSE combined with 0xdb1e0000 to win0[1] */ + /* Construct 0xdb1e0000 in a2 without literals/l32r */ + movi a2, 0xdb + slli a2, a2, 8 + movi a4, 0x1e + add a2, a2, a4 + slli a2, a2, 16 + /* Combine with EXCCAUSE */ + rsr.exccause a4 + or a2, a2, a4 s32i a2, a3, 4 + + /* 4. Write DEPC to win0[2] */ rsr.depc a2 - movi a3, HP_SRAM_WIN0_BASE_UC s32i a2, a3, 8 memw #endif From 8c32292dd20c93f6dc9a59679668c1e1977a9a3a Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 9 Jul 2026 17:01:49 +0100 Subject: [PATCH 42/47] xtensa: adsp: Support selecting diagnostic data in fatal breadcrumbs Add a Kconfig choice (XTENSA_ADSP_FATAL_BREADCRUMB_DATA) to select which diagnostic information is written to window0[1] (which is printed in dmesg as the 'status/error code' on IPC timeout): - Exception cause (default) - Faulting virtual address (excvaddr) - Caller return address (a0) Also fix compilation under Clang by: - Including in vector_handlers.c to define L2_SRAM_BASE. - Constructing the uncached memory window base (0x40024000/0x5e024000) and signature dynamically using bit shifts and additions in assembly (xtensa_asm2_util.S) to avoid literal generation under Clang's Integrated Assembler. Signed-off-by: Liam Girdwood --- arch/xtensa/Kconfig | 28 ++++++++++++++++++++++++++++ arch/xtensa/core/vector_handlers.c | 9 ++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 837243a07736..355a38d9d595 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -106,6 +106,34 @@ config XTENSA_ADSP_FATAL_BREADCRUMB preserved across a crash loop. This is a debug aid and should be left disabled in production builds. +if XTENSA_ADSP_FATAL_BREADCRUMB + +choice XTENSA_ADSP_FATAL_BREADCRUMB_DATA + prompt "Fatal breadcrumb data selection" + default XTENSA_ADSP_FATAL_BREADCRUMB_DATA_CAUSE + help + Select which exception diagnostic data to write into window0[1], which + is printed as the "status/error code" in the host dmesg log. + +config XTENSA_ADSP_FATAL_BREADCRUMB_DATA_CAUSE + bool "Exception cause" + help + Record the exception cause in window0[1]. + +config XTENSA_ADSP_FATAL_BREADCRUMB_DATA_VADDR + bool "Faulting virtual address (excvaddr)" + help + Record the faulting virtual address (excvaddr) in window0[1]. + +config XTENSA_ADSP_FATAL_BREADCRUMB_DATA_A0 + bool "Caller return address (a0)" + help + Record the calling function's return address (a0) in window0[1]. + +endchoice + +endif # XTENSA_ADSP_FATAL_BREADCRUMB + config XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB bool "Record triple fault breadcrumbs in HP-SRAM window0" depends on SOC_FAMILY_INTEL_ADSP diff --git a/arch/xtensa/core/vector_handlers.c b/arch/xtensa/core/vector_handlers.c index 1c1eb86eb6d0..d9e5aab7bcd1 100644 --- a/arch/xtensa/core/vector_handlers.c +++ b/arch/xtensa/core/vector_handlers.c @@ -20,6 +20,7 @@ #include #if defined(CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB) +#include #include #include #endif @@ -581,9 +582,15 @@ void *xtensa_excint1_c(void *esf) if (cause != EXCCAUSE_LEVEL1_INTERRUPT) { volatile uint32_t *win0 = (volatile uint32_t *)sys_cache_uncached_ptr_get((void *)HP_SRAM_WIN0_BASE); - if ((win0[1] >> 28) != 0xeU) { + if (win0[0] == 0U) { win0[0] = (uint32_t)bsa->pc; +#if defined(CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB_DATA_VADDR) + win0[1] = (uint32_t)bsa->excvaddr; +#elif defined(CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB_DATA_A0) + win0[1] = (uint32_t)bsa->a0; +#else win0[1] = 0xe0000000U | ((uint32_t)cause & 0xffU); +#endif win0[2] = (uint32_t)bsa->excvaddr; win0[4] = *(volatile uint32_t *)((uint32_t)bsa->pc & ~3U); } From 73577cc4688fabeba5f68c6c0d97f7c666764015 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 16:06:13 +0100 Subject: [PATCH 43/47] xtensa: adsp: breadcrumb: Guard instruction fetch against unmapped PC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The breadcrumb code in xtensa_excint1_c() reads the faulting instruction word via: win0[4] = *(volatile uint32_t *)(bsa->pc & ~3) This is unsafe when the exception was caused by a bad function pointer or corrupted GOT entry — in that case bsa->pc itself is unmapped, so dereferencing it triggers a double exception inside the exception handler producing the 0xdb1e001d triple fault signature. Guard the read with a range check against the DSP SRAM window [0xa0000000, 0xa2000000). PCs outside this range are skipped; win0[0] already contains the faulting PC so no diagnostic information is lost. This makes the breadcrumb infrastructure safe on both ACE15 (no MMU, bad PC silently accesses wrong memory) and ACE30 (MMU, bad PC faults). Fixes: 98cafd9df5e (xtensa: adsp: record fatal exception breadcrumbs in HP-SRAM window0) Signed-off-by: Liam Girdwood --- arch/xtensa/core/vector_handlers.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/core/vector_handlers.c b/arch/xtensa/core/vector_handlers.c index d9e5aab7bcd1..d50213c11f7f 100644 --- a/arch/xtensa/core/vector_handlers.c +++ b/arch/xtensa/core/vector_handlers.c @@ -592,7 +592,15 @@ void *xtensa_excint1_c(void *esf) win0[1] = 0xe0000000U | ((uint32_t)cause & 0xffU); #endif win0[2] = (uint32_t)bsa->excvaddr; - win0[4] = *(volatile uint32_t *)((uint32_t)bsa->pc & ~3U); + /* Only read the faulting instruction if the PC is within + * the DSP SRAM range to avoid a double exception when the + * exception was caused by a bad/unmapped function pointer. + */ + uint32_t fault_pc = (uint32_t)bsa->pc & ~3U; + + if (fault_pc >= 0xa0000000U && fault_pc < 0xa2000000U) { + win0[4] = *(volatile uint32_t *)fault_pc; + } } win0[3]++; } From fc1812a7a2d5b8dfcd425f278cebd39299b2927b Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 17:04:04 +0100 Subject: [PATCH 44/47] arch: xtensa: debug: move double exception breadcrumbs to win0[4-6] Signed-off-by: Liam Girdwood --- arch/xtensa/core/xtensa_asm2_util.S | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/xtensa/core/xtensa_asm2_util.S b/arch/xtensa/core/xtensa_asm2_util.S index d4c427a436c8..f704701888e9 100644 --- a/arch/xtensa/core/xtensa_asm2_util.S +++ b/arch/xtensa/core/xtensa_asm2_util.S @@ -624,12 +624,12 @@ _TripleFault: slli a4, a4, 8 add a3, a3, a4 - /* 2. Write EPC1 to win0[0] */ + /* 2. Write EPC1 to win0[4] (offset 16) */ rsr.epc1 a2 - s32i a2, a3, 0 + s32i a2, a3, 16 - /* 3. Write EXCCAUSE combined with 0xdb1e0000 to win0[1] */ + /* 3. Write EXCCAUSE combined with 0xdb1e0000 to win0[5] (offset 20) */ /* Construct 0xdb1e0000 in a2 without literals/l32r */ movi a2, 0xdb slli a2, a2, 8 @@ -639,11 +639,11 @@ _TripleFault: /* Combine with EXCCAUSE */ rsr.exccause a4 or a2, a2, a4 - s32i a2, a3, 4 + s32i a2, a3, 20 - /* 4. Write DEPC to win0[2] */ + /* 4. Write DEPC to win0[6] (offset 24) */ rsr.depc a2 - s32i a2, a3, 8 + s32i a2, a3, 24 memw #endif 1: From 989fff115654b9df531e1fdc4bf1fcaf950af62e Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 17:05:54 +0100 Subject: [PATCH 45/47] arch: xtensa: debug: overwrite ROM boot status with crash PC if win0[0] < 0xa0000000 Signed-off-by: Liam Girdwood --- arch/xtensa/core/vector_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/xtensa/core/vector_handlers.c b/arch/xtensa/core/vector_handlers.c index d50213c11f7f..fdefedf45ecb 100644 --- a/arch/xtensa/core/vector_handlers.c +++ b/arch/xtensa/core/vector_handlers.c @@ -582,7 +582,7 @@ void *xtensa_excint1_c(void *esf) if (cause != EXCCAUSE_LEVEL1_INTERRUPT) { volatile uint32_t *win0 = (volatile uint32_t *)sys_cache_uncached_ptr_get((void *)HP_SRAM_WIN0_BASE); - if (win0[0] == 0U) { + if (win0[0] < 0xa0000000U) { win0[0] = (uint32_t)bsa->pc; #if defined(CONFIG_XTENSA_ADSP_FATAL_BREADCRUMB_DATA_VADDR) win0[1] = (uint32_t)bsa->excvaddr; From 4ca9dbb8144f3240dae3548a3751ab83ed8fc7ec Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 12 Jul 2026 17:09:38 +0100 Subject: [PATCH 46/47] arch: xtensa: Kconfig: enable XTENSA_ADSP_FATAL_BREADCRUMB by default if TRIPLE_FAULT_BREADCRUMB is enabled Signed-off-by: Liam Girdwood --- arch/xtensa/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 355a38d9d595..7b1db84f8c5e 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -95,6 +95,7 @@ config XTENSA_EXCEPTION_ENTER_GDB config XTENSA_ADSP_FATAL_BREADCRUMB bool "Record fatal exception breadcrumbs in HP-SRAM window0" depends on SOC_FAMILY_INTEL_ADSP + default y if XTENSA_ADSP_TRIPLE_FAULT_BREADCRUMB help When a fatal exception is taken, record the faulting PC, exception cause, faulting virtual address and the faulting instruction word From b56562e7aa6ec380f441990836aea6dc819097ac Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Sun, 19 Jul 2026 08:56:27 +0100 Subject: [PATCH 47/47] Revert "arch: xtensa: asm: replace rsr.eps/epc pseudo-ops and add .literal_position for IAS" This reverts commit 264b8f55d409ea4a6e73a844275b0b0b87c4f3cd. The modern upstream LLVM/Clang toolchain (version 23.0.0git) natively supports compiling the rsr.epsN/rsr.epcN instruction forms using the integrated assembler (-fintegrated-as). Additionally, .literal_position is not required for crt1.S compilation under the current toolchain. Testing confirms that removing this commit introduces no compilation failures for tgl, mtl, or ptl SOF targets. Signed-off-by: Liam Girdwood --- arch/xtensa/core/crt1.S | 1 - arch/xtensa/include/xtensa_asm2.inc.S | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/xtensa/core/crt1.S b/arch/xtensa/core/crt1.S index 9774b303a399..1b0867d76ce9 100644 --- a/arch/xtensa/core/crt1.S +++ b/arch/xtensa/core/crt1.S @@ -37,7 +37,6 @@ .text .align 4 - .literal_position _start: #if XCHAL_HAVE_CP diff --git a/arch/xtensa/include/xtensa_asm2.inc.S b/arch/xtensa/include/xtensa_asm2.inc.S index 62c7b1597e8d..7b807418b74a 100644 --- a/arch/xtensa/include/xtensa_asm2.inc.S +++ b/arch/xtensa/include/xtensa_asm2.inc.S @@ -791,7 +791,7 @@ _not_triple_fault: and a0, a0, a2 s32i a0, a1, ___xtensa_irq_bsa_t_ps_OFFSET .else - rsr a0, eps\LVL + rsr.eps\LVL a0 s32i a0, a1, ___xtensa_irq_bsa_t_ps_OFFSET .endif @@ -802,7 +802,7 @@ _not_triple_fault: mov a3, a0 #endif - rsr a0, epc\LVL + rsr.epc\LVL a0 s32i a0, a1, ___xtensa_irq_bsa_t_pc_OFFSET /* What's happening with this jump is that the L32R