From 0ac5bdc7763cc07b3cf40783cf0da05c2b77cdc8 Mon Sep 17 00:00:00 2001 From: Sudan Landge Date: Wed, 11 Jun 2025 14:35:48 +0100 Subject: [PATCH 01/36] [nrf fromtree] boards: mps4: Enable non-secure variant support Zephyr's TF-M has been aligned with upstream TF-M v2.2.0, which adds support for Corstone-320 (CS320). The previous commit also updates TF-M to fix compiler warnings seen with MPS4. So, with this update, enable build and execution of non-secure variants of MPS4-based boards. Signed-off-by: Sudan Landge (cherry picked from commit 826742fca2c0698f0a0972fe13a51b88434e7bed) --- boards/arm/mps4/board.cmake | 11 ++++++++++- boards/arm/mps4/mps4_corstone315_fvp_ns.yaml | 3 +++ boards/arm/mps4/mps4_corstone320_fvp_ns.yaml | 3 +++ modules/trusted-firmware-m/Kconfig.tfm | 6 ++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/boards/arm/mps4/board.cmake b/boards/arm/mps4/board.cmake index 9c4d4a0ef337..56090124532a 100644 --- a/boards/arm/mps4/board.cmake +++ b/boards/arm/mps4/board.cmake @@ -33,7 +33,16 @@ if(CONFIG_ARM_PAC OR CONFIG_ARM_BTI) endif() if(CONFIG_BUILD_WITH_TFM) - set(ARMFVP_FLAGS ${ARMFVP_FLAGS} -a ${APPLICATION_BINARY_DIR}/zephyr/tfm_merged.hex) + # Workaround: Use binary (.bin) format images until TF-M supports generating them in hex (.hex) + # format. The image load addresses are referred from the TF-M official documentation at: + # https://trustedfirmware-m.readthedocs.io/en/latest/platform/arm/mps4/corstone320/README.html + set(ARMFVP_FLAGS ${ARMFVP_FLAGS} + --data ${APPLICATION_BINARY_DIR}/tfm/bin/bl1_1.bin@0x11000000 + --data ${APPLICATION_BINARY_DIR}/tfm/bin/cm_provisioning_bundle.bin@0x12024000 + --data ${APPLICATION_BINARY_DIR}/tfm/bin/dm_provisioning_bundle.bin@0x1202aa00 + --data ${APPLICATION_BINARY_DIR}/tfm/bin/bl2_signed.bin@0x12031400 + -a ${APPLICATION_BINARY_DIR}/zephyr/tfm_merged.hex + ) endif() # FVP Parameters diff --git a/boards/arm/mps4/mps4_corstone315_fvp_ns.yaml b/boards/arm/mps4/mps4_corstone315_fvp_ns.yaml index 01bee24100a2..c04889d7f6f4 100644 --- a/boards/arm/mps4/mps4_corstone315_fvp_ns.yaml +++ b/boards/arm/mps4/mps4_corstone315_fvp_ns.yaml @@ -7,6 +7,9 @@ type: mcu arch: arm ram: 1024 flash: 512 +simulation: + - name: armfvp + exec: FVP_Corstone_SSE-315 toolchain: - gnuarmemb - zephyr diff --git a/boards/arm/mps4/mps4_corstone320_fvp_ns.yaml b/boards/arm/mps4/mps4_corstone320_fvp_ns.yaml index 45ee954a3370..51bfb990e5f6 100644 --- a/boards/arm/mps4/mps4_corstone320_fvp_ns.yaml +++ b/boards/arm/mps4/mps4_corstone320_fvp_ns.yaml @@ -7,6 +7,9 @@ type: mcu arch: arm ram: 1024 flash: 512 +simulation: + - name: armfvp + exec: FVP_Corstone_SSE-320 toolchain: - gnuarmemb - zephyr diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index 36a734ad8b3e..dc4b206595d0 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -12,11 +12,13 @@ config TFM_BOARD string default "nxp/lpcxpresso55s69" if BOARD_LPCXPRESSO55S69_LPC55S69_CPU0_NS default "arm/mps2/an521" if BOARD_MPS2_AN521_CPU0_NS - default "arm/mps3/corstone300/fvp" if BOARD_MPS3_CORSTONE300_FVP_NS + default "arm/mps3/corstone300/fvp" if BOARD_MPS3_CORSTONE300_FVP_NS default "arm/mps3/corstone300/an547" if BOARD_MPS3_CORSTONE300_AN547_NS default "arm/mps3/corstone300/an552" if BOARD_MPS3_CORSTONE300_AN552_NS default "arm/mps3/corstone310/an555" if BOARD_MPS3_CORSTONE310_AN555_NS - default "arm/mps3/corstone310/fvp" if BOARD_MPS3_CORSTONE310_FVP_NS + default "arm/mps3/corstone310/fvp" if BOARD_MPS3_CORSTONE310_FVP_NS + default "arm/mps4/corstone315" if BOARD_MPS4_CORSTONE315_FVP_NS + default "arm/mps4/corstone320" if BOARD_MPS4_CORSTONE320_FVP_NS default "stm/b_u585i_iot02a" if BOARD_B_U585I_IOT02A default "stm/nucleo_l552ze_q" if BOARD_NUCLEO_L552ZE_Q default "stm/stm32l562e_dk" if BOARD_STM32L562E_DK From 064e7a5990122813d499aa6a2db2180e212d9d2d Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 31 Mar 2025 12:27:51 +0200 Subject: [PATCH 02/36] [nrf fromtree] boards: st: Add stm32wba65i-dk1 Add stm32wba65i-dk1 board support with UART console, LEDs, joystick keys using ADC channel 6. Board YAML file does not list 'supported' tags since the board is very similar to nucleo_wba65ri for which supported features are already covered. Signed-off-by: Etienne Carriere (cherry picked from commit 1dd6c2bb66ae6c7818d85970dd4f4040d667447e) --- boards/st/stm32wba65i_dk1/Kconfig.defconfig | 13 + .../stm32wba65i_dk1/Kconfig.stm32wba65i_dk1 | 5 + .../stm32wba65i_dk1/arduino_r3_connector.dtsi | 41 ++++ boards/st/stm32wba65i_dk1/board.cmake | 7 + boards/st/stm32wba65i_dk1/board.yml | 6 + .../doc/img/stm32wba65i-dk1.webp | Bin 0 -> 40424 bytes boards/st/stm32wba65i_dk1/doc/index.rst | 232 ++++++++++++++++++ boards/st/stm32wba65i_dk1/stm32wba65i_dk1.dts | 187 ++++++++++++++ .../st/stm32wba65i_dk1/stm32wba65i_dk1.yaml | 10 + .../stm32wba65i_dk1/stm32wba65i_dk1_defconfig | 24 ++ boards/st/stm32wba65i_dk1/support/openocd.cfg | 26 ++ 11 files changed, 551 insertions(+) create mode 100644 boards/st/stm32wba65i_dk1/Kconfig.defconfig create mode 100644 boards/st/stm32wba65i_dk1/Kconfig.stm32wba65i_dk1 create mode 100644 boards/st/stm32wba65i_dk1/arduino_r3_connector.dtsi create mode 100644 boards/st/stm32wba65i_dk1/board.cmake create mode 100644 boards/st/stm32wba65i_dk1/board.yml create mode 100644 boards/st/stm32wba65i_dk1/doc/img/stm32wba65i-dk1.webp create mode 100644 boards/st/stm32wba65i_dk1/doc/index.rst create mode 100644 boards/st/stm32wba65i_dk1/stm32wba65i_dk1.dts create mode 100644 boards/st/stm32wba65i_dk1/stm32wba65i_dk1.yaml create mode 100644 boards/st/stm32wba65i_dk1/stm32wba65i_dk1_defconfig create mode 100644 boards/st/stm32wba65i_dk1/support/openocd.cfg diff --git a/boards/st/stm32wba65i_dk1/Kconfig.defconfig b/boards/st/stm32wba65i_dk1/Kconfig.defconfig new file mode 100644 index 000000000000..620565526e58 --- /dev/null +++ b/boards/st/stm32wba65i_dk1/Kconfig.defconfig @@ -0,0 +1,13 @@ +# STM32WBA65I Discovery kit board configuration + +# Copyright (c) 2025 STMicroelectronics + +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_STM32WBA65I_DK1 + +config SPI_STM32_INTERRUPT + default y + depends on SPI + +endif # BOARD_STM32WBA65I_DK1 diff --git a/boards/st/stm32wba65i_dk1/Kconfig.stm32wba65i_dk1 b/boards/st/stm32wba65i_dk1/Kconfig.stm32wba65i_dk1 new file mode 100644 index 000000000000..9fcfc52471d8 --- /dev/null +++ b/boards/st/stm32wba65i_dk1/Kconfig.stm32wba65i_dk1 @@ -0,0 +1,5 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_STM32WBA65I_DK1 + select SOC_STM32WBA65XX diff --git a/boards/st/stm32wba65i_dk1/arduino_r3_connector.dtsi b/boards/st/stm32wba65i_dk1/arduino_r3_connector.dtsi new file mode 100644 index 000000000000..999cb7600ea3 --- /dev/null +++ b/boards/st/stm32wba65i_dk1/arduino_r3_connector.dtsi @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + }; +}; + +arduino_i2c: &i2c1 {}; +arduino_spi: &spi1 {}; diff --git a/boards/st/stm32wba65i_dk1/board.cmake b/boards/st/stm32wba65i_dk1/board.cmake new file mode 100644 index 000000000000..45abc466464f --- /dev/null +++ b/boards/st/stm32wba65i_dk1/board.cmake @@ -0,0 +1,7 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +board_runner_args(stm32cubeprogrammer "--port=swd" "--reset-mode=hw") + +include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd-stm32.board.cmake) diff --git a/boards/st/stm32wba65i_dk1/board.yml b/boards/st/stm32wba65i_dk1/board.yml new file mode 100644 index 000000000000..521ca2c3169e --- /dev/null +++ b/boards/st/stm32wba65i_dk1/board.yml @@ -0,0 +1,6 @@ +board: + name: stm32wba65i_dk1 + full_name: STM32WBA65I Discovery kit + vendor: st + socs: + - name: stm32wba65xx diff --git a/boards/st/stm32wba65i_dk1/doc/img/stm32wba65i-dk1.webp b/boards/st/stm32wba65i_dk1/doc/img/stm32wba65i-dk1.webp new file mode 100644 index 0000000000000000000000000000000000000000..4cae2e31f470880f264e98b0d351cffeff87aa20 GIT binary patch literal 40424 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 is required. + +Flashing an application to a STM32WBA65I-DK1 +-------------------------------------------- + +Here is an example for the :zephyr:code-sample:`blinky` application. + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: stm32wba65i_dk1 + :goals: build flash + +You will see the LED blinking every second. + +Debugging +========= + +Debugging using OpenOCD +----------------------- + +You can debug an application in the usual way using OpenOCD. Here is an example for the +:zephyr:code-sample:`blinky` application. + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: stm32wba65i_dk1 + :maybe-skip-config: + :goals: debug + +.. _STM32WBA Series on www.st.com: + https://www.st.com/en/microcontrollers-microprocessors/stm32wba-series.html + +.. _STM32CubeProgrammer: + https://www.st.com/en/development-tools/stm32cubeprog.html diff --git a/boards/st/stm32wba65i_dk1/stm32wba65i_dk1.dts b/boards/st/stm32wba65i_dk1/stm32wba65i_dk1.dts new file mode 100644 index 000000000000..48e70a3e121e --- /dev/null +++ b/boards/st/stm32wba65i_dk1/stm32wba65i_dk1.dts @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; +#include +#include +#include "arduino_r3_connector.dtsi" +#include + +/ { + model = "STMicroelectronics STM32WBA65I Discovery kit board"; + compatible = "st,stm32wba65i-dk1"; + + #address-cells = <1>; + #size-cells = <1>; + + chosen { + zephyr,bt-c2h-uart = &usart1; + zephyr,console = &usart1; + zephyr,shell-uart = &usart1; + zephyr,sram = &sram0; + zephyr,flash = &flash0; + }; + + leds: leds { + compatible = "gpio-leds"; + + green_led_1: led_0 { + gpios = <&gpiod 8 GPIO_ACTIVE_LOW>; + label = "User LD6"; + }; + + red_led_2: led_1 { + gpios = <&gpiod 9 GPIO_ACTIVE_LOW>; + label = "User LD5"; + }; + + blue_led_3: led_2 { + /* Not functional w/o a 0Ohm resistor on MB2143 R42 */ + gpios = <&gpiob 10 GPIO_ACTIVE_LOW>; + label = "User LD3"; + }; + }; + + adc-keys { + compatible = "adc-keys"; + io-channels = <&adc4 6>; + keyup-threshold-mv = <3300>; + + select_key { + press-thresholds-mv = <0>; + zephyr,code = ; + }; + + left_key { + press-thresholds-mv = <670>; + zephyr,code = ; + }; + + down_key { + press-thresholds-mv = <1320>; + zephyr,code = ; + }; + + up_key { + press-thresholds-mv = <2010>; + zephyr,code = ; + }; + + right_key { + press-thresholds-mv = <2650>; + zephyr,code = ; + }; + }; + + aliases { + led0 = &green_led_1; + led1 = &red_led_2; + }; +}; + +&clk_lsi { + status = "okay"; +}; + +&clk_lse { + status = "okay"; +}; + +&clk_hse { + hse-div2; + status = "okay"; +}; + +&clk_hsi { + status = "okay"; +}; + +&rcc { + clocks = <&clk_hse>; + clock-frequency = ; + ahb-prescaler = <1>; + ahb5-prescaler = <2>; + apb1-prescaler = <1>; + apb2-prescaler = <2>; + apb7-prescaler = <1>; +}; + +&iwdg { + status = "okay"; +}; + +&rtc { + status = "okay"; + clocks = <&rcc STM32_CLOCK(APB7, 21)>, + <&rcc STM32_SRC_LSE RTC_SEL(1)>; + prescaler = <32768>; +}; + +&usart1 { + clocks = <&rcc STM32_CLOCK(APB2, 14)>, + <&rcc STM32_SRC_HSI16 USART1_SEL(2)>; + pinctrl-0 = <&usart1_tx_pb12 &usart1_rx_pa8>; + pinctrl-1 = <&analog_pb12 &analog_pa8>; + pinctrl-names = "default", "sleep"; + current-speed = <115200>; + status = "okay"; +}; + +&spi1 { + pinctrl-0 = <&spi1_nss_pa12 &spi1_sck_pb4 + &spi1_miso_pb3 &spi1_mosi_pa15>; + pinctrl-names = "default"; + status = "okay"; +}; + +&i2c1 { + pinctrl-0 = <&i2c1_scl_pb2 &i2c1_sda_pb1>; + pinctrl-names = "default"; + status = "okay"; + clock-frequency = ; +}; + +&adc4 { + pinctrl-0 = <&adc4_in6_pa3>; + pinctrl-names = "default"; + st,adc-clock-source = "ASYNC"; + st,adc-prescaler = <4>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@6 { + reg = <0x6>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + zephyr,vref-mv = <3300>; + }; +}; + +stm32_lp_tick_source: &lptim1 { + clocks = <&rcc STM32_CLOCK(APB7, 11)>, + <&rcc STM32_SRC_LSE LPTIM1_SEL(3)>; + status = "okay"; +}; + +&rng { + status = "okay"; +}; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@1c0000 { + label = "storage"; + reg = <0x001c0000 DT_SIZE_K(256)>; + }; + }; +}; diff --git a/boards/st/stm32wba65i_dk1/stm32wba65i_dk1.yaml b/boards/st/stm32wba65i_dk1/stm32wba65i_dk1.yaml new file mode 100644 index 000000000000..b77e57240f1a --- /dev/null +++ b/boards/st/stm32wba65i_dk1/stm32wba65i_dk1.yaml @@ -0,0 +1,10 @@ +identifier: stm32wba65i_dk1/stm32wba65xx +name: ST STM32WBA65I Discovery kit +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb +ram: 512 +flash: 2048 +vendor: st diff --git a/boards/st/stm32wba65i_dk1/stm32wba65i_dk1_defconfig b/boards/st/stm32wba65i_dk1/stm32wba65i_dk1_defconfig new file mode 100644 index 000000000000..5e650e6826cb --- /dev/null +++ b/boards/st/stm32wba65i_dk1/stm32wba65i_dk1_defconfig @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2025 STMicroelectronics + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable GPIO +CONFIG_GPIO=y + +# Console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# Enable HW stack protection +CONFIG_HW_STACK_PROTECTION=y + +# Enable the internal SMPS regulator +CONFIG_POWER_SUPPLY_DIRECT_SMPS=y + +# Enable ADC for joystick +CONFIG_ADC=y diff --git a/boards/st/stm32wba65i_dk1/support/openocd.cfg b/boards/st/stm32wba65i_dk1/support/openocd.cfg new file mode 100644 index 000000000000..0745453a16a5 --- /dev/null +++ b/boards/st/stm32wba65i_dk1/support/openocd.cfg @@ -0,0 +1,26 @@ +# Note: Using OpenOCD using stm32wba65i_dk1 requires using openocd fork. +# See board documentation for more information + +source [find interface/stlink-dap.cfg] + +set WORKAREASIZE 0x8000 + +transport select "dapdirect_swd" + +# Enable debug when in low power modes +set ENABLE_LOW_POWER 1 + +# Stop Watchdog counters when halt +set STOP_WATCHDOG 1 + +# STlink Debug clock frequency +set CLOCK_FREQ 8000 + +# Reset configuration +# use hardware reset, connect under reset +# connect_assert_srst needed if low power mode application running (WFI...) +reset_config srst_only srst_nogate + +source [find target/stm32wbax.cfg] + +gdb_memory_map disable From d2d05152612d25afe69cde719f8fd4d88bbcd56c Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Fri, 16 May 2025 09:33:16 +0200 Subject: [PATCH 03/36] [nrf fromtree] modules: trusted-firmware-m: Declare stm32wba65i support Declare stm32wba65i-dk1 and nucleo_wba65ri boards support in TF-M. Both comply with TF-M integration of platform stm/stm32wba65i-dk. Signed-off-by: Etienne Carriere (cherry picked from commit 0218849c4870847af177ae6747a198d8974e2f3f) --- modules/trusted-firmware-m/Kconfig.tfm | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index dc4b206595d0..10c761b0457e 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -22,6 +22,7 @@ config TFM_BOARD default "stm/b_u585i_iot02a" if BOARD_B_U585I_IOT02A default "stm/nucleo_l552ze_q" if BOARD_NUCLEO_L552ZE_Q default "stm/stm32l562e_dk" if BOARD_STM32L562E_DK + default "stm/stm32wba65i_dk" if BOARD_NUCLEO_WBA65RI || BOARD_STM32WBA65I_DK1 default "arm/musca_b1" if BOARD_V2M_MUSCA_B1 default "arm/musca_s1" if BOARD_V2M_MUSCA_S1 default "adi/max32657" if BOARD_MAX32657EVKIT_MAX32657_NS From ef624a7c3fcd155255a8953e4954a2366458d195 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Fri, 18 Apr 2025 08:56:21 +0200 Subject: [PATCH 04/36] [nrf fromtree] modules: trusted-firmware-m: Add STM32_FLASH_LAYOUT_BEGIN_OFFSET Add TF-M directive STM32_FLASH_LAYOUT_BEGIN_OFFSET needed to specify the gap needed by external boot stage resources at flash beginning. The offset tells STM32 TF-M firmware the base offset in the flash where the several TF-M and non-secure image areas shall be located. The CMake directive was introduced mainline TF-M commit [1] and merged in Zephyr TF-M repository [2]. Link: https://github.com/TrustedFirmware-M/trusted-firmware-m/commit/fc035b874e0ab86e2a13a328da3dce0cf18eb566 [1] Link: https://github.com/zephyrproject-rtos/trusted-firmware-m/commit/954dc805411be8fedac4ed7ddfefe966ffb35576 [2] Signed-off-by: Etienne Carriere (cherry picked from commit f4b9e5f68e238197785e91a20644110a17be11bb) --- modules/trusted-firmware-m/CMakeLists.txt | 6 ++++++ modules/trusted-firmware-m/Kconfig.tfm | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 0add755cdc67..a8d51666591e 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -281,6 +281,12 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DETHOS_DRIVER_PATH=${CONFIG_TFM_ETHOS_DRIVER_PATH_LOCAL}) endif() + if(CONFIG_TFM_STM32_FLASH_LAYOUT_BEGIN_OFFSET) + list(APPEND TFM_CMAKE_ARGS + -DSTM32_FLASH_LAYOUT_BEGIN_OFFSET=${CONFIG_TFM_STM32_FLASH_LAYOUT_BEGIN_OFFSET} + ) + endif() + file(MAKE_DIRECTORY ${TFM_BINARY_DIR}) add_custom_target(tfm_cmake DEPENDS ${TFM_BINARY_DIR}/CMakeCache.txt diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index 10c761b0457e..06c7221321ec 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -519,4 +519,14 @@ config TFM_EXCEPTION_INFO_DUMP On fatal errors in the secure firmware, capture info about the exception. Print the info if the SPM log level is sufficient. +config TFM_STM32_FLASH_LAYOUT_BEGIN_OFFSET + int "Offset gap at beginning of flash layout" + depends on SOC_FAMILY_STM32 + default 0 + help + Set an offset at the beginning of the STM32 platform flash + layout above which TF-M resources location start. The platform uses + this gap for platform specific reason, as for example when a + bootloader that is external to TF-M is used. + endif # BUILD_WITH_TFM From 39b50483d09373b327407777890c9ac365eb7022 Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Tue, 26 Aug 2025 15:53:10 +0300 Subject: [PATCH 05/36] [nrf fromtree] soc: adi: max32: Add support for MAX32658 SoC MAX32658 is the 1.8V variant of MAX32657. From a software perspective, both SoCs are functionally equivalent. Reuse the existing MAX32657 backend for MAX32658 to enable support with minimal changes. Signed-off-by: Tahsin Mutlugun (cherry picked from commit a97b2007cf9fee0038cab116f7e3374be2ef2e84) --- soc/adi/max32/Kconfig.soc | 5 +++++ soc/adi/max32/soc.yml | 1 + 2 files changed, 6 insertions(+) diff --git a/soc/adi/max32/Kconfig.soc b/soc/adi/max32/Kconfig.soc index e4b2347ca021..7bd66bf2cc70 100644 --- a/soc/adi/max32/Kconfig.soc +++ b/soc/adi/max32/Kconfig.soc @@ -33,6 +33,10 @@ config SOC_MAX32657 bool select SOC_FAMILY_MAX32_M33 +config SOC_MAX32658 + bool + select SOC_MAX32657 + config SOC_MAX32660 bool select SOC_FAMILY_MAX32_M4 @@ -97,6 +101,7 @@ config SOC default "max32650" if SOC_MAX32650 default "max32655" if SOC_MAX32655 default "max32657" if SOC_MAX32657 + default "max32658" if SOC_MAX32658 default "max32660" if SOC_MAX32660 default "max32662" if SOC_MAX32662 default "max32666" if SOC_MAX32666 diff --git a/soc/adi/max32/soc.yml b/soc/adi/max32/soc.yml index 1a4b7c2568d3..270c8c28dfb3 100644 --- a/soc/adi/max32/soc.yml +++ b/soc/adi/max32/soc.yml @@ -9,6 +9,7 @@ family: cpuclusters: - name: m4 - name: max32657 + - name: max32658 - name: max32660 - name: max32662 - name: max32666 From 9fbe3a121112db79be3fee7a0ecee874b2b5d4a0 Mon Sep 17 00:00:00 2001 From: Tahsin Mutlugun Date: Tue, 26 Aug 2025 16:06:45 +0300 Subject: [PATCH 06/36] [nrf fromtree] boards: adi: Add MAX32658EVKIT secure and nonsecure boards Adds MAX32658EVKIT board with secure and nonsecure variants. Signed-off-by: Tahsin Mutlugun (cherry picked from commit 9625f00929d891a9b7add1432aba8319c3decb6d) --- boards/adi/max32658evkit/Kconfig.defconfig | 48 ++ .../adi/max32658evkit/Kconfig.max32658evkit | 6 + boards/adi/max32658evkit/board.cmake | 12 + boards/adi/max32658evkit/board.yml | 10 + .../max32658evkit/doc/img/max32658evkit.webp | Bin 0 -> 76448 bytes boards/adi/max32658evkit/doc/index.rst | 571 ++++++++++++++++++ .../max32658evkit/max32658evkit_max32658.dts | 52 ++ .../max32658evkit/max32658evkit_max32658.yaml | 21 + .../max32658evkit_max32658_common.dtsi | 108 ++++ .../max32658evkit_max32658_defconfig | 16 + .../max32658evkit_max32658_ns.dts | 75 +++ .../max32658evkit_max32658_ns.yaml | 20 + .../max32658evkit_max32658_ns_defconfig | 19 + modules/trusted-firmware-m/CMakeLists.txt | 2 +- modules/trusted-firmware-m/Kconfig.tfm | 2 +- 15 files changed, 960 insertions(+), 2 deletions(-) create mode 100644 boards/adi/max32658evkit/Kconfig.defconfig create mode 100644 boards/adi/max32658evkit/Kconfig.max32658evkit create mode 100644 boards/adi/max32658evkit/board.cmake create mode 100644 boards/adi/max32658evkit/board.yml create mode 100644 boards/adi/max32658evkit/doc/img/max32658evkit.webp create mode 100644 boards/adi/max32658evkit/doc/index.rst create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658.dts create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658.yaml create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658_common.dtsi create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658_defconfig create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658_ns.dts create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658_ns.yaml create mode 100644 boards/adi/max32658evkit/max32658evkit_max32658_ns_defconfig diff --git a/boards/adi/max32658evkit/Kconfig.defconfig b/boards/adi/max32658evkit/Kconfig.defconfig new file mode 100644 index 000000000000..8235ba516f2b --- /dev/null +++ b/boards/adi/max32658evkit/Kconfig.defconfig @@ -0,0 +1,48 @@ +# Copyright (c) 2024-2025 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_MAX32658EVKIT + +# Code Partition: +# +# For the secure version of the board the firmware is linked at the beginning +# of the flash, or into the code-partition defined in DT if it is intended to +# be loaded by MCUboot. If the secure firmware is to be combined with a non- +# secure image (TRUSTED_EXECUTION_SECURE=y), the secure FW image shall always +# be restricted to the size of its code partition. +# +# For the non-secure version of the board, the firmware +# must be linked into the code-partition (non-secure) defined in DT, regardless. +# Apply this configuration below by setting the Kconfig symbols used by +# the linker according to the information extracted from DT partitions. + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_CODE_PARTITION := zephyr,code-partition + +config FLASH_LOAD_SIZE + default $(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +if BOARD_MAX32658EVKIT_MAX32658_NS + +config FLASH_LOAD_OFFSET + default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_CODE_PARTITION)) + +# MAX32658 has one UART interface, +# It can be used either on TFM or Zephyr +# Enabling debug (TFM_SPM_LOG_LEVEL || TFM_PARTITION_LOG_LEVEL) will transfer it to the TFM side +# Disabling TFM debug will transfer it to the Zephyr side. + +choice TFM_SPM_LOG_LEVEL + default TFM_SPM_LOG_LEVEL_SILENCE +endchoice + +choice TFM_PARTITION_LOG_LEVEL + default TFM_PARTITION_LOG_LEVEL_SILENCE +endchoice + +endif # BOARD_MAX32658EVKIT_MAX32658_NS + +config I3C + default y if ADXL367 + +endif # BOARD_MAX32658EVKIT diff --git a/boards/adi/max32658evkit/Kconfig.max32658evkit b/boards/adi/max32658evkit/Kconfig.max32658evkit new file mode 100644 index 000000000000..d7b695e47587 --- /dev/null +++ b/boards/adi/max32658evkit/Kconfig.max32658evkit @@ -0,0 +1,6 @@ +# Copyright (c) 2024-2025 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_MAX32658EVKIT + select SOC_MAX32658 if BOARD_MAX32658EVKIT_MAX32658 || \ + BOARD_MAX32658EVKIT_MAX32658_NS diff --git a/boards/adi/max32658evkit/board.cmake b/boards/adi/max32658evkit/board.cmake new file mode 100644 index 000000000000..32e6669b7018 --- /dev/null +++ b/boards/adi/max32658evkit/board.cmake @@ -0,0 +1,12 @@ +# Copyright (c) 2024-2025 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_BOARD_MAX32658EVKIT_MAX32658_NS) + set_property(TARGET runners_yaml_props_target PROPERTY hex_file tfm_merged.hex) +endif() + +board_runner_args(jlink "--device=MAX32658" "--reset-after-load") + +include(${ZEPHYR_BASE}/boards/common/openocd-adi-max32.boards.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/boards/adi/max32658evkit/board.yml b/boards/adi/max32658evkit/board.yml new file mode 100644 index 000000000000..07a2bd79226d --- /dev/null +++ b/boards/adi/max32658evkit/board.yml @@ -0,0 +1,10 @@ +# Copyright (c) 2024-2025 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +board: + name: max32658evkit + vendor: adi + socs: + - name: max32658 + variants: + - name: "ns" diff --git a/boards/adi/max32658evkit/doc/img/max32658evkit.webp b/boards/adi/max32658evkit/doc/img/max32658evkit.webp new file mode 100644 index 0000000000000000000000000000000000000000..7bbf5b668eef641659656e1ba888ba53d0954131 GIT binary patch literal 76448 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../modules/tee/tf-m/trusted-firmware-m/platform/ext/target/adi/max32657/partition/flash_layout.h file.` +If you would like to update flash region for your application you shall update related section in +these files. + +Additionally if firmware update feature requires slot1 and slot1_ns section need to be +defined. On default the section size set as 0 due to firmware update not requires on default. + + +Peripherals and Memory Ownership +-------------------------------- + +The ARM Security Extensions model allows system developers to partition device hardware and +software resources, so that they exist in either the Secure world for the security subsystem, +or the Normal world for everything else. Correct system design can ensure that no Secure world +assets can be accessed from the Normal world. A Secure design places all sensitive resources +in the Secure world, and ideally has robust software running that can protect assets against +a wide range of possible software attacks (`1`_). + +MPC (Memory Protection Controller) and PPC (Peripheral Protection Controller) are allow to +protect memory and peripheral. Incase of need peripheral and flash ownership can be updated in +../modules/tee/tf-m/trusted-firmware-m/platform/ext/target/adi/max32657/s_ns_access.cmake` +file by updating cmake flags to ON/OFF. + +As an example for below configuration TRNG, SRAM_0 and SRAM_1 is not going to be accessible +by non-secure. All others is going to be accessible by NS world. + +.. code-block:: + + set(ADI_NS_PRPH_GCR ON CACHE BOOL "") + set(ADI_NS_PRPH_SIR ON CACHE BOOL "") + set(ADI_NS_PRPH_FCR ON CACHE BOOL "") + set(ADI_NS_PRPH_WDT ON CACHE BOOL "") + set(ADI_NS_PRPH_AES OFF CACHE BOOL "") + set(ADI_NS_PRPH_AESKEY OFF CACHE BOOL "") + set(ADI_NS_PRPH_CRC ON CACHE BOOL "") + set(ADI_NS_PRPH_GPIO0 ON CACHE BOOL "") + set(ADI_NS_PRPH_TIMER0 ON CACHE BOOL "") + set(ADI_NS_PRPH_TIMER1 ON CACHE BOOL "") + set(ADI_NS_PRPH_TIMER2 ON CACHE BOOL "") + set(ADI_NS_PRPH_TIMER3 ON CACHE BOOL "") + set(ADI_NS_PRPH_TIMER4 ON CACHE BOOL "") + set(ADI_NS_PRPH_TIMER5 ON CACHE BOOL "") + set(ADI_NS_PRPH_I3C ON CACHE BOOL "") + set(ADI_NS_PRPH_UART ON CACHE BOOL "") + set(ADI_NS_PRPH_SPI ON CACHE BOOL "") + set(ADI_NS_PRPH_TRNG OFF CACHE BOOL "") + set(ADI_NS_PRPH_BTLE_DBB ON CACHE BOOL "") + set(ADI_NS_PRPH_BTLE_RFFE ON CACHE BOOL "") + set(ADI_NS_PRPH_RSTZ ON CACHE BOOL "") + set(ADI_NS_PRPH_BOOST ON CACHE BOOL "") + set(ADI_NS_PRPH_BBSIR ON CACHE BOOL "") + set(ADI_NS_PRPH_BBFCR ON CACHE BOOL "") + set(ADI_NS_PRPH_RTC ON CACHE BOOL "") + set(ADI_NS_PRPH_WUT0 ON CACHE BOOL "") + set(ADI_NS_PRPH_WUT1 ON CACHE BOOL "") + set(ADI_NS_PRPH_PWR ON CACHE BOOL "") + set(ADI_NS_PRPH_MCR ON CACHE BOOL "") + + # SRAMs + set(ADI_NS_SRAM_0 OFF CACHE BOOL "Size: 32KB") + set(ADI_NS_SRAM_1 OFF CACHE BOOL "Size: 32KB") + set(ADI_NS_SRAM_2 ON CACHE BOOL "Size: 64KB") + set(ADI_NS_SRAM_3 ON CACHE BOOL "Size: 64KB") + set(ADI_NS_SRAM_4 ON CACHE BOOL "Size: 64KB") + + # Ramfuncs section size + set(ADI_S_RAM_CODE_SIZE "0x800" CACHE STRING "Default: 2KB") + + # Flash: BL2, TFM and Zephyr are contiguous sections. + set(ADI_FLASH_AREA_BL2_SIZE "0x10000" CACHE STRING "Default: 64KB") + set(ADI_FLASH_S_PARTITION_SIZE "0x50000" CACHE STRING "Default: 320KB") + set(ADI_FLASH_NS_PARTITION_SIZE "0x90000" CACHE STRING "Default: 576KB") + set(ADI_FLASH_PS_AREA_SIZE "0x4000" CACHE STRING "Default: 16KB") + set(ADI_FLASH_ITS_AREA_SIZE "0x4000" CACHE STRING "Default: 16KB") + + # + # Allow user set S-NS resources ownership by overlay file + # + if(EXISTS "${CMAKE_BINARY_DIR}/../../s_ns_access_overlay.cmake") + include(${CMAKE_BINARY_DIR}/../../s_ns_access_overlay.cmake) + endif() + + +As an alternative method (which recommended) user can configurate ownership peripheral by +an cmake overlay file too without touching TF-M source files. For this path +create ``s_ns_access_overlay.cmake`` file under your project root folder and put peripheral/memory +you would like to be accessible by secure world. + +As an example if below configuration files been put in the ``s_ns_access_overlay.cmake`` file +TRNG, SRAM_0 and SRAM_1 will be accessible by secure world only. + +.. code-block:: + + set(ADI_NS_PRPH_TRNG OFF CACHE BOOL "") + set(ADI_NS_SRAM_0 OFF CACHE BOOL "Size: 32KB") + set(ADI_NS_SRAM_1 OFF CACHE BOOL "Size: 32KB") + + +Programming and Debugging +************************* + +.. zephyr:board-supported-runners:: + +Flashing +======== + +Here is an example for the :zephyr:code-sample:`hello_world` application. This example uses the +:ref:`jlink-debug-host-tools` as default. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: max32658evkit/max32658 + :goals: flash + +Open a serial terminal, reset the board (press the RESET button), and you should +see the following message in the terminal: + +.. code-block:: console + + ***** Booting Zephyr OS build v4.1.0 ***** + Hello World! max32658evkit/max32658 + +Building and flashing secure/non-secure with Arm |reg| TrustZone |reg| +---------------------------------------------------------------------- +The TF-M integration samples can be run using the +``max32658evkit/max32658/ns`` board target. To run we need to manually flash +the resulting image (``tfm_merged.hex``) with a J-Link as follows +(reset and erase are for recovering a locked core): + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: max32658evkit/max32658/ns + :goals: build + +.. code-block:: console + + west flash --hex-file build/zephyr/tfm_merged.hex + +.. code-block:: console + + [INF] Starting bootloader + [WRN] This device was provisioned with dummy keys. This device is NOT SECURE + [INF] PSA Crypto init done, sig_type: RSA-3072 + [WRN] Cannot upgrade: slots have non-compatible sectors + [WRN] Cannot upgrade: slots have non-compatible sectors + [INF] Bootloader chainload address offset: 0x10000 + [INF] Jumping to the first image slot + ***** Booting Zephyr OS build v4.2.0 ***** + Hello World! max32658evkit/max32658/ns + + +Debugging +========= + +Here is an example for the :zephyr:code-sample:`hello_world` application. This example uses the +:ref:`jlink-debug-host-tools` as default. + +.. zephyr-app-commands:: + :zephyr-app: samples/hello_world + :board: max32658evkit/max32658 + :goals: debug + +Open a serial terminal, step through the application in your debugger, and you +should see the following message in the terminal: + +.. code-block:: console + + ***** Booting Zephyr OS build v4.2.0 ***** + Hello World! max32658evkit/max32658 + +References +********** + +.. _1: + https://developer.arm.com/documentation/100935/0100/The-TrustZone-hardware-architecture- + +.. _Trusted Firmware M: + https://tf-m-user-guide.trustedfirmware.org/building/tfm_build_instruction.html diff --git a/boards/adi/max32658evkit/max32658evkit_max32658.dts b/boards/adi/max32658evkit/max32658evkit_max32658.dts new file mode 100644 index 000000000000..20b52890f76b --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658.dts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024-2025 Analog Devices, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include "max32658evkit_max32658_common.dtsi" + +/ { + chosen { + zephyr,sram = &secure_ram; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_partition; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + secure_ram: partition@30000000 { + label = "secure-memory"; + reg = <0x30000000 DT_SIZE_K(256)>; + }; + }; +}; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot0_partition: partition@0 { + label = "image-0"; + reg = <0x0 DT_SIZE_K(960)>; + read-only; + }; + + storage_partition: partition@f0000 { + label = "storage"; + reg = <0xf0000 DT_SIZE_K(64)>; + }; + }; +}; + +&trng { + status = "okay"; +}; diff --git a/boards/adi/max32658evkit/max32658evkit_max32658.yaml b/boards/adi/max32658evkit/max32658evkit_max32658.yaml new file mode 100644 index 000000000000..f4e7fdac7f8f --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658.yaml @@ -0,0 +1,21 @@ +identifier: max32658evkit/max32658 +name: max32658evkit-max32658 +vendor: adi +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb +supported: + - serial + - gpio + - trng + - watchdog + - dma + - counter + - pwm + - rtc_counter + - spi + - i3c +ram: 256 +flash: 960 diff --git a/boards/adi/max32658evkit/max32658evkit_max32658_common.dtsi b/boards/adi/max32658evkit/max32658evkit_max32658_common.dtsi new file mode 100644 index 000000000000..9b65c7bffab6 --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658_common.dtsi @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024-2025 Analog Devices, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/ { + model = "Analog Devices MAX32658EVKIT"; + compatible = "adi,max32658evkit"; + + chosen { + zephyr,console = &uart0; + zephyr,cortex-m-idle-timer = &counter_wut1; + zephyr,shell-uart = &uart0; + }; + + leds { + compatible = "gpio-leds"; + + led1: led_1 { + gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + label = "Green LED"; + }; + }; + + buttons { + compatible = "gpio-keys"; + + pb1: pb1 { + gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "SW2"; + zephyr,code = ; + }; + }; + + /* These aliases are provided for compatibility with samples */ + aliases { + accel0 = &adxl367; + led0 = &led1; + sw0 = &pb1; + watchdog0 = &wdt0; + }; +}; + +&uart0 { + pinctrl-0 = <&uart0_tx_p0_9 &uart0_rx_p0_5>; + pinctrl-names = "default"; + current-speed = <115200>; + data-bits = <8>; + parity = "none"; + status = "okay"; +}; + +&clk_ipo { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&wdt0 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + pinctrl-0 = <&spi0_mosi_p0_2 &spi0_miso_p0_4 &spi0_sck_p0_6 &spi0_ss0_p0_3>; + pinctrl-names = "default"; +}; + +&rtc_counter { + status = "okay"; + clock-source = ; +}; + +&i3c0 { + status = "okay"; + pinctrl-0 = <&i3c_scl_p0_0 &i3c_sda_p0_1>; + pinctrl-names = "default"; + i2c-scl-hz = ; + i3c-scl-hz = ; + i3c-od-scl-hz = ; + + adxl367: adxl367@530000000000000000 { + compatible = "adi,adxl367"; + reg = <0x53 0x00 0x00>; + status = "okay"; + }; +}; + +&wut0 { + clock-source = ; +}; + +&wut1 { + status = "okay"; + clock-source = ; + wakeup-source; + counter_wut1: counter { + status = "okay"; + }; +}; diff --git a/boards/adi/max32658evkit/max32658evkit_max32658_defconfig b/boards/adi/max32658evkit/max32658evkit_max32658_defconfig new file mode 100644 index 000000000000..25ef03ee5131 --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658_defconfig @@ -0,0 +1,16 @@ +# Copyright (c) 2024-2025 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +# Enable GPIO +CONFIG_GPIO=y + +# Console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable UART +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# It is secure fw, enable flags +CONFIG_TRUSTED_EXECUTION_SECURE=y diff --git a/boards/adi/max32658evkit/max32658evkit_max32658_ns.dts b/boards/adi/max32658evkit/max32658evkit_max32658_ns.dts new file mode 100644 index 000000000000..0cc9f0880480 --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658_ns.dts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024-2025 Analog Devices, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include "max32658evkit_max32658_common.dtsi" + +/ { + chosen { + zephyr,sram = &non_secure_ram; + zephyr,flash = &flash0; + zephyr,code-partition = &slot0_ns_partition; + }; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + /* RAM split used by TFM */ + secure_ram: partition@20000000 { + label = "secure-memory"; + reg = <0x20000000 DT_SIZE_K(64)>; + }; + + non_secure_ram: partition@20010000 { + label = "non-secure-memory"; + reg = <0x20010000 DT_SIZE_K(192)>; + }; + }; +}; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x0 DT_SIZE_K(64)>; + read-only; + }; + + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x10000 DT_SIZE_K(320)>; + }; + + slot0_ns_partition: partition@60000 { + label = "image-0-nonsecure"; + reg = <0x60000 DT_SIZE_K(576)>; + }; + + /* + * slot1_partition: partition@f0000 { + * label = "image-1"; + * reg = <0xf0000 DT_SIZE_K(0)>; + * }; + * slot1_ns_partition: partition@f0000 { + * label = "image-1-nonsecure"; + * reg = <0xf0000 DT_SIZE_K(0)>; + * }; + */ + + storage_partition: partition@f0000 { + label = "storage"; + reg = <0xf0000 DT_SIZE_K(64)>; + }; + }; +}; diff --git a/boards/adi/max32658evkit/max32658evkit_max32658_ns.yaml b/boards/adi/max32658evkit/max32658evkit_max32658_ns.yaml new file mode 100644 index 000000000000..9868c5bb8a71 --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658_ns.yaml @@ -0,0 +1,20 @@ +identifier: max32658evkit/max32658/ns +name: max32658evkit-max32658-Non-Secure +vendor: adi +type: mcu +arch: arm +toolchain: + - zephyr + - gnuarmemb +supported: + - serial + - gpio + - watchdog + - dma + - counter + - pwm + - rtc_counter + - spi + - i3c +ram: 192 +flash: 576 diff --git a/boards/adi/max32658evkit/max32658evkit_max32658_ns_defconfig b/boards/adi/max32658evkit/max32658evkit_max32658_ns_defconfig new file mode 100644 index 000000000000..d808f79c5459 --- /dev/null +++ b/boards/adi/max32658evkit/max32658evkit_max32658_ns_defconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2024-2025 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +# Enable GPIO +CONFIG_GPIO=y + +# Console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable UART +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# It is non-secure fw, enable flags +CONFIG_TRUSTED_EXECUTION_NONSECURE=y + +# Set TFM and Zephyr sign key +CONFIG_TFM_MCUBOOT_SIGNATURE_TYPE="RSA-3072" diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index a8d51666591e..4b730b4a342d 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -251,7 +251,7 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DTFM_PLATFORM_NXP_HAL_FILE_PATH=${TFM_PLATFORM_NXP_HAL_FILE_PATH}) endif() - if(CONFIG_BOARD_MAX32657EVKIT_MAX32657_NS) + if(CONFIG_BOARD_MAX32657EVKIT_MAX32657_NS OR CONFIG_BOARD_MAX32658EVKIT_MAX32658_NS) # Supply path to hal_adi for TF-M build list(APPEND TFM_CMAKE_ARGS -DHAL_ADI_PATH=${ZEPHYR_ADI_MODULE_DIR}) endif() diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index 06c7221321ec..6c3ac64487f2 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -25,7 +25,7 @@ config TFM_BOARD default "stm/stm32wba65i_dk" if BOARD_NUCLEO_WBA65RI || BOARD_STM32WBA65I_DK1 default "arm/musca_b1" if BOARD_V2M_MUSCA_B1 default "arm/musca_s1" if BOARD_V2M_MUSCA_S1 - default "adi/max32657" if BOARD_MAX32657EVKIT_MAX32657_NS + default "adi/max32657" if BOARD_MAX32657EVKIT_MAX32657_NS || BOARD_MAX32658EVKIT_MAX32658_NS default "$(ZEPHYR_BASE)/modules/trusted-firmware-m/nordic/nrf9160" if SOC_NRF9160 default "$(ZEPHYR_BASE)/modules/trusted-firmware-m/nordic/nrf9120" if SOC_NRF9120 default "$(ZEPHYR_BASE)/modules/trusted-firmware-m/nordic/nrf5340_cpuapp" if SOC_NRF5340_CPUAPP From 1814763b12eb32f590d390c0bd6bd80394bdc75c Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Wed, 22 Oct 2025 11:24:37 +0100 Subject: [PATCH 07/36] [nrf fromtree] modules: tf-m: Remove some download Kconfigs Removes two Kconfig which seemed to indicate downloading of a project would happen automatically, which does not abide by how to get additional module code in Zephyr. Due to TF-M always setting these to "DOWNLOAD" in the repo, they are set even if the modules do not exist so that they do not download e.g. in CI. Unfortunately it seems that the qcbor one cannot be removed at this time due to being needed in some applications and is not apache licensed, though instructions should be provided to users instead describing how to add it to a module manifest instead, in a later task Signed-off-by: Jamie McCrae (cherry picked from commit 298a35b7140e36c9404feba2c13873d5cffa933f) --- modules/trusted-firmware-m/CMakeLists.txt | 11 +++---- modules/trusted-firmware-m/Kconfig.tfm | 37 ----------------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 4b730b4a342d..33c11d7145fd 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -67,6 +67,8 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DMCUBOOT_KEY_${SUFFIX}=${CONFIG_TFM_KEY_FILE_${SUFFIX}}) endforeach() + # Supply path to MCUboot for TF-M build + list(APPEND TFM_CMAKE_ARGS -DMCUBOOT_PATH=${ZEPHYR_MCUBOOT_MODULE_DIR}) else() list(APPEND TFM_CMAKE_ARGS -DBL2=FALSE) endif() @@ -256,11 +258,6 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DHAL_ADI_PATH=${ZEPHYR_ADI_MODULE_DIR}) endif() - if(CONFIG_TFM_BL2 AND CONFIG_TFM_MCUBOOT_PATH_LOCAL) - # Supply path to MCUboot for TF-M build - list(APPEND TFM_CMAKE_ARGS -DMCUBOOT_PATH=${ZEPHYR_MCUBOOT_MODULE_DIR}) - endif() - if(CONFIG_TFM_MCUBOOT_DATA_SHARING) list(APPEND TFM_CMAKE_ARGS -DMCUBOOT_DATA_SHARING=ON) endif() @@ -277,8 +274,8 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DTFM_TESTS_REVISION_CHECKS=OFF) - if(CONFIG_TFM_ETHOS_DRIVER_PATH_LOCAL) - list(APPEND TFM_CMAKE_ARGS -DETHOS_DRIVER_PATH=${CONFIG_TFM_ETHOS_DRIVER_PATH_LOCAL}) + if(CONFIG_SOC_SERIES_MPS3 OR CONFIG_SOC_SERIES_MPS4) + list(APPEND TFM_CMAKE_ARGS -DETHOS_DRIVER_PATH=${ZEPHYR_HAL_ETHOS_U_MODULE_DIR}) endif() if(CONFIG_TFM_STM32_FLASH_LAYOUT_BEGIN_OFFSET) diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index 6c3ac64487f2..b8bc3831848b 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -310,43 +310,6 @@ config TFM_MCUBOOT_IMAGE_NUMBER updated in one atomic operation. When this is 2, they are split and can be updated independently if dependency requirements are met. -choice TFM_MCUBOOT_PATH - prompt "Path to MCUboot or DOWNLOAD to fetch automatically" - default TFM_MCUBOOT_PATH_LOCAL - help - Path to MCUboot for TF-M builds. The default option - is to use Zephyr's MCUboot module. As an alternative, - users may switch to the 'download' version; in that - case MCUboot will be fetched by the TF-M build during - build time. The default option ensures that Zephyr builds - with TF-M do not fetch external trees. - -config TFM_MCUBOOT_PATH_LOCAL - bool "TF-M to use Zephyr's MCUboot" - help - TF-M builds with BL2 will use the Zephyr's MCUboot version, - which is present in the MCUboot module. - -config TFM_MCUBOOT_PATH_DOWNLOAD - bool "TF-M to automatically download MCUboot during build" - help - TF-M builds with BL2 will let the TF-M build to automatically - fetch and check-out the MCUboot version to use in the build. - -endchoice - -config TFM_ETHOS_DRIVER_PATH_LOCAL - string "Path to a locally available Ethos-U driver or an empty string" - depends on SOC_SERIES_MPS3 || SOC_SERIES_MPS4 - default "$(ZEPHYR_HAL_ETHOS_U_MODULE_DIR)" - help - Path to a locally available Ethos-U driver to be used for TF-M builds or - an empty string to allow TF-M to automatically fetch the Ethos-U - driver from an external repository at build time. - By default Zephyr's Ethos-U driver will be used. It is present in - the hal_ethos_u module. - Alternatively, applications can point to their own paths for Ethos-U driver. - config TFM_QCBOR_PATH string prompt "Path to QCBOR or DOWNLOAD to fetch automatically" From 2526c782cd4607a5399ef9a8300baaff4670fbc0 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 18 Aug 2025 20:05:37 +0200 Subject: [PATCH 08/36] [nrf fromtree] modules: tf-m: Kconfig.tfm: Update TFM_BOARD Reorder TFM_BOARD entries by vendor name for improved clarity. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 2697953876383ebbad8f3cd2a5ce4ddd90fd4b3f) --- modules/trusted-firmware-m/Kconfig.tfm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index b8bc3831848b..f587ea01e45f 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -10,7 +10,7 @@ config ZEPHYR_TRUSTED_FIRMWARE_M_MODULE config TFM_BOARD string - default "nxp/lpcxpresso55s69" if BOARD_LPCXPRESSO55S69_LPC55S69_CPU0_NS + default "adi/max32657" if BOARD_MAX32657EVKIT_MAX32657_NS || BOARD_MAX32658EVKIT_MAX32658_NS default "arm/mps2/an521" if BOARD_MPS2_AN521_CPU0_NS default "arm/mps3/corstone300/fvp" if BOARD_MPS3_CORSTONE300_FVP_NS default "arm/mps3/corstone300/an547" if BOARD_MPS3_CORSTONE300_AN547_NS @@ -19,13 +19,13 @@ config TFM_BOARD default "arm/mps3/corstone310/fvp" if BOARD_MPS3_CORSTONE310_FVP_NS default "arm/mps4/corstone315" if BOARD_MPS4_CORSTONE315_FVP_NS default "arm/mps4/corstone320" if BOARD_MPS4_CORSTONE320_FVP_NS + default "arm/musca_b1" if BOARD_V2M_MUSCA_B1 + default "arm/musca_s1" if BOARD_V2M_MUSCA_S1 + default "nxp/lpcxpresso55s69" if BOARD_LPCXPRESSO55S69_LPC55S69_CPU0_NS default "stm/b_u585i_iot02a" if BOARD_B_U585I_IOT02A default "stm/nucleo_l552ze_q" if BOARD_NUCLEO_L552ZE_Q default "stm/stm32l562e_dk" if BOARD_STM32L562E_DK default "stm/stm32wba65i_dk" if BOARD_NUCLEO_WBA65RI || BOARD_STM32WBA65I_DK1 - default "arm/musca_b1" if BOARD_V2M_MUSCA_B1 - default "arm/musca_s1" if BOARD_V2M_MUSCA_S1 - default "adi/max32657" if BOARD_MAX32657EVKIT_MAX32657_NS || BOARD_MAX32658EVKIT_MAX32658_NS default "$(ZEPHYR_BASE)/modules/trusted-firmware-m/nordic/nrf9160" if SOC_NRF9160 default "$(ZEPHYR_BASE)/modules/trusted-firmware-m/nordic/nrf9120" if SOC_NRF9120 default "$(ZEPHYR_BASE)/modules/trusted-firmware-m/nordic/nrf5340_cpuapp" if SOC_NRF5340_CPUAPP From a3d9f960433d3dff614c54081ff85dfe04cfba14 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 21 Jul 2025 13:36:48 +0200 Subject: [PATCH 09/36] [nrf fromtree] trusted-firmware-m: Set --align when signing The current version of TF-M script that sign MCUboot image uses a default alignment of 1. This value varies between flash devices and not all accept the default 1. This improve the script picking the write-block-size property from the current flash controller and pass as the --align parameter when signing an image. Note: This solution works out-of-box for the vast majority of devices in the Zephyr tree and an exception will throw when a device is not supported. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit debb59830b7e8eb7f8a6470b29bbdcc9f291695a) --- modules/trusted-firmware-m/CMakeLists.txt | 35 ++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 33c11d7145fd..b958f94567fa 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -451,6 +451,39 @@ if (CONFIG_BUILD_WITH_TFM) set(HEX_ADDR_ARGS_NS "--hex-addr=${TFM_HEX_BASE_ADDRESS_NS}") endif() + if(CONFIG_TFM_BL2) + set(image_alignment 1) + set(flash_write_block_size 1) + + dt_chosen(chosen_flash PROPERTY "zephyr,flash") + if(DEFINED chosen_flash AND chosen_flash) + dt_prop(flash_write_block_size PATH ${chosen_flash} PROPERTY write-block-size) + else() + message(WARNING + "The 'zephyr,flash' chosen property is not defined! + Using flash_write_block_size default value possible differs from + TF-M board definitions resulting in improver sign." + ) + endif() + + # The alignment is determined by the minimal amount of bytes necessary to + # be written in the flash sector. Ex., assuming that the sector erase + # operation is 1KiB and, on that sector, the minimum amount of bytes that + # must be written is 8 bytes then the alignment is 8. + # + # Current MCUboot maximum alignment is 32 bytes. + if(flash_write_block_size GREATER 0) + if(flash_write_block_size GREATER 32) + message(WARNING + "imgtool max alignment is 32 and current value is ${flash_write_block_size}. + Keep default image alignment of 1." + ) + else() + set(image_alignment ${flash_write_block_size}) + endif() + endif() + endif() + function(tfm_sign OUT_ARG SUFFIX PAD INPUT_FILE OUTPUT_FILE) if(PAD) set(pad_args --pad --pad-header) @@ -470,7 +503,7 @@ if (CONFIG_BUILD_WITH_TFM) --layout ${layout_file} -k ${CONFIG_TFM_KEY_FILE_${SUFFIX}} --public-key-format ${TFM_PUBLIC_KEY_FORMAT} - --align 1 + --align ${image_alignment} -v ${CONFIG_TFM_IMAGE_VERSION_${SUFFIX}} ${pad_args} ${HEX_ADDR_ARGS_${SUFFIX}} From 689cd65a2b8c56598abaee5414ffd11c830431dc Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 21 Jul 2025 15:58:47 +0200 Subject: [PATCH 10/36] [nrf fromtree] trusted-firmware-m: Set --max-sectors when signing The --max-sectors option helps catch problems with flash overlap when merging images. If there is a misalignment in flash partitions, the merge process usually fails. This uses information from Zephyr flash partitions and the flash controller to automatically determine the max sectors value and apply it when signing an image. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 99a2e4931e574cfd8b0ee65b83756b6b09be59f8) --- modules/trusted-firmware-m/CMakeLists.txt | 35 ++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index b958f94567fa..a6d645afb4c8 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -454,15 +454,17 @@ if (CONFIG_BUILD_WITH_TFM) if(CONFIG_TFM_BL2) set(image_alignment 1) set(flash_write_block_size 1) + set(flash_erase_block_size 1) dt_chosen(chosen_flash PROPERTY "zephyr,flash") if(DEFINED chosen_flash AND chosen_flash) dt_prop(flash_write_block_size PATH ${chosen_flash} PROPERTY write-block-size) + dt_prop(flash_erase_block_size PATH ${chosen_flash} PROPERTY erase-block-size) else() message(WARNING "The 'zephyr,flash' chosen property is not defined! - Using flash_write_block_size default value possible differs from - TF-M board definitions resulting in improver sign." + Using flash_write_block_size and flash_erase_block_size default values + that may differ from TF-M board definitions resulting in invalid signatures." ) endif() @@ -482,9 +484,26 @@ if (CONFIG_BUILD_WITH_TFM) set(image_alignment ${flash_write_block_size}) endif() endif() + + # Calculate the maximum number of sectors necessary to store the image. + dt_nodelabel(s_partition_node NODELABEL "slot0_partition" REQUIRED) + dt_nodelabel(ns_partition_node NODELABEL "slot0_ns_partition" REQUIRED) + dt_reg_size(s_partition_size PATH ${s_partition_node}) + dt_reg_size(ns_partition_size PATH ${ns_partition_node}) + math(EXPR S_MAX_SECTORS "${s_partition_size} / ${flash_erase_block_size}") + math(EXPR NS_MAX_SECTORS "${ns_partition_size} / ${flash_erase_block_size}") + if(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") + math(EXPR S_NS_MAX_SECTORS "${S_MAX_SECTORS} + ${NS_MAX_SECTORS}") + else() + if(${S_MAX_SECTORS} GREATER ${NS_MAX_SECTORS}) + set(S_NS_MAX_SECTORS ${S_MAX_SECTORS}) + else() + set(S_NS_MAX_SECTORS ${NS_MAX_SECTORS}) + endif() + endif() endif() - function(tfm_sign OUT_ARG SUFFIX PAD INPUT_FILE OUTPUT_FILE) + function(tfm_sign OUT_ARG SUFFIX PAD MAX_SECTORS INPUT_FILE OUTPUT_FILE) if(PAD) set(pad_args --pad --pad-header) endif() @@ -504,6 +523,7 @@ if (CONFIG_BUILD_WITH_TFM) -k ${CONFIG_TFM_KEY_FILE_${SUFFIX}} --public-key-format ${TFM_PUBLIC_KEY_FORMAT} --align ${image_alignment} + --max-sectors ${MAX_SECTORS} -v ${CONFIG_TFM_IMAGE_VERSION_${SUFFIX}} ${pad_args} ${HEX_ADDR_ARGS_${SUFFIX}} @@ -544,7 +564,7 @@ if (CONFIG_BUILD_WITH_TFM) ) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") - tfm_sign(sign_cmd S_NS TRUE ${S_NS_FILE} ${S_NS_SIGNED_FILE}) + tfm_sign(sign_cmd S_NS TRUE ${S_NS_MAX_SECTORS} ${S_NS_FILE} ${S_NS_SIGNED_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py @@ -569,12 +589,13 @@ if (CONFIG_BUILD_WITH_TFM) else() if (CONFIG_TFM_USE_NS_APP) - tfm_sign(sign_cmd_ns NS TRUE ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) else() - tfm_sign(sign_cmd_ns NS FALSE ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS FALSE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) endif() - tfm_sign(sign_cmd_s S TRUE $ ${S_SIGNED_FILE}) + tfm_sign(sign_cmd_s S TRUE ${S_NS_MAX_SECTORS} $ + ${S_SIGNED_FILE}) #Create and sign for concatenated binary image, should align with the TF-M BL2 set_property(GLOBAL APPEND PROPERTY extra_post_build_commands From 0a415d60b7f54317e66a79877e8b787c4258bc40 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 21 Jul 2025 16:18:31 +0200 Subject: [PATCH 11/36] [nrf fromtree] trusted-firmware-m: Define header and trailer options The current behavior when signing an image is to always set --pad and --pad-header for all images unless TFM_USE_NS_APP is set. This does not allow for easy creation of signed images for FOTA applications. Rewrite the PAD parameter as HEADER and TRAILER to simplify the setup of more signing options. Another important reason for this change is that the NS image, when signed without --pad, runs on the hardware but does not perform the MCUboot test, and the FWU never upgrades the image. This fixes the NS image signing process to correctly support TF-M FWU using the PSA API functions. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit b21ea79784d7f02dea8fec6ab33d51a61dd70a1f) --- modules/trusted-firmware-m/CMakeLists.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index a6d645afb4c8..93a04c0db9e2 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -503,9 +503,13 @@ if (CONFIG_BUILD_WITH_TFM) endif() endif() - function(tfm_sign OUT_ARG SUFFIX PAD MAX_SECTORS INPUT_FILE OUTPUT_FILE) - if(PAD) + function(tfm_sign OUT_ARG SUFFIX HEADER TRAILER MAX_SECTORS INPUT_FILE OUTPUT_FILE) + if(HEADER AND TRAILER) set(pad_args --pad --pad-header) + elseif(HEADER) + set(pad_args --pad-header) + elseif(TRAILER) + set(pad_args --pad) endif() # Secure + Non-secure images are signed the same way as a secure only # build, but with a different layout file. @@ -564,7 +568,7 @@ if (CONFIG_BUILD_WITH_TFM) ) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") - tfm_sign(sign_cmd S_NS TRUE ${S_NS_MAX_SECTORS} ${S_NS_FILE} ${S_NS_SIGNED_FILE}) + tfm_sign(sign_cmd S_NS TRUE TRUE ${S_NS_MAX_SECTORS} ${S_NS_FILE} ${S_NS_SIGNED_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py @@ -589,12 +593,12 @@ if (CONFIG_BUILD_WITH_TFM) else() if (CONFIG_TFM_USE_NS_APP) - tfm_sign(sign_cmd_ns NS TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) else() - tfm_sign(sign_cmd_ns NS FALSE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS FALSE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) endif() - tfm_sign(sign_cmd_s S TRUE ${S_NS_MAX_SECTORS} $ + tfm_sign(sign_cmd_s S TRUE TRUE ${S_NS_MAX_SECTORS} $ ${S_SIGNED_FILE}) #Create and sign for concatenated binary image, should align with the TF-M BL2 From 75a4fc5c106ede0d9bf1221b27cb820c02f58a49 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 21 Jul 2025 16:25:23 +0200 Subject: [PATCH 12/36] [nrf fromtree] trusted-firmware-m: Set --confirm when signing The current behavior when signing an image adds --pad but does not confirm the image. This appears to be a mistake, as the user should inspect the image status in the Firmware Upgrade software. If an image is not --confirmed, the FSM cannot infer the correct states. This sets the image as confirmed to resolve the issue. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 6cee10c5b92639d18e35a7ff27e47a460c25cfc0) --- modules/trusted-firmware-m/CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 93a04c0db9e2..a738a49835b7 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -503,7 +503,7 @@ if (CONFIG_BUILD_WITH_TFM) endif() endif() - function(tfm_sign OUT_ARG SUFFIX HEADER TRAILER MAX_SECTORS INPUT_FILE OUTPUT_FILE) + function(tfm_sign OUT_ARG SUFFIX HEADER TRAILER CONFIRM MAX_SECTORS INPUT_FILE OUTPUT_FILE) if(HEADER AND TRAILER) set(pad_args --pad --pad-header) elseif(HEADER) @@ -511,6 +511,10 @@ if (CONFIG_BUILD_WITH_TFM) elseif(TRAILER) set(pad_args --pad) endif() + if(CONFIRM) + # --confirm imply PAD + set(confirm --confirm) + endif() # Secure + Non-secure images are signed the same way as a secure only # build, but with a different layout file. set(layout_file ${PREPROCESSED_FILE_${SUFFIX}}) @@ -530,6 +534,7 @@ if (CONFIG_BUILD_WITH_TFM) --max-sectors ${MAX_SECTORS} -v ${CONFIG_TFM_IMAGE_VERSION_${SUFFIX}} ${pad_args} + ${confirm} ${HEX_ADDR_ARGS_${SUFFIX}} ${ADD_${SUFFIX}_IMAGE_MIN_VER} -s ${CONFIG_TFM_IMAGE_SECURITY_COUNTER} @@ -568,7 +573,7 @@ if (CONFIG_BUILD_WITH_TFM) ) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") - tfm_sign(sign_cmd S_NS TRUE TRUE ${S_NS_MAX_SECTORS} ${S_NS_FILE} ${S_NS_SIGNED_FILE}) + tfm_sign(sign_cmd S_NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${S_NS_FILE} ${S_NS_SIGNED_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py @@ -593,12 +598,12 @@ if (CONFIG_BUILD_WITH_TFM) else() if (CONFIG_TFM_USE_NS_APP) - tfm_sign(sign_cmd_ns NS TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) else() - tfm_sign(sign_cmd_ns NS FALSE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS FALSE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) endif() - tfm_sign(sign_cmd_s S TRUE TRUE ${S_NS_MAX_SECTORS} $ + tfm_sign(sign_cmd_s S TRUE TRUE TRUE ${S_NS_MAX_SECTORS} $ ${S_SIGNED_FILE}) #Create and sign for concatenated binary image, should align with the TF-M BL2 From a865c921b01d404c227b35d1a53f4c7edefab8f0 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 21 Jul 2025 16:31:53 +0200 Subject: [PATCH 13/36] [nrf fromtree] trusted-firmware-m: Make hex files variables explicit Make variables that define output files explicitly include 'HEX' in the name. This refactoring step allows for the introduction of BIN file generation. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 69f277cdcb02dbb1ebd2aeb8058dbaae2f4b477a) --- modules/trusted-firmware-m/CMakeLists.txt | 65 ++++++++++++----------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index a738a49835b7..b331b599743b 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -545,84 +545,87 @@ if (CONFIG_BUILD_WITH_TFM) PARENT_SCOPE) endfunction() - set(MERGED_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.hex) - set(S_NS_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns.hex) - set(S_NS_SIGNED_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) - set(NS_SIGNED_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.hex) - set(S_SIGNED_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.hex) + set(MERGED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.hex) + set(S_NS_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns.hex) + set(S_NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) + set(NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.hex) + set(S_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.hex) if (CONFIG_TFM_USE_NS_APP) # Use the TF-M NS binary as the Non-Secure application firmware image - set(NS_APP_FILE $) + set(NS_HEX_APP_FILE $) else() # Use the Zephyr binary as the Non-Secure application firmware image - set(NS_APP_FILE ${CMAKE_BINARY_DIR}/zephyr/${KERNEL_HEX_NAME}) + set(NS_HEX_APP_FILE ${CMAKE_BINARY_DIR}/zephyr/${KERNEL_HEX_NAME}) endif() if (NOT CONFIG_TFM_BL2) # Merge tfm_s and zephyr (NS) image to a single binary. set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${MERGED_FILE} + -o ${MERGED_HEX_FILE} $ - ${NS_APP_FILE} + ${NS_HEX_APP_FILE} ) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts - ${MERGED_FILE} + ${MERGED_HEX_FILE} ) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") - tfm_sign(sign_cmd S_NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${S_NS_FILE} ${S_NS_SIGNED_FILE}) + tfm_sign(sign_cmd_s_ns_hex S_NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${S_NS_HEX_FILE} + ${S_NS_SIGNED_HEX_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${S_NS_FILE} + -o ${S_NS_HEX_FILE} $ - ${NS_APP_FILE} + ${NS_HEX_APP_FILE} - COMMAND ${sign_cmd} + COMMAND ${sign_cmd_s_ns_hex} COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${MERGED_FILE} + -o ${MERGED_HEX_FILE} $<$:$> $<$>:$> - ${S_NS_SIGNED_FILE} + ${S_NS_SIGNED_HEX_FILE} ) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts - ${S_NS_FILE} - ${S_NS_SIGNED_FILE} - ${MERGED_FILE} + ${S_NS_HEX_FILE} + ${S_NS_SIGNED_HEX_FILE} + ${MERGED_HEX_FILE} ) else() if (CONFIG_TFM_USE_NS_APP) - tfm_sign(sign_cmd_ns NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns_hex NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_HEX_APP_FILE} + ${NS_SIGNED_HEX_FILE}) else() - tfm_sign(sign_cmd_ns NS FALSE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_APP_FILE} ${NS_SIGNED_FILE}) + tfm_sign(sign_cmd_ns NS FALSE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_HEX_APP_FILE} + ${NS_SIGNED_HEX_FILE}) endif() - tfm_sign(sign_cmd_s S TRUE TRUE TRUE ${S_NS_MAX_SECTORS} $ - ${S_SIGNED_FILE}) + tfm_sign(sign_cmd_s_hex S TRUE TRUE TRUE ${S_NS_MAX_SECTORS} + $ ${S_SIGNED_HEX_FILE}) #Create and sign for concatenated binary image, should align with the TF-M BL2 set_property(GLOBAL APPEND PROPERTY extra_post_build_commands - COMMAND ${sign_cmd_ns} - COMMAND ${sign_cmd_s} + COMMAND ${sign_cmd_ns_hex} + COMMAND ${sign_cmd_s_hex} COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${MERGED_FILE} + -o ${MERGED_HEX_FILE} $<$:$> $<$>:$> - ${S_SIGNED_FILE} - ${NS_SIGNED_FILE} + ${S_SIGNED_HEX_FILE} + ${NS_SIGNED_HEX_FILE} ) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts - ${S_SIGNED_FILE} - ${NS_SIGNED_FILE} - ${MERGED_FILE} + ${S_SIGNED_HEX_FILE} + ${NS_SIGNED_HEX_FILE} + ${MERGED_HEX_FILE} ) endif() From c7fdf4a6a122a7f3b8e438177e119729cc90c9e5 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Mon, 21 Jul 2025 18:02:25 +0200 Subject: [PATCH 14/36] [nrf fromtree] trusted-firmware-m: Create multi image bin files A fundamental use of Trusted Firmware-M is to provide security for IoT applications, where firmware upgrades (FOTA) are almost always mandatory. The current file signing process does not produce the necessary binaries for multi-image S/NS FWU, since hex images are not suitable for this use case. This introduces the missing signed binary files for use by the FWU partition. The changes were tested in multi-image FWU scenarios, and support for single-image scenarios can be easily added in the future. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 5b4cd27f553438f89014e29d4e6cf30b47c33cfe) --- modules/trusted-firmware-m/CMakeLists.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index b331b599743b..bad05a439730 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -185,7 +185,7 @@ if (CONFIG_BUILD_WITH_TFM) set(TFM_S_ELF_FILE ${TFM_BINARY_DIR}/bin/tfm_s.elf) set(TFM_S_BIN_FILE ${TFM_BINARY_DIR}/bin/tfm_s.bin) set(TFM_S_HEX_FILE ${TFM_BINARY_DIR}/bin/tfm_s.hex) - set(TFM_NS_BIN_FILE ${TFM_BINARY_DIR}/bin/tfm_ns.bin) + set(TFM_NS_BIN_FILE ${CMAKE_BINARY_DIR}/tfm_ns/bin/tfm_ns.bin) set(TFM_NS_HEX_FILE ${CMAKE_BINARY_DIR}/tfm_ns/bin/tfm_ns.hex) set(TFM_S_SIGNED_BIN_FILE ${TFM_BINARY_DIR}/bin/tfm_s_signed.bin) set(TFM_NS_SIGNED_BIN_FILE ${TFM_BINARY_DIR}/bin/tfm_ns_signed.bin) @@ -550,13 +550,17 @@ if (CONFIG_BUILD_WITH_TFM) set(S_NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) set(NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.hex) set(S_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.hex) + set(NS_SIGNED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.bin) + set(S_SIGNED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.bin) if (CONFIG_TFM_USE_NS_APP) # Use the TF-M NS binary as the Non-Secure application firmware image set(NS_HEX_APP_FILE $) + set(NS_BIN_APP_FILE $) else() # Use the Zephyr binary as the Non-Secure application firmware image set(NS_HEX_APP_FILE ${CMAKE_BINARY_DIR}/zephyr/${KERNEL_HEX_NAME}) + set(NS_BIN_APP_FILE ${CMAKE_BINARY_DIR}/zephyr/${KERNEL_BIN_NAME}) endif() if (NOT CONFIG_TFM_BL2) @@ -601,18 +605,26 @@ if (CONFIG_BUILD_WITH_TFM) if (CONFIG_TFM_USE_NS_APP) tfm_sign(sign_cmd_ns_hex NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_HEX_APP_FILE} ${NS_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_ns_bin NS TRUE TRUE FALSE ${S_NS_MAX_SECTORS} ${NS_BIN_APP_FILE} + ${NS_SIGNED_BIN_FILE}) else() tfm_sign(sign_cmd_ns NS FALSE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_HEX_APP_FILE} ${NS_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_ns_bin NS FALSE FALSE FALSE ${S_NS_MAX_SECTORS} ${NS_BIN_APP_FILE} + ${NS_SIGNED_BIN_FILE}) endif() tfm_sign(sign_cmd_s_hex S TRUE TRUE TRUE ${S_NS_MAX_SECTORS} $ ${S_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_s_bin S TRUE TRUE FALSE ${S_NS_MAX_SECTORS} + $ ${S_SIGNED_BIN_FILE}) #Create and sign for concatenated binary image, should align with the TF-M BL2 set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${sign_cmd_ns_hex} + COMMAND ${sign_cmd_ns_bin} COMMAND ${sign_cmd_s_hex} + COMMAND ${sign_cmd_s_bin} COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py -o ${MERGED_HEX_FILE} @@ -624,7 +636,9 @@ if (CONFIG_BUILD_WITH_TFM) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${S_SIGNED_HEX_FILE} + ${S_SIGNED_BIN_FILE} ${NS_SIGNED_HEX_FILE} + ${NS_SIGNED_BIN_FILE} ${MERGED_HEX_FILE} ) endif() From 02b838794eec0460b38c3a072b781e9c435fcf6f Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Tue, 19 Aug 2025 11:42:16 +0200 Subject: [PATCH 15/36] [nrf fromtree] trusted-firmware-m: Use cmake_parse_arguments in tfm_sign Use cmake_parse_arguments() for more idiomatic code. This makes the code more readable and easier to extend with new options. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 7fe5574fa22f10edd28006288742fef2e5405a1d) --- modules/trusted-firmware-m/CMakeLists.txt | 97 +++++++++++++++-------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index bad05a439730..df8843f48749 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -503,45 +503,67 @@ if (CONFIG_BUILD_WITH_TFM) endif() endif() - function(tfm_sign OUT_ARG SUFFIX HEADER TRAILER CONFIRM MAX_SECTORS INPUT_FILE OUTPUT_FILE) - if(HEADER AND TRAILER) + function(tfm_sign OUT_ARG) + set(options HEADER TRAILER CONFIRM) + set(oneValueArgs SUFFIX MAX_SECTORS INPUT_FILE OUTPUT_FILE) + set(multiValueArgs "") + + cmake_parse_arguments( + TFM_SIGN_ARG + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + + if(NOT DEFINED TFM_SIGN_ARG_SUFFIX OR + NOT DEFINED TFM_SIGN_ARG_INPUT_FILE OR + NOT DEFINED TFM_SIGN_ARG_OUTPUT_FILE) + message(FATAL_ERROR "SUFFIX, INPUT_FILE and OUTPUT_FILE are required arguments") + endif() + + set(pad_args "") + if(TFM_SIGN_ARG_HEADER AND TFM_SIGN_ARG_TRAILER) set(pad_args --pad --pad-header) - elseif(HEADER) + elseif(TFM_SIGN_ARG_HEADER) set(pad_args --pad-header) - elseif(TRAILER) + elseif(TFM_SIGN_ARG_TRAILER) set(pad_args --pad) endif() - if(CONFIRM) - # --confirm imply PAD + + set(confirm "") + if(TFM_SIGN_ARG_CONFIRM) set(confirm --confirm) endif() + # Secure + Non-secure images are signed the same way as a secure only # build, but with a different layout file. - set(layout_file ${PREPROCESSED_FILE_${SUFFIX}}) - if(SUFFIX STREQUAL "S_NS") - set(SUFFIX "S") + set(layout_file ${PREPROCESSED_FILE_${TFM_SIGN_ARG_SUFFIX}}) + if(TFM_SIGN_ARG_SUFFIX STREQUAL "S_NS") + set(TFM_SIGN_ARG_SUFFIX "S") endif() - set (${OUT_ARG} + + set(${OUT_ARG} # Add the MCUBoot script to the path so that if there is a version of imgtool in there then # it gets used over the system imgtool. Used so that imgtool from upstream # mcuboot is preferred over system imgtool ${CMAKE_COMMAND} -E env PYTHONPATH=${ZEPHYR_MCUBOOT_MODULE_DIR}/scripts ${PYTHON_EXECUTABLE} ${TFM_MCUBOOT_DIR}/scripts/wrapper/wrapper.py --layout ${layout_file} - -k ${CONFIG_TFM_KEY_FILE_${SUFFIX}} + -k ${CONFIG_TFM_KEY_FILE_${TFM_SIGN_ARG_SUFFIX}} --public-key-format ${TFM_PUBLIC_KEY_FORMAT} --align ${image_alignment} - --max-sectors ${MAX_SECTORS} - -v ${CONFIG_TFM_IMAGE_VERSION_${SUFFIX}} + --max-sectors ${TFM_SIGN_ARG_MAX_SECTORS} + -v ${CONFIG_TFM_IMAGE_VERSION_${TFM_SIGN_ARG_SUFFIX}} ${pad_args} ${confirm} - ${HEX_ADDR_ARGS_${SUFFIX}} - ${ADD_${SUFFIX}_IMAGE_MIN_VER} + ${HEX_ADDR_ARGS_${TFM_SIGN_ARG_SUFFIX}} + ${ADD_${TFM_SIGN_ARG_SUFFIX}_IMAGE_MIN_VER} -s ${CONFIG_TFM_IMAGE_SECURITY_COUNTER} --measured-boot-record -H ${CONFIG_ROM_START_OFFSET} - ${INPUT_FILE} - ${OUTPUT_FILE} + ${TFM_SIGN_ARG_INPUT_FILE} + ${TFM_SIGN_ARG_OUTPUT_FILE} PARENT_SCOPE) endfunction() @@ -577,8 +599,9 @@ if (CONFIG_BUILD_WITH_TFM) ) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") - tfm_sign(sign_cmd_s_ns_hex S_NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${S_NS_HEX_FILE} - ${S_NS_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_s_ns_hex SUFFIX "S_NS" + HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_HEX_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py @@ -603,21 +626,33 @@ if (CONFIG_BUILD_WITH_TFM) else() if (CONFIG_TFM_USE_NS_APP) - tfm_sign(sign_cmd_ns_hex NS TRUE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_HEX_APP_FILE} - ${NS_SIGNED_HEX_FILE}) - tfm_sign(sign_cmd_ns_bin NS TRUE TRUE FALSE ${S_NS_MAX_SECTORS} ${NS_BIN_APP_FILE} - ${NS_SIGNED_BIN_FILE}) + tfm_sign(sign_cmd_ns_hex SUFFIX "NS" + HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE ${NS_HEX_APP_FILE} + OUTPUT_FILE ${NS_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_ns_bin SUFFIX "NS" + HEADER TRAILER MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE ${NS_BIN_APP_FILE} + OUTPUT_FILE ${NS_SIGNED_BIN_FILE}) else() - tfm_sign(sign_cmd_ns NS FALSE TRUE TRUE ${S_NS_MAX_SECTORS} ${NS_HEX_APP_FILE} - ${NS_SIGNED_HEX_FILE}) - tfm_sign(sign_cmd_ns_bin NS FALSE FALSE FALSE ${S_NS_MAX_SECTORS} ${NS_BIN_APP_FILE} - ${NS_SIGNED_BIN_FILE}) + tfm_sign(sign_cmd_ns_hex SUFFIX "NS" + TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE ${NS_HEX_APP_FILE} + OUTPUT_FILE ${NS_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_ns_bin SUFFIX "NS" + MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE ${NS_BIN_APP_FILE} + OUTPUT_FILE ${NS_SIGNED_BIN_FILE}) endif() - tfm_sign(sign_cmd_s_hex S TRUE TRUE TRUE ${S_NS_MAX_SECTORS} - $ ${S_SIGNED_HEX_FILE}) - tfm_sign(sign_cmd_s_bin S TRUE TRUE FALSE ${S_NS_MAX_SECTORS} - $ ${S_SIGNED_BIN_FILE}) + tfm_sign(sign_cmd_s_hex SUFFIX "S" + HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE $ + OUTPUT_FILE ${S_SIGNED_HEX_FILE}) + tfm_sign(sign_cmd_s_bin SUFFIX "S" + HEADER TRAILER MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE $ + OUTPUT_FILE ${S_SIGNED_BIN_FILE}) #Create and sign for concatenated binary image, should align with the TF-M BL2 set_property(GLOBAL APPEND PROPERTY extra_post_build_commands From 02735af09daa62417185fa564d7fad6e0bbebdbc Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Tue, 23 Sep 2025 09:00:16 +0200 Subject: [PATCH 16/36] [nrf fromtree] trusted-firmware-m: Prepare to generate tfm_merged.bin When CONFIG_TFM_MCUBOOT_IMAGE_NUMBER is 1, the process to create the final tfm_merged.bin file is more complex. This prepares the content to introduce the generation of tfm_merged.bin for use in FOTA applications. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit d1534c539526d60f6c38140a8a253668c5c0f25b) --- modules/trusted-firmware-m/CMakeLists.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index df8843f48749..bc5a847bac0c 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -568,8 +568,8 @@ if (CONFIG_BUILD_WITH_TFM) endfunction() set(MERGED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.hex) - set(S_NS_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns.hex) - set(S_NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) + set(S_NS_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed.hex) + set(S_NS_SIGNED_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed_signed.hex) set(NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.hex) set(S_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.hex) set(NS_SIGNED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.bin) @@ -599,28 +599,28 @@ if (CONFIG_BUILD_WITH_TFM) ) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") - tfm_sign(sign_cmd_s_ns_hex SUFFIX "S_NS" + tfm_sign(sign_cmd_s_ns_confirm_hex SUFFIX "S_NS" HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} - INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_HEX_FILE}) + INPUT_FILE ${S_NS_CONFIRMED_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_CONFIRMED_HEX_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${S_NS_HEX_FILE} + -o ${S_NS_CONFIRMED_HEX_FILE} $ ${NS_HEX_APP_FILE} - COMMAND ${sign_cmd_s_ns_hex} + COMMAND ${sign_cmd_s_ns_confirm_hex} COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py -o ${MERGED_HEX_FILE} $<$:$> $<$>:$> - ${S_NS_SIGNED_HEX_FILE} + ${S_NS_SIGNED_CONFIRMED_HEX_FILE} ) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts - ${S_NS_HEX_FILE} - ${S_NS_SIGNED_HEX_FILE} + ${S_NS_CONFIRMED_HEX_FILE} + ${S_NS_SIGNED_CONFIRMED_HEX_FILE} ${MERGED_HEX_FILE} ) From aa303f4feda4659ea8c1be5c7ce7391d4ecf29de Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Tue, 23 Sep 2025 09:49:22 +0200 Subject: [PATCH 17/36] [nrf fromtree] trusted-firmware-m: Generate tfm_merged.bin When CONFIG_TFM_MCUBOOT_IMAGE_NUMBER is 1, all images are merged. Currently, there is no tfm_merged.bin file for use in FOTA. This adds file generation to fulfill that requirement. Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit bbc73af78f187b1daab0dd4a3cca92cb28a87999) --- modules/trusted-firmware-m/CMakeLists.txt | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index bc5a847bac0c..0e10598c9c75 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -568,8 +568,11 @@ if (CONFIG_BUILD_WITH_TFM) endfunction() set(MERGED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.hex) + set(MERGED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.bin) set(S_NS_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed.hex) set(S_NS_SIGNED_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed_signed.hex) + set(S_NS_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns.hex) + set(S_NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) set(NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.hex) set(S_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.hex) set(NS_SIGNED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.bin) @@ -602,6 +605,9 @@ if (CONFIG_BUILD_WITH_TFM) tfm_sign(sign_cmd_s_ns_confirm_hex SUFFIX "S_NS" HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} INPUT_FILE ${S_NS_CONFIRMED_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_CONFIRMED_HEX_FILE}) + tfm_sign(sign_cmd_s_ns_hex SUFFIX "S_NS" + HEADER TRAILER MAX_SECTORS ${S_NS_MAX_SECTORS} + INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_HEX_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py @@ -618,10 +624,28 @@ if (CONFIG_BUILD_WITH_TFM) ${S_NS_SIGNED_CONFIRMED_HEX_FILE} ) + set_property(GLOBAL APPEND PROPERTY extra_post_build_commands + COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py + -o ${S_NS_HEX_FILE} + $ + ${NS_HEX_APP_FILE} + + COMMAND ${sign_cmd_s_ns_hex} + + COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py + -o ${MERGED_BIN_FILE} --output-bin + $<$:$> + $<$>:$> + ${S_NS_SIGNED_HEX_FILE} + ) + set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${S_NS_CONFIRMED_HEX_FILE} ${S_NS_SIGNED_CONFIRMED_HEX_FILE} + ${S_NS_HEX_FILE} + ${S_NS_SIGNED_HEX_FILE} ${MERGED_HEX_FILE} + ${MERGED_BIN_FILE} ) else() From cf9e526c6cbe33e3470ae7daf0d795386e9524ee Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Nov 2025 16:45:45 +1000 Subject: [PATCH 18/36] [nrf fromtree] modules: tf-m: fix CMake formatting Fix formatting errors that cause CI to complain. Signed-off-by: Jordan Yates (cherry picked from commit 2219a875b25093920768eda63ddc54ae5e7a925e) --- modules/trusted-firmware-m/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 0e10598c9c75..ff965457da42 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -319,8 +319,8 @@ if (CONFIG_BUILD_WITH_TFM) # number of parallel jobs to 1. set(PARALLEL_JOBS -j 1) else() - # Leave PARALLEL_JOBS unset and use the default number of - # threads. Which is num_cores+2 on Ninja and MAKEFLAGS with Make. + # Leave PARALLEL_JOBS unset and use the default number of + # threads. Which is num_cores+2 on Ninja and MAKEFLAGS with Make. endif() set(tfm_image_info MAP "name: tfm, source-dir: ${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}") From 62f3df0a2fc967a3b1432f09bcd5130dc6918b19 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Nov 2025 16:23:25 +1000 Subject: [PATCH 19/36] [nrf fromtree] modules: tf-m: remove `S_NS_CONFIRMED_HEX_FILE` `S_NS_CONFIRMED_HEX_FILE` was never generating a confirmed file, just the same file contents as `S_NS_HEX_FILE`. Since no logic needs a confirmed merge of `tfm_s.hex` and `zephyr.hex`, just remove the logic instead of fixing it. Signed-off-by: Jordan Yates (cherry picked from commit 15e8305ce9d3f131bae5dc46a1f1a410c7d82793) --- modules/trusted-firmware-m/CMakeLists.txt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index ff965457da42..6ffefd6b93c0 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -569,7 +569,6 @@ if (CONFIG_BUILD_WITH_TFM) set(MERGED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.hex) set(MERGED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_merged.bin) - set(S_NS_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed.hex) set(S_NS_SIGNED_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed_signed.hex) set(S_NS_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns.hex) set(S_NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) @@ -604,14 +603,14 @@ if (CONFIG_BUILD_WITH_TFM) elseif(CONFIG_TFM_MCUBOOT_IMAGE_NUMBER STREQUAL "1") tfm_sign(sign_cmd_s_ns_confirm_hex SUFFIX "S_NS" HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} - INPUT_FILE ${S_NS_CONFIRMED_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_CONFIRMED_HEX_FILE}) + INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_CONFIRMED_HEX_FILE}) tfm_sign(sign_cmd_s_ns_hex SUFFIX "S_NS" HEADER TRAILER MAX_SECTORS ${S_NS_MAX_SECTORS} INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_HEX_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${S_NS_CONFIRMED_HEX_FILE} + -o ${S_NS_HEX_FILE} $ ${NS_HEX_APP_FILE} @@ -625,11 +624,6 @@ if (CONFIG_BUILD_WITH_TFM) ) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands - COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${S_NS_HEX_FILE} - $ - ${NS_HEX_APP_FILE} - COMMAND ${sign_cmd_s_ns_hex} COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py @@ -640,7 +634,6 @@ if (CONFIG_BUILD_WITH_TFM) ) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts - ${S_NS_CONFIRMED_HEX_FILE} ${S_NS_SIGNED_CONFIRMED_HEX_FILE} ${S_NS_HEX_FILE} ${S_NS_SIGNED_HEX_FILE} From fd1aaf11b13545dc5f6838c09fd6bb45887d5dd2 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Nov 2025 16:26:47 +1000 Subject: [PATCH 20/36] [nrf fromtree] modules: tf-m: generate `tfm_s_zephyr_ns_signed.bin` Generate a binary version of `tfm_s_zephyr_ns_signed.hex` with objcopy. This file is valid for performing OTA upgrades, unlike `tfm_merged.bin`, which contains BL2. Signed-off-by: Jordan Yates (cherry picked from commit db339e46b510af313267b3db2641e5fd2ecaff44) --- modules/trusted-firmware-m/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 6ffefd6b93c0..934dd47b06d2 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -572,6 +572,7 @@ if (CONFIG_BUILD_WITH_TFM) set(S_NS_SIGNED_CONFIRMED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_confirmed_signed.hex) set(S_NS_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns.hex) set(S_NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.hex) + set(S_NS_SIGNED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_zephyr_ns_signed.bin) set(NS_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.hex) set(S_SIGNED_HEX_FILE ${CMAKE_BINARY_DIR}/zephyr/tfm_s_signed.hex) set(NS_SIGNED_BIN_FILE ${CMAKE_BINARY_DIR}/zephyr/zephyr_ns_signed.bin) @@ -631,6 +632,8 @@ if (CONFIG_BUILD_WITH_TFM) $<$:$> $<$>:$> ${S_NS_SIGNED_HEX_FILE} + + COMMAND ${CMAKE_OBJCOPY} --input-target=ihex --output-target=binary ${S_NS_SIGNED_HEX_FILE} ${S_NS_SIGNED_BIN_FILE} ) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts From b811dcf6c8ba64d23a63dd8b78e8d8322cb3ebb7 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sun, 2 Nov 2025 16:28:37 +1000 Subject: [PATCH 21/36] [nrf fromtree] modules: tf-m: generate `tfm_merged.bin` from `tfm_merged.hex` Since `tfm_merged.bin` now contains BL2, it can only be used for the same purposes as `tfm_merged.hex` (intial firmware loading). Therefore it should be using the confirmed images that `tfm_merged.hex` does. Since the only difference between the two files with that change is now the output format, we can directly generate `tfm_merged.bin` from `tfm_merged.hex` with `objcopy` instead of going through `mergehex.py`. Signed-off-by: Jordan Yates (cherry picked from commit 74a23eacd540c4e16317d56bf0a8fd0a6088e935) --- modules/trusted-firmware-m/CMakeLists.txt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 934dd47b06d2..afebaaf0ea1e 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -622,17 +622,13 @@ if (CONFIG_BUILD_WITH_TFM) $<$:$> $<$>:$> ${S_NS_SIGNED_CONFIRMED_HEX_FILE} + + COMMAND ${CMAKE_OBJCOPY} --input-target=ihex --output-target=binary ${MERGED_HEX_FILE} ${MERGED_BIN_FILE} ) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${sign_cmd_s_ns_hex} - COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/build/mergehex.py - -o ${MERGED_BIN_FILE} --output-bin - $<$:$> - $<$>:$> - ${S_NS_SIGNED_HEX_FILE} - COMMAND ${CMAKE_OBJCOPY} --input-target=ihex --output-target=binary ${S_NS_SIGNED_HEX_FILE} ${S_NS_SIGNED_BIN_FILE} ) @@ -640,6 +636,7 @@ if (CONFIG_BUILD_WITH_TFM) ${S_NS_SIGNED_CONFIRMED_HEX_FILE} ${S_NS_HEX_FILE} ${S_NS_SIGNED_HEX_FILE} + ${S_NS_SIGNED_BIN_FILE} ${MERGED_HEX_FILE} ${MERGED_BIN_FILE} ) From 215a316b565502a7516eb77818cca13a558e44b0 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Wed, 14 Aug 2024 16:22:00 +1000 Subject: [PATCH 22/36] [nrf fromtree] modules: tfm: disable SECURE_UART when TFM_LOG_LEVEL_SILENCE Explicitly disable the SECURE_UART TFM define when `CONFIG_TFM_LOG_LEVEL_SILENCE=y`. The secure UART is only enabled by default on nRF platforms to match the current TF-M defaults. Signed-off-by: Jordan Yates (cherry picked from commit 9366e4a48eb9879ea9115c7eea424dc311b372a0) --- doc/releases/migration-guide-4.4.rst | 7 +++++++ modules/trusted-firmware-m/CMakeLists.txt | 6 ++++++ modules/trusted-firmware-m/Kconfig.tfm | 9 +++++++++ 3 files changed, 22 insertions(+) diff --git a/doc/releases/migration-guide-4.4.rst b/doc/releases/migration-guide-4.4.rst index 762082635b1c..6919f84e875c 100644 --- a/doc/releases/migration-guide-4.4.rst +++ b/doc/releases/migration-guide-4.4.rst @@ -57,5 +57,12 @@ Other subsystems Modules ******* +Trusted Firmware-M +================== + +* The ``SECURE_UART1`` TF-M define is now controlled by Zephyr's + :kconfig:option:`CONFIG_TFM_SECURE_UART`. This option will override any platform values previously + specified in the TF-M repository. + Architectures ************* diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index afebaaf0ea1e..95e034f19532 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -156,6 +156,12 @@ if (CONFIG_BUILD_WITH_TFM) list(APPEND TFM_CMAKE_ARGS -DTFM_SPM_LOG_LEVEL=${TFM_SPM_LOG_LEVEL}) endif() + if(CONFIG_TFM_SECURE_UART) + list(APPEND TFM_CMAKE_ARGS -DSECURE_UART1=1) + else() + list(APPEND TFM_CMAKE_ARGS -DSECURE_UART1=0) + endif() + # Enable TFM partitions as specified in Kconfig foreach(partition ${TFM_VALID_PARTITIONS}) if (CONFIG_${partition}) diff --git a/modules/trusted-firmware-m/Kconfig.tfm b/modules/trusted-firmware-m/Kconfig.tfm index f587ea01e45f..6a8140b23d5a 100644 --- a/modules/trusted-firmware-m/Kconfig.tfm +++ b/modules/trusted-firmware-m/Kconfig.tfm @@ -475,6 +475,15 @@ config TFM_SPM_LOG_LEVEL_SILENCE bool "Off" endchoice +config TFM_SECURE_UART + bool "TF-M configure UART instance as secure peripheral" + default y if SOC_FAMILY_NORDIC_NRF && !TFM_LOG_LEVEL_SILENCE + help + Configure the UART instance as a secure peripheral for TF-M logging. + This makes the UART instance unavailable to the non-secure application. + When this option is selected the device tree node for the UART instance + needs to be disabled for the non-secure application. + config TFM_EXCEPTION_INFO_DUMP bool "TF-M exception info dump" default y From 4226ab8295c29da4ce02e1b963d92526b249cc67 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Thu, 4 Dec 2025 11:36:15 +1000 Subject: [PATCH 23/36] [nrf fromtree] modules: tfm: don't pad unconfirmed image If the image is not confirmed, there is no need to pad the entire flash slot with empty data. This reduces the size of `tfm_s_zephyr_ns.signed.bin` (the file actually sent for an OTA upgrade) from 100% of the slot size down to the size of the application. Signed-off-by: Jordan Yates (cherry picked from commit 6fe6a8bbb473a180299793d4be7d9443f36d441c) --- modules/trusted-firmware-m/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 95e034f19532..0fc884cf79fa 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -612,7 +612,7 @@ if (CONFIG_BUILD_WITH_TFM) HEADER TRAILER CONFIRM MAX_SECTORS ${S_NS_MAX_SECTORS} INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_CONFIRMED_HEX_FILE}) tfm_sign(sign_cmd_s_ns_hex SUFFIX "S_NS" - HEADER TRAILER MAX_SECTORS ${S_NS_MAX_SECTORS} + HEADER MAX_SECTORS ${S_NS_MAX_SECTORS} INPUT_FILE ${S_NS_HEX_FILE} OUTPUT_FILE ${S_NS_SIGNED_HEX_FILE}) set_property(GLOBAL APPEND PROPERTY extra_post_build_commands From 8fbc3af51bbe0e5ffd33435d3a2536bd04175b9e Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Thu, 28 Aug 2025 15:14:10 +0300 Subject: [PATCH 24/36] [nrf fromtree] manifest: psa-arch-tests: update to 24.03_API1.6_CRYPTO_1.1.0 Fixup to the TF-M 2.2.0 update. Signed-off-by: Tomi Fontanilles (cherry picked from commit b8aa511424c1850e41f9270711f18b2f5869c8ed) --- submanifests/optional.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submanifests/optional.yaml b/submanifests/optional.yaml index 57ad7affdd1c..724ab8d1fb30 100644 --- a/submanifests/optional.yaml +++ b/submanifests/optional.yaml @@ -23,7 +23,7 @@ manifest: groups: - optional - name: psa-arch-tests - revision: 2cadb02a72eacda7042505dcbdd492371e8ce024 + revision: 10fd24782323fc3faf6c787959f4a9d4130bedb8 path: modules/tee/tf-m/psa-arch-tests remote: upstream groups: From 11f731126e4e64d69c95d253d00d70dfe0023be2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 27 Jul 2025 18:44:24 -0700 Subject: [PATCH 25/36] [nrf fromtree] modules/psa-arch-tests: Add GCC 14.3 support patch psa-arch-tests includes device drivers that failed to mark registers with 'volatile'. GCC 14.3 cleverly optimized sequential register accesses using strd/ldrd instructions which caused the drivers to fail. Move the psa-arch-tests repository forward to the version which includes a fix for this. Signed-off-by: Keith Packard (cherry picked from commit 5505c0d7ad38ce053f9f589604f7ec01fff3ba94) --- submanifests/optional.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submanifests/optional.yaml b/submanifests/optional.yaml index 724ab8d1fb30..04accf4152c0 100644 --- a/submanifests/optional.yaml +++ b/submanifests/optional.yaml @@ -23,7 +23,7 @@ manifest: groups: - optional - name: psa-arch-tests - revision: 10fd24782323fc3faf6c787959f4a9d4130bedb8 + revision: 87b08682a111ebb085cd8b1ea41d603191d6d146 path: modules/tee/tf-m/psa-arch-tests remote: upstream groups: From 2a342b147fe7d27e4aebd120d0353033c4ba55c0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 24 Oct 2025 08:40:38 -0700 Subject: [PATCH 26/36] [nrf fromtree] west: Sync picolibc with SDK 0.17.4 This updates the version of picolibc to match that used by SDK 0.17.4. In the Zephyr picolibc fork, that's marked with the zephyr-sdk-0.17.4 tag now, which is on the zephyr-sdk-0.17 branch. Changes since the previous version which impact using the module: * machine/arm: Disable exception tables in ARM string asm code * cmake: Silence messages about core-isa.h * Correct return type of __non_atomic_*_ungetc functions * Delete obsoleted _syslist.h Signed-off-by: Keith Packard (cherry picked from commit 046232a6497463162fb846da4ba889ad1feff6cf) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index b9a1a35b79e9..893d43b5be66 100644 --- a/west.yml +++ b/west.yml @@ -352,7 +352,7 @@ manifest: - debug - name: picolibc path: modules/lib/picolibc - revision: 560946f26db075c296beea5b39d99e6de43c9010 + revision: ca8b6ebba5226a75545e57a140443168a26ba664 - name: segger revision: cf56b1d9c80f81a26e2ac5727c9cf177116a4692 path: modules/debug/segger From f4c724b04fe57eb636f171bdde505c61dcba8d4f Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Mon, 20 Oct 2025 10:58:49 -0400 Subject: [PATCH 27/36] [nrf fromtree] manifest: optional: remove sof from optional manifest Nothing in Zephyr uses SOF, it is the other way round, SOF uses Zephyr, creating a cyclic dependency in some cases making it difficult to apply changes to areas used by SOF upstream. Part of #91061 Signed-off-by: Anas Nashif (cherry picked from commit 1a780f933e1da387a28bcfcca4038db6b6c13c5f) --- MAINTAINERS.yml | 14 -------------- modules/Kconfig | 1 - modules/Kconfig.sof | 11 ----------- submanifests/optional.yaml | 6 ------ 4 files changed, 32 deletions(-) delete mode 100644 modules/Kconfig.sof diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 2018a6ed356b..f689445af543 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -5803,20 +5803,6 @@ West: labels: - "area: Debugging" -"West project: sof": - status: maintained - maintainers: - - kv2019i - collaborators: - - andyross - - nashif - - lyakh - - lgirdwood - files: - - modules/Kconfig.sof - labels: - - "area: Audio" - "West project: tf-m-tests": status: maintained maintainers: diff --git a/modules/Kconfig b/modules/Kconfig index b66955981fa0..cd29dc3ccc88 100644 --- a/modules/Kconfig +++ b/modules/Kconfig @@ -36,7 +36,6 @@ source "modules/Kconfig.picolibc" source "modules/Kconfig.renesas" source "modules/Kconfig.rust" source "modules/Kconfig.simplelink" -source "modules/Kconfig.sof" source "modules/Kconfig.stm32" source "modules/Kconfig.syst" source "modules/Kconfig.telink" diff --git a/modules/Kconfig.sof b/modules/Kconfig.sof deleted file mode 100644 index 4a0b94186606..000000000000 --- a/modules/Kconfig.sof +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2020 Intel Corporation -# SPDX-License-Identifier: Apache-2.0 - -config ZEPHYR_SOF_MODULE - bool - -config SOF - bool "Sound Open Firmware (SOF)" - depends on ZEPHYR_SOF_MODULE - help - Build Sound Open Firmware (SOF) support. diff --git a/submanifests/optional.yaml b/submanifests/optional.yaml index 04accf4152c0..322a37871bc7 100644 --- a/submanifests/optional.yaml +++ b/submanifests/optional.yaml @@ -28,12 +28,6 @@ manifest: remote: upstream groups: - optional - - name: sof - revision: ba8de7551f88a4f8d4533791274fa85b37ec332e - path: modules/audio/sof - remote: upstream - groups: - - optional - name: tf-m-tests revision: a90702bcb8fadb6f70daf0ffbb13888dfe63fc99 path: modules/tee/tf-m/tf-m-tests From ed44dd43e4c8f4f518719a7d8159fdf536348b61 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 22 Oct 2025 07:03:19 -0400 Subject: [PATCH 28/36] [nrf fromtree] intel_adsp: remove workaround for SOF setting core count During transition to HWMv2 this workaround was added, which should instead be in SOF and not in Zephyr, as CORE_COUNT is a SOF Kconfig. Remove this and instead set the CORE_COUNT in SOF to the MP_MAX_NUM_CPUS. Signed-off-by: Anas Nashif (cherry picked from commit 71ec5df8f31fd4b386d5f9582fd66403c861069c) --- soc/intel/intel_adsp/Kconfig.defconfig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/soc/intel/intel_adsp/Kconfig.defconfig b/soc/intel/intel_adsp/Kconfig.defconfig index 1d8312e7fb6c..f69bc31cd8c7 100644 --- a/soc/intel/intel_adsp/Kconfig.defconfig +++ b/soc/intel/intel_adsp/Kconfig.defconfig @@ -8,11 +8,6 @@ if SOC_FAMILY_INTEL_ADSP rsource "*/Kconfig.defconfig.series" # A workaround for HWMv2 to recover SOF arch/xtensa defaults overridden by arch/host. -if SOF -config CORE_COUNT - int - default MP_MAX_NUM_CPUS -endif config XTENSA_RPO_CACHE def_bool y From 50ed7e41926920d2b411d85a1ea317934751a373 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Wed, 22 Oct 2025 07:38:10 -0400 Subject: [PATCH 29/36] [nrf fromtree] west_commands: do not depend on CONFIG_SOF Do not depend on SOF config, use RIMAGE_SCHEMA instead, defined in SOF. Signed-off-by: Anas Nashif (cherry picked from commit 95b48cd5ba7af5b33f01568e9771d6868cf29d3e) --- scripts/west_commands/sign.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/west_commands/sign.py b/scripts/west_commands/sign.py index c6fa273138e5..c90361a1447b 100644 --- a/scripts/west_commands/sign.py +++ b/scripts/west_commands/sign.py @@ -585,8 +585,8 @@ def sign(self, command, build_dir, build_conf, formats): # Non-SOF build does not have extended manifest data for # rimage to process, which might result in rimage error. # So skip it when not doing SOF builds. - is_sof_build = build_conf.getboolean('CONFIG_SOF') - if not is_sof_build: + rimage_schema = build_conf.get('CONFIG_RIMAGE_SIGNING_SCHEMA', None) + if rimage_schema is None: no_manifest = True self.generate_uuid_registry() From 5fdeae88aba4ce508c7dd59b549ed66a22bfb185 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Tue, 3 Jun 2025 10:19:20 -0700 Subject: [PATCH 30/36] [nrf fromtree] soc: intel_adsp: remove IDC dt default for CONFIG_INTEL_ADSP_IPC The SoC specific IPC driver is for host IPC, and not IDC (which is between CPUs). So there is no need to use the IDC devicetree binding to enable the kconfig. Signed-off-by: Daniel Leung (cherry picked from commit d910306fd00671a4e9df21131a723e4aa9a6d749) --- soc/intel/intel_adsp/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/soc/intel/intel_adsp/Kconfig b/soc/intel/intel_adsp/Kconfig index 27a8e838b67b..7e827efad522 100644 --- a/soc/intel/intel_adsp/Kconfig +++ b/soc/intel/intel_adsp/Kconfig @@ -33,12 +33,10 @@ config INTEL_ADSP_SIM_NO_SECONDARY_CORE_FLOW endif # INTEL_ADSP_SIM DT_COMPAT_INTEL_ADSP_HOST_IPC := intel,adsp-host-ipc -DT_COMPAT_INTEL_ADSP_IDC := intel,adsp-idc config INTEL_ADSP_IPC bool "Driver for the host IPC interrupt delivery" default $(dt_compat_enabled,$(DT_COMPAT_INTEL_ADSP_HOST_IPC)) if !SOF - default $(dt_compat_enabled,$(DT_COMPAT_INTEL_ADSP_IDC)) if !SOF help Driver for the host IPC interrupt delivery mechanism. Currently SOF has its own driver for this hardware. From d9ad2248adfde4dbb644edc9a412ab0c58abd0c6 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Fri, 12 Sep 2025 15:22:56 -0700 Subject: [PATCH 31/36] [nrf fromtree] soc: intel_adsp: rework host IPC using IPC service This reworks the Intel audio DSP host IPC driver as a backend of the IPC service. This is the first step to rework IPC in SOF (Sound Open Firmware) into using a more generic IPC API instead of a SoC specific one. For now, it keeps the old interface to maintain usability as it is going to be a multiple process to rework IPC over there. Also, the structure of the new IPC backend resembles the SoC specific driver to make it easier to compare between them at this first iteration. Future optimizations will probably be needed once we start modifying the SOF side to utilize the IPC interface. Signed-off-by: Daniel Leung (cherry picked from commit cf7e2e63c17aab88289637ebc691003de1c8b628) --- .../zephyr/ipc/backends/intel_adsp_host_ipc.h | 213 ++++++++ soc/intel/intel_adsp/Kconfig | 17 +- soc/intel/intel_adsp/common/CMakeLists.txt | 2 +- .../common/include/intel_adsp_ipc.h | 117 +---- soc/intel/intel_adsp/common/ipc.c | 345 +++---------- .../ipc/ipc_service/backends/CMakeLists.txt | 1 + subsys/ipc/ipc_service/backends/Kconfig | 1 + .../ipc_service/backends/Kconfig.intel_adsp | 11 + .../backends/ipc_intel_adsp_host_ipc.c | 464 ++++++++++++++++++ 9 files changed, 789 insertions(+), 382 deletions(-) create mode 100644 include/zephyr/ipc/backends/intel_adsp_host_ipc.h create mode 100644 subsys/ipc/ipc_service/backends/Kconfig.intel_adsp create mode 100644 subsys/ipc/ipc_service/backends/ipc_intel_adsp_host_ipc.c diff --git a/include/zephyr/ipc/backends/intel_adsp_host_ipc.h b/include/zephyr/ipc/backends/intel_adsp_host_ipc.h new file mode 100644 index 000000000000..74938e53c86f --- /dev/null +++ b/include/zephyr/ipc/backends/intel_adsp_host_ipc.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2022, 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_IPC_BACKEND_INTEL_ADSP_IPC_H +#define ZEPHYR_INCLUDE_IPC_BACKEND_INTEL_ADSP_IPC_H + +#include +#include +#include +#include + +#include + +/** Enum on IPC send length argument to indicate IPC message type. */ +enum intel_adsp_send_len { + /** Normal IPC message. */ + INTEL_ADSP_IPC_SEND_MSG, + + /** Synchronous IPC message. */ + INTEL_ADSP_IPC_SEND_MSG_SYNC, + + /** Emergency IPC message. */ + INTEL_ADSP_IPC_SEND_MSG_EMERGENCY, + + /** Send a DONE message. */ + INTEL_ADSP_IPC_SEND_DONE, + + /** Query backend to see if IPC is complete. */ + INTEL_ADSP_IPC_SEND_IS_COMPLETE, +}; + +/** Enum on callback return values. */ +enum intel_adsp_cb_ret { + /** Callback return to indicate no issue. Must be 0. */ + INTEL_ADSP_IPC_CB_RET_OKAY = 0, + + /** Callback return to signal needing external completion. */ + INTEL_ADSP_IPC_CB_RET_EXT_COMPLETE, +}; + +/** Enum on callback length argument to indicate which triggers the callback. */ +enum intel_adsp_cb_len { + /** Callback length to indicate this is an IPC message. */ + INTEL_ADSP_IPC_CB_MSG, + + /** Callback length to indicate this is a DONE message. */ + INTEL_ADSP_IPC_CB_DONE, +}; + +/** Struct for IPC message descriptor. */ +struct intel_adsp_ipc_msg { + /** Header specific to platform. */ + uint32_t data; + + /** Extension specific to platform. */ + uint32_t ext_data; + + /** Timeout for sending synchronuous message. */ + k_timeout_t timeout; +}; + +#ifdef CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE + +/** + * @brief Intel ADSP IPC Message Handler Callback. + * + * This function, once registered via intel_adsp_ipc_set_message_handler(), + * is invoked in interrupt context to service messages sent from the + * foreign/connected IPC context. The message contents of the TDR and + * TDD registers are provided in the data/ext_data argument. + * + * The function should return true if processing of the message is + * complete and return notification to the other side (via the TDA + * register) is desired immediately. Returning false means that no + * return "DONE" interrupt will occur until intel_adsp_ipc_complete() is + * called on this device at some point in the future. + * + * @note Further messages on the link will not be transmitted or + * received while an in-progress message remains incomplete! + * + * @param dev IPC device. + * @param arg Registered argument from intel_adsp_ipc_set_message_handler(). + * @param data Message data from other side (low bits of TDR register). + * @param ext_dat Extended message data (TDD register). + * @return true if the message is completely handled. + */ +typedef bool (*intel_adsp_ipc_handler_t)(const struct device *dev, void *arg, uint32_t data, + uint32_t ext_data); + +/** + * @brief Intel ADSP IPC Message Complete Callback. + * + * This function, once registered via intel_adsp_ipc_set_done_handler(), is + * invoked in interrupt context when a "DONE" return interrupt is + * received from the other side of the connection (indicating that a + * previously sent message is finished processing). + * + * @note On Intel ADSP hardware the DONE interrupt is transmitted + * synchronously with the interrupt being cleared on the remote + * device. It is not possible to delay processing. This callback + * will still occur, but protocols which rely on notification of + * asynchronous command processing will need modification. + * + * @param dev IPC device. + * @param arg Registered argument from intel_adsp_ipc_set_done_handler(). + * @return True if IPC completion will be done externally, otherwise false. + * @note Returning True will cause this API to skip writing IPC registers + * signalling IPC message completion and those actions should be done by + * external code manually. Returning false from the handler will perform + * writing to IPC registers signalling message completion normally by this API. + */ +typedef bool (*intel_adsp_ipc_done_t)(const struct device *dev, void *arg); + +#endif /* CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE */ + +#ifdef CONFIG_PM_DEVICE +typedef int (*intel_adsp_ipc_resume_handler_t)(const struct device *dev, void *arg); + +typedef int (*intel_adsp_ipc_suspend_handler_t)(const struct device *dev, void *arg); +#endif /* CONFIG_PM_DEVICE */ + +/** + * Intel Audio DSP IPC service backend config struct. + */ +struct intel_adsp_ipc_config { + /** Pointer to hardware register block. */ + volatile struct intel_adsp_ipc *regs; +}; + +/** + * Intel Audio DSP IPC service backend data struct. + */ +struct intel_adsp_ipc_data { + /** Semaphore used to wait for remote acknowledgment of sent message. */ + struct k_sem sem; + + /** General driver lock. */ + struct k_spinlock lock; + + /** Pending TX acknowlegement. */ + bool tx_ack_pending; + + /** Pointer to endpoint configuration. */ + const struct ipc_ept_cfg *ept_cfg; + +#ifdef CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE + /** Callback for message handler. */ + intel_adsp_ipc_handler_t handle_message; + + /** Argument for message handler callback. */ + void *handler_arg; + + /** Callback for done notification. */ + intel_adsp_ipc_done_t done_notify; + + /** Argument for done notification callback. */ + void *done_arg; +#endif /* CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE */ + +#ifdef CONFIG_PM_DEVICE + /** Pointer to resume handler. */ + intel_adsp_ipc_resume_handler_t resume_fn; + + /** Argument for resume handler. */ + void *resume_fn_args; + + /** Pointer to suspend handler. */ + intel_adsp_ipc_suspend_handler_t suspend_fn; + + /** Argument for suspend handler. */ + void *suspend_fn_args; +#endif /* CONFIG_PM_DEVICE */ +}; + +/** + * Endpoint private data struct. + */ +struct intel_adsp_ipc_ept_priv_data { + /** Callback return value (enum intel_adsp_cb_ret). */ + int cb_ret; + + /** Pointer to additional private data. */ + void *priv; +}; + +#ifdef CONFIG_PM_DEVICE + +/** + * @brief Registers resume callback handler used to resume Device from suspended state. + * + * @param dev IPC device. + * @param fn Callback function. + * @param arg Value to pass as the "arg" parameter to the function. + */ +void intel_adsp_ipc_set_resume_handler(const struct device *dev, intel_adsp_ipc_resume_handler_t fn, + void *arg); + +/** + * @brief Registers suspend callback handler used to suspend active Device. + * + * @param dev IPC device. + * @param fn Callback function. + * @param arg Value to pass as the "arg" parameter to the function. + */ +void intel_adsp_ipc_set_suspend_handler(const struct device *dev, + intel_adsp_ipc_suspend_handler_t fn, void *arg); + +#endif /* CONFIG_PM_DEVICE */ + +#endif /* ZEPHYR_INCLUDE_IPC_BACKEND_INTEL_ADSP_IPC_H */ diff --git a/soc/intel/intel_adsp/Kconfig b/soc/intel/intel_adsp/Kconfig index 7e827efad522..7523f167fad2 100644 --- a/soc/intel/intel_adsp/Kconfig +++ b/soc/intel/intel_adsp/Kconfig @@ -11,6 +11,7 @@ config SOC_FAMILY_INTEL_ADSP select ARCH_HAS_USERSPACE if XTENSA_MMU imply XTENSA_MMU_DOUBLE_MAP select CPU_CACHE_INCOHERENT + select IPC_SERVICE if DT_HAS_INTEL_ADSP_HOST_IPC_ENABLED if SOC_FAMILY_INTEL_ADSP @@ -32,14 +33,20 @@ config INTEL_ADSP_SIM_NO_SECONDARY_CORE_FLOW endif # INTEL_ADSP_SIM -DT_COMPAT_INTEL_ADSP_HOST_IPC := intel,adsp-host-ipc - config INTEL_ADSP_IPC bool "Driver for the host IPC interrupt delivery" - default $(dt_compat_enabled,$(DT_COMPAT_INTEL_ADSP_HOST_IPC)) if !SOF + select DEPRECATED + help + Deprecated config for IPC. Will be removed in the future. + +config INTEL_ADSP_IPC_OLD_INTERFACE + bool "Expose old interface for the IPC" + depends on IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC + default y + select INTEL_ADSP_IPC help - Driver for the host IPC interrupt delivery mechanism. - Currently SOF has its own driver for this hardware. + Expose the old IPC interface (intel_adsp_ipc_* functions) to + maintain backward compatibility. config MEMORY_WIN_0_SIZE int "Size of memory window 0" diff --git a/soc/intel/intel_adsp/common/CMakeLists.txt b/soc/intel/intel_adsp/common/CMakeLists.txt index a39e9ea98e77..9e49bd26eabe 100644 --- a/soc/intel/intel_adsp/common/CMakeLists.txt +++ b/soc/intel/intel_adsp/common/CMakeLists.txt @@ -9,7 +9,7 @@ zephyr_library_named(intel_adsp_common) zephyr_include_directories(include) zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) -zephyr_library_sources_ifdef(CONFIG_INTEL_ADSP_IPC ipc.c) +zephyr_library_sources_ifdef(CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE ipc.c) zephyr_library_sources_ifdef(CONFIG_GDBSTUB gdbstub_backend_sram.c diff --git a/soc/intel/intel_adsp/common/include/intel_adsp_ipc.h b/soc/intel/intel_adsp/common/include/intel_adsp_ipc.h index 1f283466a735..30a8f81360e2 100644 --- a/soc/intel/intel_adsp/common/include/intel_adsp_ipc.h +++ b/soc/intel/intel_adsp/common/include/intel_adsp_ipc.h @@ -9,69 +9,9 @@ #include #include -struct intel_adsp_ipc_config { - volatile struct intel_adsp_ipc *regs; -}; +#include -/** - * @brief Intel ADSP IPC Message Handler Callback. - * - * This function, once registered via intel_adsp_ipc_set_message_handler(), - * is invoked in interrupt context to service messages sent from the - * foreign/connected IPC context. The message contents of the TDR and - * TDD registers are provided in the data/ext_data argument. - * - * The function should return true if processing of the message is - * complete and return notification to the other side (via the TDA - * register) is desired immediately. Returning false means that no - * return "DONE" interrupt will occur until intel_adsp_ipc_complete() is - * called on this device at some point in the future. - * - * @note Further messages on the link will not be transmitted or - * received while an in-progress message remains incomplete! - * - * @param dev IPC device. - * @param arg Registered argument from intel_adsp_ipc_set_message_handler(). - * @param data Message data from other side (low bits of TDR register). - * @param ext_dat Extended message data (TDD register). - * @return true if the message is completely handled. - */ -typedef bool (*intel_adsp_ipc_handler_t)(const struct device *dev, void *arg, - uint32_t data, uint32_t ext_data); - -/** - * @brief Intel ADSP IPC Message Complete Callback. - * - * This function, once registered via intel_adsp_ipc_set_done_handler(), is - * invoked in interrupt context when a "DONE" return interrupt is - * received from the other side of the connection (indicating that a - * previously sent message is finished processing). - * - * @note On Intel ADSP hardware the DONE interrupt is transmitted - * synchronously with the interrupt being cleared on the remote - * device. It is not possible to delay processing. This callback - * will still occur, but protocols which rely on notification of - * asynchronous command processing will need modification. - * - * @param dev IPC device. - * @param arg Registered argument from intel_adsp_ipc_set_done_handler(). - * @return True if IPC completion will be done externally, otherwise false. - * @note Returning True will cause this API to skip writing IPC registers - * signalling IPC message completion and those actions should be done by - * external code manually. Returning false from the handler will perform - * writing to IPC registers signalling message completion normally by this API. - */ -typedef bool (*intel_adsp_ipc_done_t)(const struct device *dev, void *arg); - -struct intel_adsp_ipc_data { - struct k_sem sem; - struct k_spinlock lock; - intel_adsp_ipc_handler_t handle_message; - void *handler_arg; - intel_adsp_ipc_done_t done_notify; - void *done_arg; - bool tx_ack_pending; -}; +#if defined(CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE) || defined(__DOXYGEN__) /** * @brief Register message callback handler. @@ -98,34 +38,27 @@ void intel_adsp_ipc_set_message_handler(const struct device *dev, void intel_adsp_ipc_set_done_handler(const struct device *dev, intel_adsp_ipc_done_t fn, void *arg); -/** @brief Initialize Intel ADSP IPC device. - * - * Initialize the device. Must be called before any API calls or - * interrupts can be serviced. - * - * @param dev IPC device. - * @return Zero on success, negative codes for error. - */ -int intel_adsp_ipc_init(const struct device *dev); - -/** @brief Complete an in-progress message. +/** + * @brief Complete an in-progress message. * * Notify the other side that the current in-progress message is * complete. This is a noop if no message is in progress. * * @note Further messages on the link will not be transmitted or - * received while an in-progress message remains incomplete! + * received while an in-progress message remains incomplete! * * @param dev IPC device. */ void intel_adsp_ipc_complete(const struct device *dev); -/** @brief Message-in-progress predicate. +/** + * @brief Message-in-progress predicate. * * Returns false if a message has been received but not yet completed * via intel_adsp_ipc_complete(), true otherwise. * * @param dev IPC device. + * * @return True if no message is in progress. */ bool intel_adsp_ipc_is_complete(const struct device *dev); @@ -175,38 +108,6 @@ int intel_adsp_ipc_send_message_sync(const struct device *dev, void intel_adsp_ipc_send_message_emergency(const struct device *dev, uint32_t data, uint32_t ext_data); -#ifdef CONFIG_PM_DEVICE - -typedef int (*intel_adsp_ipc_resume_handler_t)(const struct device *dev, void *arg); - -typedef int (*intel_adsp_ipc_suspend_handler_t)(const struct device *dev, void *arg); - -/** - * @brief Registers resume callback handler used to resume Device from suspended state. - * - * @param dev IPC device. - * @param fn Callback function. - * @param arg Value to pass as the "arg" parameter to the function. - */ -void intel_adsp_ipc_set_resume_handler(const struct device *dev, - intel_adsp_ipc_resume_handler_t fn, void *arg); - -/** - * @brief Registers suspend callback handler used to suspend active Device. - * - * @param dev IPC device. - * @param fn Callback function. - * @param arg Value to pass as the "arg" parameter to the function. - */ -void intel_adsp_ipc_set_suspend_handler(const struct device *dev, - intel_adsp_ipc_suspend_handler_t fn, void *arg); - -struct ipc_control_driver_api { - intel_adsp_ipc_resume_handler_t resume_fn; - void *resume_fn_args; - intel_adsp_ipc_suspend_handler_t suspend_fn; - void *suspend_fn_args; -}; +#endif /* CONFIG_INTEL_ADSP_IPC_OLD_INTERFACE */ -#endif /* CONFIG_PM_DEVICE */ #endif /* ZEPHYR_INCLUDE_INTEL_ADSP_IPC_H */ diff --git a/soc/intel/intel_adsp/common/ipc.c b/soc/intel/intel_adsp/common/ipc.c index bceee1b7c2a2..aff2c2f70ee6 100644 --- a/soc/intel/intel_adsp/common/ipc.c +++ b/soc/intel/intel_adsp/common/ipc.c @@ -1,20 +1,23 @@ -/* Copyright (c) 2022 Intel Corporation +/* + * Copyright (c) 2022, 2025 Intel Corporation + * * SPDX-License-Identifier: Apache-2.0 */ - #include -#include -#include -#include -#include -#include -#include -#include -#include #include -void intel_adsp_ipc_set_message_handler(const struct device *dev, - intel_adsp_ipc_handler_t fn, void *arg) +#include +#include + +#include +#include + +static struct ipc_ept intel_adsp_ipc_ept; +static struct intel_adsp_ipc_ept_priv_data intel_adsp_ipc_priv_data; +static struct ipc_ept_cfg intel_adsp_ipc_ept_cfg; + +void intel_adsp_ipc_set_message_handler(const struct device *dev, intel_adsp_ipc_handler_t fn, + void *arg) { struct intel_adsp_ipc_data *devdata = dev->data; k_spinlock_key_t key = k_spin_lock(&devdata->lock); @@ -24,8 +27,7 @@ void intel_adsp_ipc_set_message_handler(const struct device *dev, k_spin_unlock(&devdata->lock, key); } -void intel_adsp_ipc_set_done_handler(const struct device *dev, - intel_adsp_ipc_done_t fn, void *arg) +void intel_adsp_ipc_set_done_handler(const struct device *dev, intel_adsp_ipc_done_t fn, void *arg) { struct intel_adsp_ipc_data *devdata = dev->data; k_spinlock_key_t key = k_spin_lock(&devdata->lock); @@ -35,314 +37,121 @@ void intel_adsp_ipc_set_done_handler(const struct device *dev, k_spin_unlock(&devdata->lock, key); } -static void intel_adsp_ipc_isr(const void *devarg) +static void intel_adsp_ipc_receive_cb(const void *data, size_t len, void *priv) { - const struct device *dev = devarg; - const struct intel_adsp_ipc_config *config = dev->config; + const struct device *dev = INTEL_ADSP_IPC_HOST_DEV; struct intel_adsp_ipc_data *devdata = dev->data; + struct intel_adsp_ipc_ept_priv_data *priv_data = + (struct intel_adsp_ipc_ept_priv_data *)priv; + bool done = true; - volatile struct intel_adsp_ipc *regs = config->regs; - k_spinlock_key_t key = k_spin_lock(&devdata->lock); - - if (regs->tdr & INTEL_ADSP_IPC_BUSY) { - bool done = true; + if (len == INTEL_ADSP_IPC_CB_MSG) { + const struct intel_adsp_ipc_msg *msg = (const struct intel_adsp_ipc_msg *)data; if (devdata->handle_message != NULL) { - uint32_t msg = regs->tdr & ~INTEL_ADSP_IPC_BUSY; - uint32_t ext = regs->tdd; - - done = devdata->handle_message(dev, devdata->handler_arg, msg, ext); + done = devdata->handle_message(dev, devdata->handler_arg, msg->data, + msg->ext_data); } - regs->tdr = INTEL_ADSP_IPC_BUSY; if (done) { -#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE - regs->tda = INTEL_ADSP_IPC_ACE1X_TDA_DONE; -#else - regs->tda = INTEL_ADSP_IPC_DONE; -#endif + priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_OKAY; + } else { + priv_data->cb_ret = -EBADMSG; } - } - - /* Same signal, but on different bits in 1.5 */ - bool done = (regs->ida & INTEL_ADSP_IPC_DONE); - - if (done) { + } else if (len == INTEL_ADSP_IPC_CB_DONE) { bool external_completion = false; if (devdata->done_notify != NULL) { external_completion = devdata->done_notify(dev, devdata->done_arg); } - devdata->tx_ack_pending = false; - /* Allow the system to enter the runtime idle state after the IPC acknowledgment - * is received. - */ - pm_policy_state_lock_put(PM_STATE_RUNTIME_IDLE, PM_ALL_SUBSTATES); - k_sem_give(&devdata->sem); - - /* IPC completion registers will be set externally */ + if (external_completion) { - k_spin_unlock(&devdata->lock, key); - return; + priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_EXT_COMPLETE; + } else { + priv_data->cb_ret = INTEL_ADSP_IPC_CB_RET_OKAY; } - - regs->ida = INTEL_ADSP_IPC_DONE; } - - k_spin_unlock(&devdata->lock, key); -} - -int intel_adsp_ipc_init(const struct device *dev) -{ - pm_device_busy_set(dev); - struct intel_adsp_ipc_data *devdata = dev->data; - const struct intel_adsp_ipc_config *config = dev->config; - - memset(devdata, 0, sizeof(*devdata)); - - k_sem_init(&devdata->sem, 0, 1); - - /* ACK any latched interrupts (including TDA to clear IDA on - * the other side!), then enable. - */ - config->regs->tdr = INTEL_ADSP_IPC_BUSY; - config->regs->ida = INTEL_ADSP_IPC_DONE; -#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE - config->regs->tda = INTEL_ADSP_IPC_ACE1X_TDA_DONE; -#else - config->regs->tda = INTEL_ADSP_IPC_DONE; -#endif - config->regs->ctl |= (INTEL_ADSP_IPC_CTL_IDIE | INTEL_ADSP_IPC_CTL_TBIE); - pm_device_busy_clear(dev); - - return 0; } void intel_adsp_ipc_complete(const struct device *dev) { - const struct intel_adsp_ipc_config *config = dev->config; + int ret; -#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE - config->regs->tda = INTEL_ADSP_IPC_ACE1X_TDA_DONE; -#else - config->regs->tda = INTEL_ADSP_IPC_DONE; -#endif + ret = ipc_service_send(&intel_adsp_ipc_ept, NULL, INTEL_ADSP_IPC_SEND_DONE); + + ARG_UNUSED(ret); } bool intel_adsp_ipc_is_complete(const struct device *dev) { - const struct intel_adsp_ipc_config *config = dev->config; - const struct intel_adsp_ipc_data *devdata = dev->data; - bool not_busy = (config->regs->idr & INTEL_ADSP_IPC_BUSY) == 0; + int ret; + + ret = ipc_service_send(&intel_adsp_ipc_ept, NULL, INTEL_ADSP_IPC_SEND_IS_COMPLETE); - return not_busy && !devdata->tx_ack_pending; + return ret == 0; } -int intel_adsp_ipc_send_message(const struct device *dev, - uint32_t data, uint32_t ext_data) +int intel_adsp_ipc_send_message(const struct device *dev, uint32_t data, uint32_t ext_data) { -#ifdef CONFIG_PM_DEVICE - enum pm_device_state current_state; + struct intel_adsp_ipc_msg msg = {.data = data, .ext_data = ext_data}; + int ret; - if (pm_device_state_get(INTEL_ADSP_IPC_HOST_DEV, ¤t_state) != 0 || - current_state != PM_DEVICE_STATE_ACTIVE) { - return -ESHUTDOWN; - } -#endif + ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG); - pm_device_busy_set(dev); - const struct intel_adsp_ipc_config *config = dev->config; - struct intel_adsp_ipc_data *devdata = dev->data; - k_spinlock_key_t key = k_spin_lock(&devdata->lock); - - if ((config->regs->idr & INTEL_ADSP_IPC_BUSY) != 0 || devdata->tx_ack_pending) { - k_spin_unlock(&devdata->lock, key); - return -EBUSY; + if (ret < 0) { + return ret; } - k_sem_reset(&devdata->sem); - /* Prevent entering runtime idle state until IPC acknowledgment is received. */ - pm_policy_state_lock_get(PM_STATE_RUNTIME_IDLE, PM_ALL_SUBSTATES); - devdata->tx_ack_pending = true; - config->regs->idd = ext_data; - config->regs->idr = data | INTEL_ADSP_IPC_BUSY; - k_spin_unlock(&devdata->lock, key); - pm_device_busy_clear(dev); return 0; } -int intel_adsp_ipc_send_message_sync(const struct device *dev, - uint32_t data, uint32_t ext_data, - k_timeout_t timeout) +int intel_adsp_ipc_send_message_sync(const struct device *dev, uint32_t data, uint32_t ext_data, + k_timeout_t timeout) { - struct intel_adsp_ipc_data *devdata = dev->data; + struct intel_adsp_ipc_msg msg = { + .data = data, + .ext_data = ext_data, + .timeout = timeout, + }; + int ret; - int ret = intel_adsp_ipc_send_message(dev, data, ext_data); + ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG_SYNC); - if (!ret) { - k_sem_take(&devdata->sem, timeout); + if (ret < 0) { + return ret; } - return ret; + + return 0; } void intel_adsp_ipc_send_message_emergency(const struct device *dev, uint32_t data, uint32_t ext_data) { - const struct intel_adsp_ipc_config * const config = dev->config; - - volatile struct intel_adsp_ipc * const regs = config->regs; - bool done; - - /* check if host is processing message. */ - while (regs->idr & INTEL_ADSP_IPC_BUSY) { - k_busy_wait(1); - } - - /* check if host has pending acknowledge msg - * Same signal, but on different bits in 1.5 - */ - done = regs->ida & INTEL_ADSP_IPC_DONE; - if (done) { - /* IPC completion */ - regs->ida = INTEL_ADSP_IPC_DONE; - } - - regs->idd = ext_data; - regs->idr = data | INTEL_ADSP_IPC_BUSY; -} - -#if DT_NODE_EXISTS(INTEL_ADSP_IPC_HOST_DTNODE) - -#if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) -static inline void ace_ipc_intc_unmask(void) -{ - ACE_DINT[0].ie[ACE_INTL_HIPC] = BIT(0); -} -#else -static inline void ace_ipc_intc_unmask(void) {} -#endif - -static int dt_init(const struct device *dev) -{ - IRQ_CONNECT(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE), 0, intel_adsp_ipc_isr, - INTEL_ADSP_IPC_HOST_DEV, 0); - irq_enable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); - - ace_ipc_intc_unmask(); - - return intel_adsp_ipc_init(dev); -} - -#ifdef CONFIG_PM_DEVICE - -void intel_adsp_ipc_set_resume_handler(const struct device *dev, - intel_adsp_ipc_resume_handler_t fn, void *arg) -{ - struct ipc_control_driver_api *api = - (struct ipc_control_driver_api *)dev->api; - struct intel_adsp_ipc_data *devdata = dev->data; - k_spinlock_key_t key = k_spin_lock(&devdata->lock); + struct intel_adsp_ipc_msg msg = {.data = data, .ext_data = ext_data}; + int ret; - api->resume_fn = fn; - api->resume_fn_args = arg; + ret = ipc_service_send(&intel_adsp_ipc_ept, &msg, INTEL_ADSP_IPC_SEND_MSG_EMERGENCY); - k_spin_unlock(&devdata->lock, key); + ARG_UNUSED(ret); } -void intel_adsp_ipc_set_suspend_handler(const struct device *dev, - intel_adsp_ipc_suspend_handler_t fn, void *arg) -{ - struct ipc_control_driver_api *api = - (struct ipc_control_driver_api *)dev->api; - struct intel_adsp_ipc_data *devdata = dev->data; - k_spinlock_key_t key = k_spin_lock(&devdata->lock); - - api->suspend_fn = fn; - api->suspend_fn_args = arg; - - k_spin_unlock(&devdata->lock, key); -} +static struct ipc_ept_cfg intel_adsp_ipc_ept_cfg = { + .name = "intel_adsp_ipc_ept", + .cb = { + .received = intel_adsp_ipc_receive_cb, + }, + .priv = (void *)&intel_adsp_ipc_priv_data, +}; -/** - * @brief Manages IPC driver power state change. - * - * @param dev IPC device. - * @param action Power state to be changed to. - * @return int Returns 0 on success or optionaly error code from the - * registered ipc_power_control_api callbacks. - * - * @note PM lock is taken at the start of each power transition to prevent concurrent calls - * to @ref pm_device_action_run function. - * If IPC Device performs hardware operation and device is busy (what should not happen) - * function returns failure. It is API user responsibility to make sure we are not entering - * device power transition while device is busy. - */ -static int ipc_pm_action(const struct device *dev, enum pm_device_action action) +static int intel_adsp_ipc_old_init(void) { - if (pm_device_is_busy(INTEL_ADSP_IPC_HOST_DEV)) { - return -EBUSY; - } - - const struct ipc_control_driver_api *api = - (const struct ipc_control_driver_api *)dev->api; + int ret; + const struct device *ipc_dev = INTEL_ADSP_IPC_HOST_DEV; - int ret = 0; - - switch (action) { - case PM_DEVICE_ACTION_SUSPEND: - if (api->suspend_fn) { - ret = api->suspend_fn(dev, api->suspend_fn_args); - if (!ret) { - irq_disable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); - } - } - break; - case PM_DEVICE_ACTION_RESUME: - irq_enable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); - if (!irq_is_enabled(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE))) { - ret = -EINTR; - break; - } - ace_ipc_intc_unmask(); - ret = intel_adsp_ipc_init(dev); - if (ret) { - break; - } - if (api->resume_fn) { - ret = api->resume_fn(dev, api->resume_fn_args); - } - break; - default: - /* Return as default value when given PM action is not supported */ - return -ENOTSUP; - } + ret = ipc_service_register_endpoint(ipc_dev, &intel_adsp_ipc_ept, &intel_adsp_ipc_ept_cfg); return ret; } -/** - * @brief Callback functions to be executed by Zephyr application - * during IPC device suspend and resume. - */ -static struct ipc_control_driver_api ipc_power_control_api = { - .resume_fn = NULL, - .resume_fn_args = NULL, - .suspend_fn = NULL, - .suspend_fn_args = NULL -}; - -PM_DEVICE_DT_DEFINE(INTEL_ADSP_IPC_HOST_DTNODE, ipc_pm_action); - -#endif /* CONFIG_PM_DEVICE */ - -static const struct intel_adsp_ipc_config ipc_host_config = { - .regs = (void *)INTEL_ADSP_IPC_REG_ADDRESS, -}; - -static struct intel_adsp_ipc_data ipc_host_data; - -DEVICE_DT_DEFINE(INTEL_ADSP_IPC_HOST_DTNODE, dt_init, PM_DEVICE_DT_GET(INTEL_ADSP_IPC_HOST_DTNODE), - &ipc_host_data, &ipc_host_config, PRE_KERNEL_2, 0, COND_CODE_1(CONFIG_PM_DEVICE, - (&ipc_power_control_api), (NULL))); - -#endif /* DT_NODE_EXISTS(INTEL_ADSP_IPC_HOST_DTNODE) */ +/* Backend is at PRE_KERNEL_2:0, so we need to init after that. */ +SYS_INIT(intel_adsp_ipc_old_init, PRE_KERNEL_2, 1); diff --git a/subsys/ipc/ipc_service/backends/CMakeLists.txt b/subsys/ipc/ipc_service/backends/CMakeLists.txt index d6ca8fb76c33..37c62b88e000 100644 --- a/subsys/ipc/ipc_service/backends/CMakeLists.txt +++ b/subsys/ipc/ipc_service/backends/CMakeLists.txt @@ -4,4 +4,5 @@ zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_ICMSG ipc_icmsg.c) zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_INITIATOR ipc_icmsg_me_initiator.c) zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_FOLLOWER ipc_icmsg_me_follower.c) zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_ICBMSG ipc_icbmsg.c) +zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC ipc_intel_adsp_host_ipc.c) zephyr_sources_ifdef(CONFIG_IPC_SERVICE_BACKEND_RPMSG ipc_rpmsg_static_vrings.c) diff --git a/subsys/ipc/ipc_service/backends/Kconfig b/subsys/ipc/ipc_service/backends/Kconfig index bb8b444898f9..5677c609d410 100644 --- a/subsys/ipc/ipc_service/backends/Kconfig +++ b/subsys/ipc/ipc_service/backends/Kconfig @@ -50,4 +50,5 @@ config IPC_SERVICE_BACKEND_ICMSG_ME_FOLLOWER rsource "Kconfig.icmsg_me" rsource "Kconfig.icbmsg" +rsource "Kconfig.intel_adsp" rsource "Kconfig.rpmsg" diff --git a/subsys/ipc/ipc_service/backends/Kconfig.intel_adsp b/subsys/ipc/ipc_service/backends/Kconfig.intel_adsp new file mode 100644 index 000000000000..47a884310d7a --- /dev/null +++ b/subsys/ipc/ipc_service/backends/Kconfig.intel_adsp @@ -0,0 +1,11 @@ +# +# Copyright (c) 2025 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config IPC_SERVICE_BACKEND_INTEL_ADSP_HOST_IPC + bool "Backend for the Intel Audio DSP Host IPC" + default DT_HAS_INTEL_ADSP_HOST_IPC_ENABLED + help + IPC Service Backend for Intel Audio DSP Host IPC. diff --git a/subsys/ipc/ipc_service/backends/ipc_intel_adsp_host_ipc.c b/subsys/ipc/ipc_service/backends/ipc_intel_adsp_host_ipc.c new file mode 100644 index 000000000000..274e13c9ea6d --- /dev/null +++ b/subsys/ipc/ipc_service/backends/ipc_intel_adsp_host_ipc.c @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2022, 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * + * @brief IPC service backend for Intel Audio DSP host IPC + * + * @note When declaring struct ipt_ept_cfg, the field priv must point to + * a struct intel_adsp_ipc_ept_priv_data. This is used for passing + * callback returns. + * + * @note For sending message and the received callback, the data and len + * arguments are not used to represent a byte array. Instead, + * the data argument points to the descriptor of data to be sent + * (of struct intel_adsp_ipc_msg). The len argument represents + * the type of message to be sent (enum intel_adsp_send_len) and + * the type of callback (enum intel_adsp_cb_len). + */ + +#define DT_DRV_COMPAT intel_adsp_host_ipc + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static inline void ace_ipc_intc_mask(void) +{ +#if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) + ACE_DINT[0].ie[ACE_INTL_HIPC] = ACE_DINT[0].ie[ACE_INTL_HIPC] & ~BIT(0); +#endif +} + +static inline void ace_ipc_intc_unmask(void) +{ +#if defined(CONFIG_SOC_SERIES_INTEL_ADSP_ACE) + ACE_DINT[0].ie[ACE_INTL_HIPC] = BIT(0); +#endif +} + +static void intel_adsp_ipc_isr(const void *devarg) +{ + const struct device *dev = devarg; + const struct intel_adsp_ipc_config *config = dev->config; + struct intel_adsp_ipc_data *devdata = dev->data; + const struct ipc_ept_cfg *ept_cfg = devdata->ept_cfg; + + struct intel_adsp_ipc_ept_priv_data *priv_data = + (struct intel_adsp_ipc_ept_priv_data *)ept_cfg->priv; + + volatile struct intel_adsp_ipc *regs = config->regs; + k_spinlock_key_t key = k_spin_lock(&devdata->lock); + + if (regs->tdr & INTEL_ADSP_IPC_BUSY) { + bool done = true; + + if (ept_cfg->cb.received != NULL) { + struct intel_adsp_ipc_msg cb_msg = { + .data = regs->tdr & ~INTEL_ADSP_IPC_BUSY, + .ext_data = regs->tdd, + }; + + ept_cfg->cb.received(&cb_msg, INTEL_ADSP_IPC_CB_MSG, ept_cfg->priv); + + done = (priv_data->cb_ret == INTEL_ADSP_IPC_CB_RET_OKAY); + } + + regs->tdr = INTEL_ADSP_IPC_BUSY; + if (done) { +#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE + regs->tda = INTEL_ADSP_IPC_ACE1X_TDA_DONE; +#else + regs->tda = INTEL_ADSP_IPC_DONE; +#endif + } + } + + /* Same signal, but on different bits in 1.5 */ + bool done = (regs->ida & INTEL_ADSP_IPC_DONE); + + if (done) { + bool external_completion = false; + + if (ept_cfg->cb.received != NULL) { + ept_cfg->cb.received(NULL, INTEL_ADSP_IPC_CB_DONE, ept_cfg->priv); + + if (priv_data->cb_ret == INTEL_ADSP_IPC_CB_RET_EXT_COMPLETE) { + external_completion = true; + } + } + + devdata->tx_ack_pending = false; + + /* + * Allow the system to enter the runtime idle state after the IPC acknowledgment + * is received. + */ + pm_policy_state_lock_put(PM_STATE_RUNTIME_IDLE, PM_ALL_SUBSTATES); + k_sem_give(&devdata->sem); + + /* IPC completion registers will be set externally. */ + if (external_completion) { + k_spin_unlock(&devdata->lock, key); + return; + } + + regs->ida = INTEL_ADSP_IPC_DONE; + } + + k_spin_unlock(&devdata->lock, key); +} + +int intel_adsp_ipc_init(const struct device *dev) +{ + pm_device_busy_set(dev); + struct intel_adsp_ipc_data *devdata = dev->data; + const struct intel_adsp_ipc_config *config = dev->config; + + k_sem_init(&devdata->sem, 0, 1); + + /* ACK any latched interrupts (including TDA to clear IDA on + * the other side!), then enable. + */ + config->regs->tdr = INTEL_ADSP_IPC_BUSY; + config->regs->ida = INTEL_ADSP_IPC_DONE; +#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE + config->regs->tda = INTEL_ADSP_IPC_ACE1X_TDA_DONE; +#else + config->regs->tda = INTEL_ADSP_IPC_DONE; +#endif + config->regs->ctl |= (INTEL_ADSP_IPC_CTL_IDIE | INTEL_ADSP_IPC_CTL_TBIE); + pm_device_busy_clear(dev); + + return 0; +} + +static int intel_adsp_ipc_register_ept(const struct device *instance, void **token, + const struct ipc_ept_cfg *cfg) +{ + struct intel_adsp_ipc_data *data = instance->data; + + data->ept_cfg = cfg; + + irq_enable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); + ace_ipc_intc_unmask(); + + return 0; +} + +static int intel_adsp_ipc_deregister_ept(const struct device *instance, void *token) +{ + struct intel_adsp_ipc_data *data = instance->data; + + data->ept_cfg = NULL; + + ace_ipc_intc_mask(); + irq_disable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); + + return 0; +} + +static void ipc_complete(const struct device *dev) +{ + const struct intel_adsp_ipc_config *config = dev->config; + +#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE + config->regs->tda = INTEL_ADSP_IPC_ACE1X_TDA_DONE; +#else + config->regs->tda = INTEL_ADSP_IPC_DONE; +#endif +} + +static bool ipc_is_complete(const struct device *dev) +{ + const struct intel_adsp_ipc_config *config = dev->config; + const struct intel_adsp_ipc_data *devdata = dev->data; + bool not_busy = (config->regs->idr & INTEL_ADSP_IPC_BUSY) == 0; + + return not_busy && !devdata->tx_ack_pending; +} + +static int ipc_send_message(const struct device *dev, uint32_t data, uint32_t ext_data) +{ +#ifdef CONFIG_PM_DEVICE + enum pm_device_state current_state; + + if (pm_device_state_get(INTEL_ADSP_IPC_HOST_DEV, ¤t_state) != 0 || + current_state != PM_DEVICE_STATE_ACTIVE) { + return -ESHUTDOWN; + } +#endif + + pm_device_busy_set(dev); + const struct intel_adsp_ipc_config *config = dev->config; + struct intel_adsp_ipc_data *devdata = dev->data; + k_spinlock_key_t key = k_spin_lock(&devdata->lock); + + if ((config->regs->idr & INTEL_ADSP_IPC_BUSY) != 0 || devdata->tx_ack_pending) { + k_spin_unlock(&devdata->lock, key); + return -EBUSY; + } + + k_sem_reset(&devdata->sem); + + /* Prevent entering runtime idle state until IPC acknowledgment is received. */ + pm_policy_state_lock_get(PM_STATE_RUNTIME_IDLE, PM_ALL_SUBSTATES); + + devdata->tx_ack_pending = true; + + config->regs->idd = ext_data; + config->regs->idr = data | INTEL_ADSP_IPC_BUSY; + + k_spin_unlock(&devdata->lock, key); + + pm_device_busy_clear(dev); + + return 0; +} + +static int ipc_send_message_sync(const struct device *dev, uint32_t data, uint32_t ext_data, + k_timeout_t timeout) +{ + struct intel_adsp_ipc_data *devdata = dev->data; + + int ret = ipc_send_message(dev, data, ext_data); + + if (ret == 0) { + k_sem_take(&devdata->sem, timeout); + } + + return ret; +} + +static int ipc_send_message_emergency(const struct device *dev, uint32_t data, uint32_t ext_data) +{ + const struct intel_adsp_ipc_config *const config = dev->config; + + volatile struct intel_adsp_ipc *const regs = config->regs; + bool done; + + /* check if host is processing message. */ + while (regs->idr & INTEL_ADSP_IPC_BUSY) { + k_busy_wait(1); + } + + /* check if host has pending acknowledge msg + * Same signal, but on different bits in 1.5 + */ + done = regs->ida & INTEL_ADSP_IPC_DONE; + if (done) { + /* IPC completion */ + regs->ida = INTEL_ADSP_IPC_DONE; + } + + regs->idd = ext_data; + regs->idr = data | INTEL_ADSP_IPC_BUSY; + + return 0; +} + +/** + * @brief Send an IPC message. + * + * This implements the inner working of ipc_service_send(). + * + * @note Arguments @a data and @a len are not used to point to a byte buffer of data + * to be sent. Instead, @a data must point to a descriptor of data to be sent, + * struct intel_adsp_ipc_msg. And @a len indicates what type of message to send + * as described in enum intel_adsp_send_len. + * + * Return values for various message types: + * - For INTEL_ADSP_IPC_SEND_MSG_*, returns 0 when message is sent. Negative errno if + * errors. + * - For INTEL_ADSP_IPC_SEND_DONE, always returns 0 for sending DONE message. + * - For INTEL_ADSP_IPC_SEND_IS_COMPLETE, returns 0 if host has processed the message. + * -EAGAIN if not. + * + * @param[in] dev Pointer to device struct. + * @param[in] token Backend-specific token. + * @param[in] data Descriptor of IPC message to be sent (as struct intel_adsp_ipc_msg). + * @param[in] len Type of message to be sent (described in enum intel_adsp_send_len). + * + * @return 0 if message is sent successfully or query returns okay. + * Negative errno otherwise. + */ +static int intel_adsp_ipc_send(const struct device *dev, void *token, const void *data, size_t len) +{ + int ret; + + const struct intel_adsp_ipc_msg *msg = (const struct intel_adsp_ipc_msg *)data; + + switch (len) { + case INTEL_ADSP_IPC_SEND_MSG: { + ret = ipc_send_message(dev, msg->data, msg->ext_data); + + break; + } + case INTEL_ADSP_IPC_SEND_MSG_SYNC: { + ret = ipc_send_message_sync(dev, msg->data, msg->ext_data, msg->timeout); + + break; + } + case INTEL_ADSP_IPC_SEND_MSG_EMERGENCY: { + ret = ipc_send_message_emergency(dev, msg->data, msg->ext_data); + + break; + } + case INTEL_ADSP_IPC_SEND_DONE: { + ipc_complete(dev); + + ret = 0; + + break; + } + case INTEL_ADSP_IPC_SEND_IS_COMPLETE: { + bool completed = ipc_is_complete(dev); + + if (completed) { + ret = 0; + } else { + ret = -EAGAIN; + } + + break; + } + default: + ret = -EBADMSG; + + break; + } + + return ret; +} + +static int intel_adsp_ipc_dt_init(const struct device *dev) +{ + struct intel_adsp_ipc_data *devdata = dev->data; + + memset(devdata, 0, sizeof(*devdata)); + + IRQ_CONNECT(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE), 0, intel_adsp_ipc_isr, + INTEL_ADSP_IPC_HOST_DEV, 0); + + return intel_adsp_ipc_init(dev); +} + +#ifdef CONFIG_PM_DEVICE + +void intel_adsp_ipc_set_resume_handler(const struct device *dev, intel_adsp_ipc_resume_handler_t fn, + void *arg) +{ + struct intel_adsp_ipc_data *devdata = dev->data; + k_spinlock_key_t key = k_spin_lock(&devdata->lock); + + devdata->resume_fn = fn; + devdata->resume_fn_args = arg; + + k_spin_unlock(&devdata->lock, key); +} + +void intel_adsp_ipc_set_suspend_handler(const struct device *dev, + intel_adsp_ipc_suspend_handler_t fn, void *arg) +{ + struct intel_adsp_ipc_data *devdata = dev->data; + k_spinlock_key_t key = k_spin_lock(&devdata->lock); + + devdata->suspend_fn = fn; + devdata->suspend_fn_args = arg; + + k_spin_unlock(&devdata->lock, key); +} + +/** + * @brief Manages IPC driver power state change. + * + * @param dev IPC device. + * @param action Power state to be changed to. + * + * @return int Returns 0 on success or optionaly error code from the + * registered ipc_power_control_api callbacks. + * + * @note PM lock is taken at the start of each power transition to prevent concurrent calls + * to @ref pm_device_action_run function. + * If IPC Device performs hardware operation and device is busy (what should not happen) + * function returns failure. It is API user responsibility to make sure we are not entering + * device power transition while device is busy. + */ +static int ipc_pm_action(const struct device *dev, enum pm_device_action action) +{ + if (pm_device_is_busy(INTEL_ADSP_IPC_HOST_DEV)) { + return -EBUSY; + } + + struct intel_adsp_ipc_data *devdata = dev->data; + + int ret = 0; + + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + if (devdata->suspend_fn) { + ret = devdata->suspend_fn(dev, devdata->suspend_fn_args); + if (!ret) { + irq_disable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); + } + } + break; + case PM_DEVICE_ACTION_RESUME: + irq_enable(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE)); + if (!irq_is_enabled(DT_IRQN(INTEL_ADSP_IPC_HOST_DTNODE))) { + ret = -EINTR; + break; + } + ace_ipc_intc_unmask(); + ret = intel_adsp_ipc_init(dev); + if (ret) { + break; + } + if (devdata->resume_fn) { + ret = devdata->resume_fn(dev, devdata->resume_fn_args); + } + break; + default: + /* Return as default value when given PM action is not supported */ + return -ENOTSUP; + } + + return ret; +} + +PM_DEVICE_DT_DEFINE(INTEL_ADSP_IPC_HOST_DTNODE, ipc_pm_action); + +#endif /* CONFIG_PM_DEVICE */ + +static const struct intel_adsp_ipc_config ipc_host_config = { + .regs = (void *)INTEL_ADSP_IPC_REG_ADDRESS, +}; + +static struct intel_adsp_ipc_data ipc_host_data; + +const static struct ipc_service_backend intel_adsp_ipc_backend_api = { + .send = intel_adsp_ipc_send, + .register_endpoint = intel_adsp_ipc_register_ept, + .deregister_endpoint = intel_adsp_ipc_deregister_ept, +}; + +DEVICE_DT_DEFINE(INTEL_ADSP_IPC_HOST_DTNODE, intel_adsp_ipc_dt_init, + PM_DEVICE_DT_GET(INTEL_ADSP_IPC_HOST_DTNODE), &ipc_host_data, &ipc_host_config, + PRE_KERNEL_2, 0, &intel_adsp_ipc_backend_api); From f06269317a840ec8f09764256823741ffdae1294 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 18 Jul 2025 13:31:21 -0400 Subject: [PATCH 32/36] [nrf fromtree] manifest: move tf-m-tests to main manifest Those tests are needed for verifying and testing tf-m. While not needed directly by zephyr, they are needed for testing and CI. Signed-off-by: Anas Nashif (cherry picked from commit a2ccf506bdae2445594b9661a7b2c1dfc83932b8) --- submanifests/optional.yaml | 6 ------ west.yml | 7 ++++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/submanifests/optional.yaml b/submanifests/optional.yaml index 322a37871bc7..1f49ad1485f1 100644 --- a/submanifests/optional.yaml +++ b/submanifests/optional.yaml @@ -28,12 +28,6 @@ manifest: remote: upstream groups: - optional - - name: tf-m-tests - revision: a90702bcb8fadb6f70daf0ffbb13888dfe63fc99 - path: modules/tee/tf-m/tf-m-tests - remote: upstream - groups: - - optional - name: tflite-micro revision: 8d404de73acf7687831e16d88e86e4f73cfddf8e path: optional/modules/lib/tflite-micro diff --git a/west.yml b/west.yml index 893d43b5be66..4df85b88494f 100644 --- a/west.yml +++ b/west.yml @@ -24,7 +24,7 @@ manifest: - name: babblesim url-base: https://github.com/BabbleSim - group-filter: [-babblesim, -optional] + group-filter: [-babblesim, -optional, -testing] # # Please add items below based on alphabetical order @@ -358,6 +358,11 @@ manifest: path: modules/debug/segger groups: - debug + - name: tf-m-tests + revision: a90702bcb8fadb6f70daf0ffbb13888dfe63fc99 + path: modules/tee/tf-m/tf-m-tests + groups: + - testing - name: trusted-firmware-a revision: 713ffbf96c5bcbdeab757423f10f73eb304eff07 path: modules/tee/tf-a/trusted-firmware-a From fa984cf569beee774310fac2b0114903ab2b2653 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Fri, 18 Jul 2025 13:57:11 -0400 Subject: [PATCH 33/36] [nrf fromtree] manifest: move psa-arch-tests to main manifest Tests needed to verify tf-a module. Signed-off-by: Anas Nashif (cherry picked from commit a7c53391d5224f4a28c36198ddc86d8b43cba5e8) --- submanifests/optional.yaml | 6 ------ west.yml | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/submanifests/optional.yaml b/submanifests/optional.yaml index 1f49ad1485f1..a97aded1f717 100644 --- a/submanifests/optional.yaml +++ b/submanifests/optional.yaml @@ -22,12 +22,6 @@ manifest: remote: upstream groups: - optional - - name: psa-arch-tests - revision: 87b08682a111ebb085cd8b1ea41d603191d6d146 - path: modules/tee/tf-m/psa-arch-tests - remote: upstream - groups: - - optional - name: tflite-micro revision: 8d404de73acf7687831e16d88e86e4f73cfddf8e path: optional/modules/lib/tflite-micro diff --git a/west.yml b/west.yml index 4df85b88494f..4d6f077f98d2 100644 --- a/west.yml +++ b/west.yml @@ -353,6 +353,12 @@ manifest: - name: picolibc path: modules/lib/picolibc revision: ca8b6ebba5226a75545e57a140443168a26ba664 + - name: psa-arch-tests + revision: 87b08682a111ebb085cd8b1ea41d603191d6d146 + path: modules/tee/tf-m/psa-arch-tests + groups: + - testing + - tee - name: segger revision: cf56b1d9c80f81a26e2ac5727c9cf177116a4692 path: modules/debug/segger From 400ea93b7199df9ffcd7950eda0156ad1e271564 Mon Sep 17 00:00:00 2001 From: Tomi Fontanilles Date: Wed, 12 Nov 2025 12:55:08 +0200 Subject: [PATCH 34/36] [nrf fromtree] manifest: tf-m: update to 2.2.2 Update the TF-M repos to 2.2.2, from version 2.2.0. Signed-off-by: Tomi Fontanilles (cherry picked from commit ea3697bf886367b0cc6e402274c2054dbb1a8e89) --- west.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/west.yml b/west.yml index 4d6f077f98d2..12ca14f6682b 100644 --- a/west.yml +++ b/west.yml @@ -354,7 +354,7 @@ manifest: path: modules/lib/picolibc revision: ca8b6ebba5226a75545e57a140443168a26ba664 - name: psa-arch-tests - revision: 87b08682a111ebb085cd8b1ea41d603191d6d146 + revision: afeed6ed87146d9828e0ff862f3533790df8c421 path: modules/tee/tf-m/psa-arch-tests groups: - testing @@ -365,7 +365,7 @@ manifest: groups: - debug - name: tf-m-tests - revision: a90702bcb8fadb6f70daf0ffbb13888dfe63fc99 + revision: cde5b6ed540d3ff5a09564fded6b39b0a70ad3bf path: modules/tee/tf-m/tf-m-tests groups: - testing @@ -375,7 +375,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: c2f9edc77f72838e7d6f5f9c0b95e4318ddfced1 + revision: 9889df41022c6679b71da55d93f9e3693a804976 path: modules/tee/tf-m/trusted-firmware-m groups: - tee From f054af9b618bbf35fa15d3f0facabb5dd431cc23 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 1 Dec 2025 18:08:59 +0100 Subject: [PATCH 35/36] [nrf fromtree] manifest: psa-arch-tests: support for STM32U5 and STM32WBA65 Update PSA-Arch-tests to support testing STM32U585 and STM32WBA based boards. Signed-off-by: Etienne Carriere (cherry picked from commit 88cc16191b4950f8a9fa7d019a6a0be98085614e) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 12ca14f6682b..5766f226f0ba 100644 --- a/west.yml +++ b/west.yml @@ -354,7 +354,7 @@ manifest: path: modules/lib/picolibc revision: ca8b6ebba5226a75545e57a140443168a26ba664 - name: psa-arch-tests - revision: afeed6ed87146d9828e0ff862f3533790df8c421 + revision: 941cd8436a2e0f1da9d8584b83a403930826899d path: modules/tee/tf-m/psa-arch-tests groups: - testing From 7ec8916af732c94114b05388ab0491598eb2c911 Mon Sep 17 00:00:00 2001 From: BUDKE Gerson Fernando Date: Wed, 3 Dec 2025 10:10:50 +0100 Subject: [PATCH 36/36] [nrf fromtree] west.yml: Bump TF-M with stm32u5a sram5 support Enable SRAM5 region in the Global TrustZone controller (GTZC). Signed-off-by: BUDKE Gerson Fernando (cherry picked from commit 5d6e4d9469db18abb1cbe85ae745ba6e192d4b06) --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 5766f226f0ba..417337854e95 100644 --- a/west.yml +++ b/west.yml @@ -375,7 +375,7 @@ manifest: groups: - tee - name: trusted-firmware-m - revision: 9889df41022c6679b71da55d93f9e3693a804976 + revision: e295109067f71e1c8db76d02396baa050687b1df path: modules/tee/tf-m/trusted-firmware-m groups: - tee